1
The following changes since commit 5375af3cd7b8adcc10c18d8083b7be63976c9645:
1
The following changes since commit 56f9e46b841c7be478ca038d8d4085d776ab4b0d:
2
2
3
Merge remote-tracking branch 'remotes/mst/tags/for_upstream' into staging (2020-05-04 15:51:09 +0100)
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2017-02-20' into staging (2017-02-20 17:42:47 +0000)
4
4
5
are available in the Git repository at:
5
are available in the git repository at:
6
6
7
https://github.com/XanClic/qemu.git tags/pull-block-2020-05-05
7
git://github.com/stefanha/qemu.git tags/block-pull-request
8
8
9
for you to fetch changes up to 4ce5dd3e9b5ee0fac18625860eb3727399ee965e:
9
for you to fetch changes up to a7b91d35bab97a2d3e779d0c64c9b837b52a6cf7:
10
10
11
block/block-copy: use aio-task-pool API (2020-05-05 14:03:28 +0200)
11
coroutine-lock: make CoRwlock thread-safe and fair (2017-02-21 11:39:40 +0000)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
14
Pull request
15
- Asynchronous copying for block-copy (i.e., the backup job)
15
16
- Allow resizing of qcow2 images when they have internal snapshots
16
v2:
17
- iotests: Logging improvements for Python tests
17
* Rebased to resolve scsi conflicts
18
- iotest 153 fix, and block comment cleanups
19
18
20
----------------------------------------------------------------
19
----------------------------------------------------------------
21
Eric Blake (4):
22
block: Add blk_new_with_bs() helper
23
qcow2: Allow resize of images with internal snapshots
24
qcow2: Tweak comment about bitmaps vs. resize
25
block: Comment cleanups
26
20
27
John Snow (14):
21
Paolo Bonzini (24):
28
iotests: do a light delinting
22
block: move AioContext, QEMUTimer, main-loop to libqemuutil
29
iotests: don't use 'format' for drive_add
23
aio: introduce aio_co_schedule and aio_co_wake
30
iotests: ignore import warnings from pylint
24
block-backend: allow blk_prw from coroutine context
31
iotests: replace mutable list default args
25
test-thread-pool: use generic AioContext infrastructure
32
iotests: add pylintrc file
26
io: add methods to set I/O handlers on AioContext
33
iotests: alphabetize standard imports
27
io: make qio_channel_yield aware of AioContexts
34
iotests: drop pre-Python 3.4 compatibility code
28
nbd: convert to use qio_channel_yield
35
iotests: touch up log function signature
29
coroutine-lock: reschedule coroutine on the AioContext it was running
36
iotests: limit line length to 79 chars
30
on
37
iotests: add hmp helper with logging
31
blkdebug: reschedule coroutine on the AioContext it is running on
38
iotests: add script_initialize
32
qed: introduce qed_aio_start_io and qed_aio_next_io_cb
39
iotest 258: use script_main
33
aio: push aio_context_acquire/release down to dispatching
40
iotests: Mark verify functions as private
34
block: explicitly acquire aiocontext in timers that need it
41
iotests: use python logging for iotests.log()
35
block: explicitly acquire aiocontext in callbacks that need it
36
block: explicitly acquire aiocontext in bottom halves that need it
37
block: explicitly acquire aiocontext in aio callbacks that need it
38
aio-posix: partially inline aio_dispatch into aio_poll
39
async: remove unnecessary inc/dec pairs
40
block: document fields protected by AioContext lock
41
coroutine-lock: make CoMutex thread-safe
42
coroutine-lock: add limited spinning to CoMutex
43
test-aio-multithread: add performance comparison with thread-based
44
mutexes
45
coroutine-lock: place CoMutex before CoQueue in header
46
coroutine-lock: add mutex argument to CoQueue APIs
47
coroutine-lock: make CoRwlock thread-safe and fair
42
48
43
Maxim Levitsky (1):
49
Makefile.objs | 4 -
44
Fix iotest 153
50
stubs/Makefile.objs | 1 +
45
51
tests/Makefile.include | 19 +-
46
Vladimir Sementsov-Ogievskiy (5):
52
util/Makefile.objs | 6 +-
47
block/block-copy: rename in-flight requests to tasks
53
block/nbd-client.h | 2 +-
48
block/block-copy: alloc task on each iteration
54
block/qed.h | 3 +
49
block/block-copy: add state pointer to BlockCopyTask
55
include/block/aio.h | 38 ++-
50
block/block-copy: refactor task creation
56
include/block/block_int.h | 64 +++--
51
block/block-copy: use aio-task-pool API
57
include/io/channel.h | 72 +++++-
52
58
include/qemu/coroutine.h | 84 ++++---
53
include/sysemu/block-backend.h | 2 +
59
include/qemu/coroutine_int.h | 11 +-
54
block/block-backend.c | 23 +++
60
include/sysemu/block-backend.h | 14 +-
55
block/block-copy.c | 279 +++++++++++++++++--------
61
tests/iothread.h | 25 ++
56
block/crypto.c | 9 +-
62
block/backup.c | 2 +-
57
block/io.c | 3 +-
63
block/blkdebug.c | 9 +-
58
block/parallels.c | 8 +-
64
block/blkreplay.c | 2 +-
59
block/qcow.c | 8 +-
65
block/block-backend.c | 13 +-
60
block/qcow2-refcount.c | 2 +-
66
block/curl.c | 44 +++-
61
block/qcow2-snapshot.c | 20 +-
67
block/gluster.c | 9 +-
62
block/qcow2.c | 45 ++--
68
block/io.c | 42 +---
63
block/qed.c | 8 +-
69
block/iscsi.c | 15 +-
64
block/sheepdog.c | 10 +-
70
block/linux-aio.c | 10 +-
65
block/vdi.c | 8 +-
71
block/mirror.c | 12 +-
66
block/vhdx.c | 8 +-
72
block/nbd-client.c | 119 +++++----
67
block/vmdk.c | 9 +-
73
block/nfs.c | 9 +-
68
block/vpc.c | 8 +-
74
block/qcow2-cluster.c | 4 +-
69
block/vvfat.c | 10 +-
75
block/qed-cluster.c | 2 +
70
blockdev.c | 8 +-
76
block/qed-table.c | 12 +-
71
blockjob.c | 7 +-
77
block/qed.c | 58 +++--
72
tests/qemu-iotests/001 | 2 +-
78
block/sheepdog.c | 31 +--
73
tests/qemu-iotests/030 | 4 +-
79
block/ssh.c | 29 +--
74
tests/qemu-iotests/052 | 2 +-
80
block/throttle-groups.c | 4 +-
75
tests/qemu-iotests/055 | 3 +-
81
block/win32-aio.c | 9 +-
76
tests/qemu-iotests/061 | 35 ++++
82
dma-helpers.c | 2 +
77
tests/qemu-iotests/061.out | 28 +++
83
hw/9pfs/9p.c | 2 +-
78
tests/qemu-iotests/134 | 2 +-
84
hw/block/virtio-blk.c | 19 +-
79
tests/qemu-iotests/149 | 3 +-
85
hw/scsi/scsi-bus.c | 2 +
80
tests/qemu-iotests/153 | 2 +-
86
hw/scsi/scsi-disk.c | 15 ++
81
tests/qemu-iotests/153.out | 12 +-
87
hw/scsi/scsi-generic.c | 20 +-
82
tests/qemu-iotests/155 | 2 +-
88
hw/scsi/virtio-scsi.c | 7 +
83
tests/qemu-iotests/188 | 2 +-
89
io/channel-command.c | 13 +
84
tests/qemu-iotests/194 | 4 +-
90
io/channel-file.c | 11 +
85
tests/qemu-iotests/202 | 4 +-
91
io/channel-socket.c | 16 +-
86
tests/qemu-iotests/203 | 4 +-
92
io/channel-tls.c | 12 +
87
tests/qemu-iotests/206 | 2 +-
93
io/channel-watch.c | 6 +
88
tests/qemu-iotests/207 | 6 +-
94
io/channel.c | 97 ++++++--
89
tests/qemu-iotests/208 | 2 +-
95
nbd/client.c | 2 +-
90
tests/qemu-iotests/209 | 2 +-
96
nbd/common.c | 9 +-
91
tests/qemu-iotests/210 | 6 +-
97
nbd/server.c | 94 +++-----
92
tests/qemu-iotests/211 | 6 +-
98
stubs/linux-aio.c | 32 +++
93
tests/qemu-iotests/212 | 6 +-
99
stubs/set-fd-handler.c | 11 -
94
tests/qemu-iotests/213 | 6 +-
100
tests/iothread.c | 91 +++++++
95
tests/qemu-iotests/216 | 4 +-
101
tests/test-aio-multithread.c | 463 ++++++++++++++++++++++++++++++++++++
96
tests/qemu-iotests/218 | 2 +-
102
tests/test-thread-pool.c | 12 +-
97
tests/qemu-iotests/219 | 2 +-
103
aio-posix.c => util/aio-posix.c | 62 ++---
98
tests/qemu-iotests/222 | 7 +-
104
aio-win32.c => util/aio-win32.c | 30 +--
99
tests/qemu-iotests/224 | 4 +-
105
util/aiocb.c | 55 +++++
100
tests/qemu-iotests/228 | 6 +-
106
async.c => util/async.c | 84 ++++++-
101
tests/qemu-iotests/234 | 4 +-
107
iohandler.c => util/iohandler.c | 0
102
tests/qemu-iotests/235 | 4 +-
108
main-loop.c => util/main-loop.c | 0
103
tests/qemu-iotests/236 | 2 +-
109
util/qemu-coroutine-lock.c | 254 ++++++++++++++++++--
104
tests/qemu-iotests/237 | 2 +-
110
util/qemu-coroutine-sleep.c | 2 +-
105
tests/qemu-iotests/238 | 2 +
111
util/qemu-coroutine.c | 8 +
106
tests/qemu-iotests/242 | 2 +-
112
qemu-timer.c => util/qemu-timer.c | 0
107
tests/qemu-iotests/245 | 1 +
113
thread-pool.c => util/thread-pool.c | 8 +-
108
tests/qemu-iotests/245.out | 10 +-
114
trace-events | 11 -
109
tests/qemu-iotests/246 | 2 +-
115
util/trace-events | 17 +-
110
tests/qemu-iotests/248 | 2 +-
116
67 files changed, 1712 insertions(+), 533 deletions(-)
111
tests/qemu-iotests/254 | 2 +-
117
create mode 100644 tests/iothread.h
112
tests/qemu-iotests/255 | 2 +-
118
create mode 100644 stubs/linux-aio.c
113
tests/qemu-iotests/256 | 2 +-
119
create mode 100644 tests/iothread.c
114
tests/qemu-iotests/258 | 10 +-
120
create mode 100644 tests/test-aio-multithread.c
115
tests/qemu-iotests/260 | 4 +-
121
rename aio-posix.c => util/aio-posix.c (94%)
116
tests/qemu-iotests/262 | 4 +-
122
rename aio-win32.c => util/aio-win32.c (95%)
117
tests/qemu-iotests/264 | 4 +-
123
create mode 100644 util/aiocb.c
118
tests/qemu-iotests/274 | 4 +-
124
rename async.c => util/async.c (82%)
119
tests/qemu-iotests/277 | 2 +
125
rename iohandler.c => util/iohandler.c (100%)
120
tests/qemu-iotests/280 | 8 +-
126
rename main-loop.c => util/main-loop.c (100%)
121
tests/qemu-iotests/283 | 4 +-
127
rename qemu-timer.c => util/qemu-timer.c (100%)
122
tests/qemu-iotests/iotests.py | 366 ++++++++++++++++++++-------------
128
rename thread-pool.c => util/thread-pool.c (97%)
123
tests/qemu-iotests/pylintrc | 26 +++
124
71 files changed, 728 insertions(+), 386 deletions(-)
125
create mode 100644 tests/qemu-iotests/pylintrc
126
129
127
--
130
--
128
2.26.2
131
2.9.3
129
132
130
133
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We can turn logging on/off globally instead of per-function.
3
AioContext is fairly self contained, the only dependency is QEMUTimer but
4
4
that in turn doesn't need anything else. So move them out of block-obj-y
5
Remove use_log from run_job, and use python logging to turn on
5
to avoid introducing a dependency from io/ to block-obj-y.
6
diffable output when we run through a script entry point.
6
7
7
main-loop and its dependency iohandler also need to be moved, because
8
iotest 245 changes output order due to buffering reasons.
8
later in this series io/ will call iohandler_get_aio_context.
9
9
10
An extended note on python logging:
10
[Changed copyright "the QEMU team" to "other QEMU contributors" as
11
11
suggested by Daniel Berrange and agreed by Paolo.
12
A NullHandler is added to `qemu.iotests` to stop output from being
12
--Stefan]
13
generated if this code is used as a library without configuring logging.
13
14
A NullHandler is only needed at the root, so a duplicate handler is not
14
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
15
needed for `qemu.iotests.diff_io`.
15
Reviewed-by: Fam Zheng <famz@redhat.com>
16
16
Message-id: 20170213135235.12274-2-pbonzini@redhat.com
17
When logging is not configured, messages at the 'WARNING' levels or
17
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
above are printed with default settings. The NullHandler stops this from
19
occurring, which is considered good hygiene for code used as a library.
20
21
See https://docs.python.org/3/howto/logging.html#library-config
22
23
When logging is actually enabled (always at the behest of an explicit
24
call by a client script), a root logger is implicitly created at the
25
root, which allows messages to propagate upwards and be handled/emitted
26
from the root logger with default settings.
27
28
When we want iotest logging, we attach a handler to the
29
qemu.iotests.diff_io logger and disable propagation to avoid possible
30
double-printing.
31
32
For more information on python logging infrastructure, I highly
33
recommend downloading the pip package `logging_tree`, which provides
34
convenient visualizations of the hierarchical logging configuration
35
under different circumstances.
36
37
See https://pypi.org/project/logging_tree/ for more information.
38
39
Signed-off-by: John Snow <jsnow@redhat.com>
40
Reviewed-by: Max Reitz <mreitz@redhat.com>
41
Message-Id: <20200331000014.11581-15-jsnow@redhat.com>
42
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
43
Signed-off-by: Max Reitz <mreitz@redhat.com>
44
---
18
---
45
tests/qemu-iotests/030 | 4 +--
19
Makefile.objs | 4 ---
46
tests/qemu-iotests/155 | 2 +-
20
stubs/Makefile.objs | 1 +
47
tests/qemu-iotests/245 | 1 +
21
tests/Makefile.include | 11 ++++----
48
tests/qemu-iotests/245.out | 10 +++----
22
util/Makefile.objs | 6 +++-
49
tests/qemu-iotests/iotests.py | 53 ++++++++++++++++++++---------------
23
block/io.c | 29 -------------------
50
5 files changed, 39 insertions(+), 31 deletions(-)
24
stubs/linux-aio.c | 32 +++++++++++++++++++++
51
25
stubs/set-fd-handler.c | 11 --------
52
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
26
aio-posix.c => util/aio-posix.c | 2 +-
53
index XXXXXXX..XXXXXXX 100755
27
aio-win32.c => util/aio-win32.c | 0
54
--- a/tests/qemu-iotests/030
28
util/aiocb.c | 55 +++++++++++++++++++++++++++++++++++++
55
+++ b/tests/qemu-iotests/030
29
async.c => util/async.c | 3 +-
56
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
30
iohandler.c => util/iohandler.c | 0
57
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
31
main-loop.c => util/main-loop.c | 0
58
self.assert_qmp(result, 'return', {})
32
qemu-timer.c => util/qemu-timer.c | 0
59
33
thread-pool.c => util/thread-pool.c | 2 +-
60
- self.vm.run_job(job='drive0', auto_dismiss=True, use_log=False)
34
trace-events | 11 --------
61
- self.vm.run_job(job='node4', auto_dismiss=True, use_log=False)
35
util/trace-events | 11 ++++++++
62
+ self.vm.run_job(job='drive0', auto_dismiss=True)
36
17 files changed, 114 insertions(+), 64 deletions(-)
63
+ self.vm.run_job(job='node4', auto_dismiss=True)
37
create mode 100644 stubs/linux-aio.c
64
self.assert_no_active_block_jobs()
38
rename aio-posix.c => util/aio-posix.c (99%)
65
39
rename aio-win32.c => util/aio-win32.c (100%)
66
# Test a block-stream and a block-commit job in parallel
40
create mode 100644 util/aiocb.c
67
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
41
rename async.c => util/async.c (99%)
68
index XXXXXXX..XXXXXXX 100755
42
rename iohandler.c => util/iohandler.c (100%)
69
--- a/tests/qemu-iotests/155
43
rename main-loop.c => util/main-loop.c (100%)
70
+++ b/tests/qemu-iotests/155
44
rename qemu-timer.c => util/qemu-timer.c (100%)
71
@@ -XXX,XX +XXX,XX @@ class MirrorBaseClass(BaseClass):
45
rename thread-pool.c => util/thread-pool.c (99%)
72
46
73
self.assert_qmp(result, 'return', {})
47
diff --git a/Makefile.objs b/Makefile.objs
74
48
index XXXXXXX..XXXXXXX 100644
75
- self.vm.run_job('mirror-job', use_log=False, auto_finalize=False,
49
--- a/Makefile.objs
76
+ self.vm.run_job('mirror-job', auto_finalize=False,
50
+++ b/Makefile.objs
77
pre_finalize=self.openBacking, auto_dismiss=True)
51
@@ -XXX,XX +XXX,XX @@ chardev-obj-y = chardev/
78
52
#######################################################################
79
def testFull(self):
53
# block-obj-y is code used by both qemu system emulation and qemu-img
80
diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245
54
81
index XXXXXXX..XXXXXXX 100755
55
-block-obj-y = async.o thread-pool.o
82
--- a/tests/qemu-iotests/245
56
block-obj-y += nbd/
83
+++ b/tests/qemu-iotests/245
57
block-obj-y += block.o blockjob.o
84
@@ -XXX,XX +XXX,XX @@ class TestBlockdevReopen(iotests.QMPTestCase):
58
-block-obj-y += main-loop.o iohandler.o qemu-timer.o
85
self.run_test_iothreads(None, 'iothread0')
59
-block-obj-$(CONFIG_POSIX) += aio-posix.o
86
60
-block-obj-$(CONFIG_WIN32) += aio-win32.o
87
if __name__ == '__main__':
61
block-obj-y += block/
88
+ iotests.activate_logging()
62
block-obj-y += qemu-io-cmds.o
89
iotests.main(supported_fmts=["qcow2"],
63
block-obj-$(CONFIG_REPLICATION) += replication.o
90
supported_protocols=["file"])
64
diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs
91
diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out
65
index XXXXXXX..XXXXXXX 100644
92
index XXXXXXX..XXXXXXX 100644
66
--- a/stubs/Makefile.objs
93
--- a/tests/qemu-iotests/245.out
67
+++ b/stubs/Makefile.objs
94
+++ b/tests/qemu-iotests/245.out
68
@@ -XXX,XX +XXX,XX @@ stub-obj-y += get-vm-name.o
95
@@ -XXX,XX +XXX,XX @@
69
stub-obj-y += iothread.o
96
-.....................
70
stub-obj-y += iothread-lock.o
97
-----------------------------------------------------------------------
71
stub-obj-y += is-daemonized.o
98
-Ran 21 tests
72
+stub-obj-$(CONFIG_LINUX_AIO) += linux-aio.o
99
-
73
stub-obj-y += machine-init-done.o
100
-OK
74
stub-obj-y += migr-blocker.o
101
{"execute": "job-finalize", "arguments": {"id": "commit0"}}
75
stub-obj-y += monitor.o
102
{"return": {}}
76
diff --git a/tests/Makefile.include b/tests/Makefile.include
103
{"data": {"id": "commit0", "type": "commit"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
77
index XXXXXXX..XXXXXXX 100644
104
@@ -XXX,XX +XXX,XX @@ OK
78
--- a/tests/Makefile.include
105
{"return": {}}
79
+++ b/tests/Makefile.include
106
{"data": {"id": "stream0", "type": "stream"}, "event": "BLOCK_JOB_PENDING", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
80
@@ -XXX,XX +XXX,XX @@ check-unit-y += tests/test-visitor-serialization$(EXESUF)
107
{"data": {"device": "stream0", "len": 3145728, "offset": 3145728, "speed": 0, "type": "stream"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
81
check-unit-y += tests/test-iov$(EXESUF)
108
+.....................
82
gcov-files-test-iov-y = util/iov.c
109
+----------------------------------------------------------------------
83
check-unit-y += tests/test-aio$(EXESUF)
110
+Ran 21 tests
84
+gcov-files-test-aio-y = util/async.c util/qemu-timer.o
111
+
85
+gcov-files-test-aio-$(CONFIG_WIN32) += util/aio-win32.c
112
+OK
86
+gcov-files-test-aio-$(CONFIG_POSIX) += util/aio-posix.c
113
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
87
check-unit-y += tests/test-throttle$(EXESUF)
114
index XXXXXXX..XXXXXXX 100644
88
gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
115
--- a/tests/qemu-iotests/iotests.py
89
gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
116
+++ b/tests/qemu-iotests/iotests.py
90
@@ -XXX,XX +XXX,XX @@ tests/check-qjson$(EXESUF): tests/check-qjson.o $(test-util-obj-y)
117
@@ -XXX,XX +XXX,XX @@ assert sys.version_info >= (3, 6)
91
tests/check-qom-interface$(EXESUF): tests/check-qom-interface.o $(test-qom-obj-y)
118
QMPResponse = Dict[str, Any]
92
tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
119
93
120
94
-tests/test-char$(EXESUF): tests/test-char.o qemu-timer.o \
121
+# Use this logger for logging messages directly from the iotests module
95
-    $(test-util-obj-y) $(qtest-obj-y) $(test-block-obj-y) $(chardev-obj-y)
122
+logger = logging.getLogger('qemu.iotests')
96
+tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y)
123
+logger.addHandler(logging.NullHandler())
97
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
124
+
98
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
125
+# Use this logger for messages that ought to be used for diff output.
99
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
126
+test_logger = logging.getLogger('qemu.iotests.diff_io')
100
@@ -XXX,XX +XXX,XX @@ tests/test-vmstate$(EXESUF): tests/test-vmstate.o \
127
+
101
    migration/vmstate.o migration/qemu-file.o \
128
+
102
migration/qemu-file-channel.o migration/qjson.o \
129
faulthandler.enable()
103
    $(test-io-obj-y)
130
104
-tests/test-timed-average$(EXESUF): tests/test-timed-average.o qemu-timer.o \
131
# This will not work if arguments contain spaces but is necessary if we
105
-    $(test-util-obj-y)
132
@@ -XXX,XX +XXX,XX @@ def log(msg: Msg,
106
+tests/test-timed-average$(EXESUF): tests/test-timed-average.o $(test-util-obj-y)
133
if isinstance(msg, (dict, list)):
107
tests/test-base64$(EXESUF): tests/test-base64.o \
134
# Don't sort if it's already sorted
108
    libqemuutil.a libqemustub.a
135
do_sort = not isinstance(msg, OrderedDict)
109
tests/ptimer-test$(EXESUF): tests/ptimer-test.o tests/ptimer-test-stubs.o hw/core/ptimer.o libqemustub.a
136
- print(json.dumps(msg, sort_keys=do_sort, indent=indent))
110
@@ -XXX,XX +XXX,XX @@ tests/usb-hcd-ehci-test$(EXESUF): tests/usb-hcd-ehci-test.o $(libqos-usb-obj-y)
137
+ test_logger.info(json.dumps(msg, sort_keys=do_sort, indent=indent))
111
tests/usb-hcd-xhci-test$(EXESUF): tests/usb-hcd-xhci-test.o $(libqos-usb-obj-y)
138
else:
112
tests/pc-cpu-test$(EXESUF): tests/pc-cpu-test.o
139
- print(msg)
113
tests/postcopy-test$(EXESUF): tests/postcopy-test.o
140
+ test_logger.info(msg)
114
-tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o qemu-timer.o \
141
115
+tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \
142
class Timeout:
116
    $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \
143
def __init__(self, seconds, errmsg="Timeout"):
117
    $(chardev-obj-y)
144
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
118
tests/qemu-iotests/socket_scm_helper$(EXESUF): tests/qemu-iotests/socket_scm_helper.o
145
119
diff --git a/util/Makefile.objs b/util/Makefile.objs
146
# Returns None on success, and an error string on failure
120
index XXXXXXX..XXXXXXX 100644
147
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
121
--- a/util/Makefile.objs
148
- pre_finalize=None, cancel=False, use_log=True, wait=60.0):
122
+++ b/util/Makefile.objs
149
+ pre_finalize=None, cancel=False, wait=60.0):
123
@@ -XXX,XX +XXX,XX @@
150
"""
124
util-obj-y = osdep.o cutils.o unicode.o qemu-timer-common.o
151
run_job moves a job from creation through to dismissal.
125
util-obj-y += bufferiszero.o
152
126
util-obj-y += lockcnt.o
153
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
127
+util-obj-y += aiocb.o async.o thread-pool.o qemu-timer.o
154
invoked prior to issuing job-finalize, if any.
128
+util-obj-y += main-loop.o iohandler.o
155
:param cancel: Bool. When true, cancels the job after the pre_finalize
129
+util-obj-$(CONFIG_POSIX) += aio-posix.o
156
callback.
130
util-obj-$(CONFIG_POSIX) += compatfd.o
157
- :param use_log: Bool. When false, does not log QMP messages.
131
util-obj-$(CONFIG_POSIX) += event_notifier-posix.o
158
:param wait: Float. Timeout value specifying how long to wait for any
132
util-obj-$(CONFIG_POSIX) += mmap-alloc.o
159
event, in seconds. Defaults to 60.0.
133
util-obj-$(CONFIG_POSIX) += oslib-posix.o
160
"""
134
util-obj-$(CONFIG_POSIX) += qemu-openpty.o
161
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
135
util-obj-$(CONFIG_POSIX) += qemu-thread-posix.o
162
while True:
136
-util-obj-$(CONFIG_WIN32) += event_notifier-win32.o
163
ev = filter_qmp_event(self.events_wait(events, timeout=wait))
137
util-obj-$(CONFIG_POSIX) += memfd.o
164
if ev['event'] != 'JOB_STATUS_CHANGE':
138
+util-obj-$(CONFIG_WIN32) += aio-win32.o
165
- if use_log:
139
+util-obj-$(CONFIG_WIN32) += event_notifier-win32.o
166
- log(ev)
140
util-obj-$(CONFIG_WIN32) += oslib-win32.o
167
+ log(ev)
141
util-obj-$(CONFIG_WIN32) += qemu-thread-win32.o
168
continue
142
util-obj-y += envlist.o path.o module.o
169
status = ev['data']['status']
143
diff --git a/block/io.c b/block/io.c
170
if status == 'aborting':
144
index XXXXXXX..XXXXXXX 100644
171
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
145
--- a/block/io.c
172
for j in result['return']:
146
+++ b/block/io.c
173
if j['id'] == job:
147
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *bdrv_aio_flush(BlockDriverState *bs,
174
error = j['error']
148
return &acb->common;
175
- if use_log:
149
}
176
- log('Job failed: %s' % (j['error']))
150
177
+ log('Job failed: %s' % (j['error']))
151
-void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
178
elif status == 'ready':
152
- BlockCompletionFunc *cb, void *opaque)
179
- if use_log:
153
-{
180
- self.qmp_log('job-complete', id=job)
154
- BlockAIOCB *acb;
181
- else:
155
-
182
- self.qmp('job-complete', id=job)
156
- acb = g_malloc(aiocb_info->aiocb_size);
183
+ self.qmp_log('job-complete', id=job)
157
- acb->aiocb_info = aiocb_info;
184
elif status == 'pending' and not auto_finalize:
158
- acb->bs = bs;
185
if pre_finalize:
159
- acb->cb = cb;
186
pre_finalize()
160
- acb->opaque = opaque;
187
- if cancel and use_log:
161
- acb->refcnt = 1;
188
+ if cancel:
162
- return acb;
189
self.qmp_log('job-cancel', id=job)
163
-}
190
- elif cancel:
164
-
191
- self.qmp('job-cancel', id=job)
165
-void qemu_aio_ref(void *p)
192
- elif use_log:
166
-{
193
- self.qmp_log('job-finalize', id=job)
167
- BlockAIOCB *acb = p;
194
else:
168
- acb->refcnt++;
195
- self.qmp('job-finalize', id=job)
169
-}
196
+ self.qmp_log('job-finalize', id=job)
170
-
197
elif status == 'concluded' and not auto_dismiss:
171
-void qemu_aio_unref(void *p)
198
- if use_log:
172
-{
199
- self.qmp_log('job-dismiss', id=job)
173
- BlockAIOCB *acb = p;
200
- else:
174
- assert(acb->refcnt > 0);
201
- self.qmp('job-dismiss', id=job)
175
- if (--acb->refcnt == 0) {
202
+ self.qmp_log('job-dismiss', id=job)
176
- g_free(acb);
203
elif status == 'null':
177
- }
204
return error
178
-}
205
179
-
206
@@ -XXX,XX +XXX,XX @@ def notrun(reason):
180
/**************************************************************/
207
seq = os.path.basename(sys.argv[0])
181
/* Coroutine block device emulation */
208
182
209
open('%s/%s.notrun' % (output_dir, seq), 'w').write(reason + '\n')
183
diff --git a/stubs/linux-aio.c b/stubs/linux-aio.c
210
- print('%s not run: %s' % (seq, reason))
184
new file mode 100644
211
+ logger.warning("%s not run: %s", seq, reason)
185
index XXXXXXX..XXXXXXX
212
sys.exit(0)
186
--- /dev/null
213
187
+++ b/stubs/linux-aio.c
214
def case_notrun(reason):
188
@@ -XXX,XX +XXX,XX @@
215
@@ -XXX,XX +XXX,XX @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
189
+/*
216
if debug:
190
+ * Linux native AIO support.
217
sys.argv.remove('-d')
191
+ *
218
logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
192
+ * Copyright (C) 2009 IBM, Corp.
219
+ logger.debug("iotests debugging messages active")
193
+ * Copyright (C) 2009 Red Hat, Inc.
220
194
+ *
221
return debug
195
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
222
196
+ * See the COPYING file in the top-level directory.
223
@@ -XXX,XX +XXX,XX @@ def execute_test(*args, test_function=None, **kwargs):
197
+ */
224
else:
198
+#include "qemu/osdep.h"
225
test_function()
199
+#include "block/aio.h"
226
200
+#include "block/raw-aio.h"
227
+def activate_logging():
201
+
228
+ """Activate iotests.log() output to stdout for script-style tests."""
202
+void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)
229
+ handler = logging.StreamHandler(stream=sys.stdout)
203
+{
230
+ formatter = logging.Formatter('%(message)s')
204
+ abort();
231
+ handler.setFormatter(formatter)
205
+}
232
+ test_logger.addHandler(handler)
206
+
233
+ test_logger.setLevel(logging.INFO)
207
+void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
234
+ test_logger.propagate = False
208
+{
235
+
209
+ abort();
236
# This is called from script-style iotests without a single point of entry
210
+}
237
def script_initialize(*args, **kwargs):
211
+
238
"""Initialize script-style tests without running any tests."""
212
+LinuxAioState *laio_init(void)
239
+ activate_logging()
213
+{
240
execute_setup_common(*args, **kwargs)
214
+ abort();
241
215
+}
242
# This is called from script-style iotests with a single point of entry
216
+
243
def script_main(test_function, *args, **kwargs):
217
+void laio_cleanup(LinuxAioState *s)
244
"""Run script-style tests outside of the unittest framework"""
218
+{
245
+ activate_logging()
219
+ abort();
246
execute_test(*args, test_function=test_function, **kwargs)
220
+}
247
221
diff --git a/stubs/set-fd-handler.c b/stubs/set-fd-handler.c
248
# This is called from unittest style iotests
222
index XXXXXXX..XXXXXXX 100644
223
--- a/stubs/set-fd-handler.c
224
+++ b/stubs/set-fd-handler.c
225
@@ -XXX,XX +XXX,XX @@ void qemu_set_fd_handler(int fd,
226
{
227
abort();
228
}
229
-
230
-void aio_set_fd_handler(AioContext *ctx,
231
- int fd,
232
- bool is_external,
233
- IOHandler *io_read,
234
- IOHandler *io_write,
235
- AioPollFn *io_poll,
236
- void *opaque)
237
-{
238
- abort();
239
-}
240
diff --git a/aio-posix.c b/util/aio-posix.c
241
similarity index 99%
242
rename from aio-posix.c
243
rename to util/aio-posix.c
244
index XXXXXXX..XXXXXXX 100644
245
--- a/aio-posix.c
246
+++ b/util/aio-posix.c
247
@@ -XXX,XX +XXX,XX @@
248
#include "qemu/rcu_queue.h"
249
#include "qemu/sockets.h"
250
#include "qemu/cutils.h"
251
-#include "trace-root.h"
252
+#include "trace.h"
253
#ifdef CONFIG_EPOLL_CREATE1
254
#include <sys/epoll.h>
255
#endif
256
diff --git a/aio-win32.c b/util/aio-win32.c
257
similarity index 100%
258
rename from aio-win32.c
259
rename to util/aio-win32.c
260
diff --git a/util/aiocb.c b/util/aiocb.c
261
new file mode 100644
262
index XXXXXXX..XXXXXXX
263
--- /dev/null
264
+++ b/util/aiocb.c
265
@@ -XXX,XX +XXX,XX @@
266
+/*
267
+ * BlockAIOCB allocation
268
+ *
269
+ * Copyright (c) 2003-2017 Fabrice Bellard and other QEMU contributors
270
+ *
271
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
272
+ * of this software and associated documentation files (the "Software"), to deal
273
+ * in the Software without restriction, including without limitation the rights
274
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
275
+ * copies of the Software, and to permit persons to whom the Software is
276
+ * furnished to do so, subject to the following conditions:
277
+ *
278
+ * The above copyright notice and this permission notice shall be included in
279
+ * all copies or substantial portions of the Software.
280
+ *
281
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
282
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
283
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
284
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
285
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
286
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
287
+ * THE SOFTWARE.
288
+ */
289
+
290
+#include "qemu/osdep.h"
291
+#include "block/aio.h"
292
+
293
+void *qemu_aio_get(const AIOCBInfo *aiocb_info, BlockDriverState *bs,
294
+ BlockCompletionFunc *cb, void *opaque)
295
+{
296
+ BlockAIOCB *acb;
297
+
298
+ acb = g_malloc(aiocb_info->aiocb_size);
299
+ acb->aiocb_info = aiocb_info;
300
+ acb->bs = bs;
301
+ acb->cb = cb;
302
+ acb->opaque = opaque;
303
+ acb->refcnt = 1;
304
+ return acb;
305
+}
306
+
307
+void qemu_aio_ref(void *p)
308
+{
309
+ BlockAIOCB *acb = p;
310
+ acb->refcnt++;
311
+}
312
+
313
+void qemu_aio_unref(void *p)
314
+{
315
+ BlockAIOCB *acb = p;
316
+ assert(acb->refcnt > 0);
317
+ if (--acb->refcnt == 0) {
318
+ g_free(acb);
319
+ }
320
+}
321
diff --git a/async.c b/util/async.c
322
similarity index 99%
323
rename from async.c
324
rename to util/async.c
325
index XXXXXXX..XXXXXXX 100644
326
--- a/async.c
327
+++ b/util/async.c
328
@@ -XXX,XX +XXX,XX @@
329
/*
330
- * QEMU System Emulator
331
+ * Data plane event loop
332
*
333
* Copyright (c) 2003-2008 Fabrice Bellard
334
+ * Copyright (c) 2009-2017 QEMU contributors
335
*
336
* Permission is hereby granted, free of charge, to any person obtaining a copy
337
* of this software and associated documentation files (the "Software"), to deal
338
diff --git a/iohandler.c b/util/iohandler.c
339
similarity index 100%
340
rename from iohandler.c
341
rename to util/iohandler.c
342
diff --git a/main-loop.c b/util/main-loop.c
343
similarity index 100%
344
rename from main-loop.c
345
rename to util/main-loop.c
346
diff --git a/qemu-timer.c b/util/qemu-timer.c
347
similarity index 100%
348
rename from qemu-timer.c
349
rename to util/qemu-timer.c
350
diff --git a/thread-pool.c b/util/thread-pool.c
351
similarity index 99%
352
rename from thread-pool.c
353
rename to util/thread-pool.c
354
index XXXXXXX..XXXXXXX 100644
355
--- a/thread-pool.c
356
+++ b/util/thread-pool.c
357
@@ -XXX,XX +XXX,XX @@
358
#include "qemu/queue.h"
359
#include "qemu/thread.h"
360
#include "qemu/coroutine.h"
361
-#include "trace-root.h"
362
+#include "trace.h"
363
#include "block/thread-pool.h"
364
#include "qemu/main-loop.h"
365
366
diff --git a/trace-events b/trace-events
367
index XXXXXXX..XXXXXXX 100644
368
--- a/trace-events
369
+++ b/trace-events
370
@@ -XXX,XX +XXX,XX @@
371
#
372
# The <format-string> should be a sprintf()-compatible format string.
373
374
-# aio-posix.c
375
-run_poll_handlers_begin(void *ctx, int64_t max_ns) "ctx %p max_ns %"PRId64
376
-run_poll_handlers_end(void *ctx, bool progress) "ctx %p progress %d"
377
-poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
378
-poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
379
-
380
-# thread-pool.c
381
-thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p"
382
-thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d"
383
-thread_pool_cancel(void *req, void *opaque) "req %p opaque %p"
384
-
385
# ioport.c
386
cpu_in(unsigned int addr, char size, unsigned int val) "addr %#x(%c) value %u"
387
cpu_out(unsigned int addr, char size, unsigned int val) "addr %#x(%c) value %u"
388
diff --git a/util/trace-events b/util/trace-events
389
index XXXXXXX..XXXXXXX 100644
390
--- a/util/trace-events
391
+++ b/util/trace-events
392
@@ -XXX,XX +XXX,XX @@
393
# See docs/tracing.txt for syntax documentation.
394
395
+# util/aio-posix.c
396
+run_poll_handlers_begin(void *ctx, int64_t max_ns) "ctx %p max_ns %"PRId64
397
+run_poll_handlers_end(void *ctx, bool progress) "ctx %p progress %d"
398
+poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
399
+poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
400
+
401
+# util/thread-pool.c
402
+thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p"
403
+thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d"
404
+thread_pool_cancel(void *req, void *opaque) "req %p opaque %p"
405
+
406
# util/buffer.c
407
buffer_resize(const char *buf, size_t olen, size_t len) "%s: old %zd, new %zd"
408
buffer_move_empty(const char *buf, size_t len, const char *from) "%s: %zd bytes from %s"
249
--
409
--
250
2.26.2
410
2.9.3
251
411
252
412
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
This allows others to get repeatable results with pylint. If you run
3
aio_co_wake provides the infrastructure to start a coroutine on a "home"
4
`pylint iotests.py`, you should see a 100% pass.
4
AioContext. It will be used by CoMutex and CoQueue, so that coroutines
5
5
don't jump from one context to another when they go to sleep on a
6
Signed-off-by: John Snow <jsnow@redhat.com>
6
mutex or waitqueue. However, it can also be used as a more efficient
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
alternative to one-shot bottom halves, and saves the effort of tracking
8
Message-Id: <20200331000014.11581-6-jsnow@redhat.com>
8
which AioContext a coroutine is running on.
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
aio_co_schedule is the part of aio_co_wake that starts a coroutine
11
on a remove AioContext, but it is also useful to implement e.g.
12
bdrv_set_aio_context callbacks.
13
14
The implementation of aio_co_schedule is based on a lock-free
15
multiple-producer, single-consumer queue. The multiple producers use
16
cmpxchg to add to a LIFO stack. The consumer (a per-AioContext bottom
17
half) grabs all items added so far, inverts the list to make it FIFO,
18
and goes through it one item at a time until it's empty. The data
19
structure was inspired by OSv, which uses it in the very code we'll
20
"port" to QEMU for the thread-safe CoMutex.
21
22
Most of the new code is really tests.
23
24
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
25
Reviewed-by: Fam Zheng <famz@redhat.com>
26
Message-id: 20170213135235.12274-3-pbonzini@redhat.com
27
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
28
---
12
tests/qemu-iotests/pylintrc | 22 ++++++++++++++++++++++
29
tests/Makefile.include | 8 +-
13
1 file changed, 22 insertions(+)
30
include/block/aio.h | 32 +++++++
14
create mode 100644 tests/qemu-iotests/pylintrc
31
include/qemu/coroutine_int.h | 11 ++-
15
32
tests/iothread.h | 25 +++++
16
diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc
33
tests/iothread.c | 91 ++++++++++++++++++
34
tests/test-aio-multithread.c | 213 +++++++++++++++++++++++++++++++++++++++++++
35
util/async.c | 65 +++++++++++++
36
util/qemu-coroutine.c | 8 ++
37
util/trace-events | 4 +
38
9 files changed, 453 insertions(+), 4 deletions(-)
39
create mode 100644 tests/iothread.h
40
create mode 100644 tests/iothread.c
41
create mode 100644 tests/test-aio-multithread.c
42
43
diff --git a/tests/Makefile.include b/tests/Makefile.include
44
index XXXXXXX..XXXXXXX 100644
45
--- a/tests/Makefile.include
46
+++ b/tests/Makefile.include
47
@@ -XXX,XX +XXX,XX @@ check-unit-y += tests/test-aio$(EXESUF)
48
gcov-files-test-aio-y = util/async.c util/qemu-timer.o
49
gcov-files-test-aio-$(CONFIG_WIN32) += util/aio-win32.c
50
gcov-files-test-aio-$(CONFIG_POSIX) += util/aio-posix.c
51
+check-unit-y += tests/test-aio-multithread$(EXESUF)
52
+gcov-files-test-aio-multithread-y = $(gcov-files-test-aio-y)
53
+gcov-files-test-aio-multithread-y += util/qemu-coroutine.c tests/iothread.c
54
check-unit-y += tests/test-throttle$(EXESUF)
55
-gcov-files-test-aio-$(CONFIG_WIN32) = aio-win32.c
56
-gcov-files-test-aio-$(CONFIG_POSIX) = aio-posix.c
57
check-unit-y += tests/test-thread-pool$(EXESUF)
58
gcov-files-test-thread-pool-y = thread-pool.c
59
gcov-files-test-hbitmap-y = util/hbitmap.c
60
@@ -XXX,XX +XXX,XX @@ test-qapi-obj-y = tests/test-qapi-visit.o tests/test-qapi-types.o \
61
    $(test-qom-obj-y)
62
test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y)
63
test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y)
64
-test-block-obj-y = $(block-obj-y) $(test-io-obj-y)
65
+test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o
66
67
tests/check-qint$(EXESUF): tests/check-qint.o $(test-util-obj-y)
68
tests/check-qstring$(EXESUF): tests/check-qstring.o $(test-util-obj-y)
69
@@ -XXX,XX +XXX,XX @@ tests/check-qom-proplist$(EXESUF): tests/check-qom-proplist.o $(test-qom-obj-y)
70
tests/test-char$(EXESUF): tests/test-char.o $(test-util-obj-y) $(qtest-obj-y) $(test-io-obj-y) $(chardev-obj-y)
71
tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
72
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
73
+tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
74
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
75
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
76
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
77
diff --git a/include/block/aio.h b/include/block/aio.h
78
index XXXXXXX..XXXXXXX 100644
79
--- a/include/block/aio.h
80
+++ b/include/block/aio.h
81
@@ -XXX,XX +XXX,XX @@ typedef void QEMUBHFunc(void *opaque);
82
typedef bool AioPollFn(void *opaque);
83
typedef void IOHandler(void *opaque);
84
85
+struct Coroutine;
86
struct ThreadPool;
87
struct LinuxAioState;
88
89
@@ -XXX,XX +XXX,XX @@ struct AioContext {
90
bool notified;
91
EventNotifier notifier;
92
93
+ QSLIST_HEAD(, Coroutine) scheduled_coroutines;
94
+ QEMUBH *co_schedule_bh;
95
+
96
/* Thread pool for performing work and receiving completion callbacks.
97
* Has its own locking.
98
*/
99
@@ -XXX,XX +XXX,XX @@ static inline bool aio_node_check(AioContext *ctx, bool is_external)
100
}
101
102
/**
103
+ * aio_co_schedule:
104
+ * @ctx: the aio context
105
+ * @co: the coroutine
106
+ *
107
+ * Start a coroutine on a remote AioContext.
108
+ *
109
+ * The coroutine must not be entered by anyone else while aio_co_schedule()
110
+ * is active. In addition the coroutine must have yielded unless ctx
111
+ * is the context in which the coroutine is running (i.e. the value of
112
+ * qemu_get_current_aio_context() from the coroutine itself).
113
+ */
114
+void aio_co_schedule(AioContext *ctx, struct Coroutine *co);
115
+
116
+/**
117
+ * aio_co_wake:
118
+ * @co: the coroutine
119
+ *
120
+ * Restart a coroutine on the AioContext where it was running last, thus
121
+ * preventing coroutines from jumping from one context to another when they
122
+ * go to sleep.
123
+ *
124
+ * aio_co_wake may be executed either in coroutine or non-coroutine
125
+ * context. The coroutine must not be entered by anyone else while
126
+ * aio_co_wake() is active.
127
+ */
128
+void aio_co_wake(struct Coroutine *co);
129
+
130
+/**
131
* Return the AioContext whose event loop runs in the current thread.
132
*
133
* If called from an IOThread this will be the IOThread's AioContext. If
134
diff --git a/include/qemu/coroutine_int.h b/include/qemu/coroutine_int.h
135
index XXXXXXX..XXXXXXX 100644
136
--- a/include/qemu/coroutine_int.h
137
+++ b/include/qemu/coroutine_int.h
138
@@ -XXX,XX +XXX,XX @@ struct Coroutine {
139
CoroutineEntry *entry;
140
void *entry_arg;
141
Coroutine *caller;
142
+
143
+ /* Only used when the coroutine has terminated. */
144
QSLIST_ENTRY(Coroutine) pool_next;
145
+
146
size_t locks_held;
147
148
- /* Coroutines that should be woken up when we yield or terminate */
149
+ /* Coroutines that should be woken up when we yield or terminate.
150
+ * Only used when the coroutine is running.
151
+ */
152
QSIMPLEQ_HEAD(, Coroutine) co_queue_wakeup;
153
+
154
+ /* Only used when the coroutine has yielded. */
155
+ AioContext *ctx;
156
QSIMPLEQ_ENTRY(Coroutine) co_queue_next;
157
+ QSLIST_ENTRY(Coroutine) co_scheduled_next;
158
};
159
160
Coroutine *qemu_coroutine_new(void);
161
diff --git a/tests/iothread.h b/tests/iothread.h
17
new file mode 100644
162
new file mode 100644
18
index XXXXXXX..XXXXXXX
163
index XXXXXXX..XXXXXXX
19
--- /dev/null
164
--- /dev/null
20
+++ b/tests/qemu-iotests/pylintrc
165
+++ b/tests/iothread.h
21
@@ -XXX,XX +XXX,XX @@
166
@@ -XXX,XX +XXX,XX @@
22
+[MESSAGES CONTROL]
167
+/*
23
+
168
+ * Event loop thread implementation for unit tests
24
+# Disable the message, report, category or checker with the given id(s). You
169
+ *
25
+# can either give multiple identifiers separated by comma (,) or put this
170
+ * Copyright Red Hat Inc., 2013, 2016
26
+# option multiple times (only on the command line, not in the configuration
171
+ *
27
+# file where it should appear only once). You can also use "--disable=all" to
172
+ * Authors:
28
+# disable everything first and then reenable specific checks. For example, if
173
+ * Stefan Hajnoczi <stefanha@redhat.com>
29
+# you want to run only the similarities checker, you can use "--disable=all
174
+ * Paolo Bonzini <pbonzini@redhat.com>
30
+# --enable=similarities". If you want to run only the classes checker, but have
175
+ *
31
+# no Warning level messages displayed, use "--disable=all --enable=classes
176
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
32
+# --disable=W".
177
+ * See the COPYING file in the top-level directory.
33
+disable=invalid-name,
178
+ */
34
+ no-else-return,
179
+#ifndef TEST_IOTHREAD_H
35
+ too-few-public-methods,
180
+#define TEST_IOTHREAD_H
36
+ too-many-arguments,
181
+
37
+ too-many-branches,
182
+#include "block/aio.h"
38
+ too-many-lines,
183
+#include "qemu/thread.h"
39
+ too-many-locals,
184
+
40
+ too-many-public-methods,
185
+typedef struct IOThread IOThread;
41
+ # These are temporary, and should be removed:
186
+
42
+ line-too-long,
187
+IOThread *iothread_new(void);
43
+ missing-docstring,
188
+void iothread_join(IOThread *iothread);
189
+AioContext *iothread_get_aio_context(IOThread *iothread);
190
+
191
+#endif
192
diff --git a/tests/iothread.c b/tests/iothread.c
193
new file mode 100644
194
index XXXXXXX..XXXXXXX
195
--- /dev/null
196
+++ b/tests/iothread.c
197
@@ -XXX,XX +XXX,XX @@
198
+/*
199
+ * Event loop thread implementation for unit tests
200
+ *
201
+ * Copyright Red Hat Inc., 2013, 2016
202
+ *
203
+ * Authors:
204
+ * Stefan Hajnoczi <stefanha@redhat.com>
205
+ * Paolo Bonzini <pbonzini@redhat.com>
206
+ *
207
+ * This work is licensed under the terms of the GNU GPL, version 2 or later.
208
+ * See the COPYING file in the top-level directory.
209
+ *
210
+ */
211
+
212
+#include "qemu/osdep.h"
213
+#include "qapi/error.h"
214
+#include "block/aio.h"
215
+#include "qemu/main-loop.h"
216
+#include "qemu/rcu.h"
217
+#include "iothread.h"
218
+
219
+struct IOThread {
220
+ AioContext *ctx;
221
+
222
+ QemuThread thread;
223
+ QemuMutex init_done_lock;
224
+ QemuCond init_done_cond; /* is thread initialization done? */
225
+ bool stopping;
226
+};
227
+
228
+static __thread IOThread *my_iothread;
229
+
230
+AioContext *qemu_get_current_aio_context(void)
231
+{
232
+ return my_iothread ? my_iothread->ctx : qemu_get_aio_context();
233
+}
234
+
235
+static void *iothread_run(void *opaque)
236
+{
237
+ IOThread *iothread = opaque;
238
+
239
+ rcu_register_thread();
240
+
241
+ my_iothread = iothread;
242
+ qemu_mutex_lock(&iothread->init_done_lock);
243
+ iothread->ctx = aio_context_new(&error_abort);
244
+ qemu_cond_signal(&iothread->init_done_cond);
245
+ qemu_mutex_unlock(&iothread->init_done_lock);
246
+
247
+ while (!atomic_read(&iothread->stopping)) {
248
+ aio_poll(iothread->ctx, true);
249
+ }
250
+
251
+ rcu_unregister_thread();
252
+ return NULL;
253
+}
254
+
255
+void iothread_join(IOThread *iothread)
256
+{
257
+ iothread->stopping = true;
258
+ aio_notify(iothread->ctx);
259
+ qemu_thread_join(&iothread->thread);
260
+ qemu_cond_destroy(&iothread->init_done_cond);
261
+ qemu_mutex_destroy(&iothread->init_done_lock);
262
+ aio_context_unref(iothread->ctx);
263
+ g_free(iothread);
264
+}
265
+
266
+IOThread *iothread_new(void)
267
+{
268
+ IOThread *iothread = g_new0(IOThread, 1);
269
+
270
+ qemu_mutex_init(&iothread->init_done_lock);
271
+ qemu_cond_init(&iothread->init_done_cond);
272
+ qemu_thread_create(&iothread->thread, NULL, iothread_run,
273
+ iothread, QEMU_THREAD_JOINABLE);
274
+
275
+ /* Wait for initialization to complete */
276
+ qemu_mutex_lock(&iothread->init_done_lock);
277
+ while (iothread->ctx == NULL) {
278
+ qemu_cond_wait(&iothread->init_done_cond,
279
+ &iothread->init_done_lock);
280
+ }
281
+ qemu_mutex_unlock(&iothread->init_done_lock);
282
+ return iothread;
283
+}
284
+
285
+AioContext *iothread_get_aio_context(IOThread *iothread)
286
+{
287
+ return iothread->ctx;
288
+}
289
diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c
290
new file mode 100644
291
index XXXXXXX..XXXXXXX
292
--- /dev/null
293
+++ b/tests/test-aio-multithread.c
294
@@ -XXX,XX +XXX,XX @@
295
+/*
296
+ * AioContext multithreading tests
297
+ *
298
+ * Copyright Red Hat, Inc. 2016
299
+ *
300
+ * Authors:
301
+ * Paolo Bonzini <pbonzini@redhat.com>
302
+ *
303
+ * This work is licensed under the terms of the GNU LGPL, version 2 or later.
304
+ * See the COPYING.LIB file in the top-level directory.
305
+ */
306
+
307
+#include "qemu/osdep.h"
308
+#include <glib.h>
309
+#include "block/aio.h"
310
+#include "qapi/error.h"
311
+#include "qemu/coroutine.h"
312
+#include "qemu/thread.h"
313
+#include "qemu/error-report.h"
314
+#include "iothread.h"
315
+
316
+/* AioContext management */
317
+
318
+#define NUM_CONTEXTS 5
319
+
320
+static IOThread *threads[NUM_CONTEXTS];
321
+static AioContext *ctx[NUM_CONTEXTS];
322
+static __thread int id = -1;
323
+
324
+static QemuEvent done_event;
325
+
326
+/* Run a function synchronously on a remote iothread. */
327
+
328
+typedef struct CtxRunData {
329
+ QEMUBHFunc *cb;
330
+ void *arg;
331
+} CtxRunData;
332
+
333
+static void ctx_run_bh_cb(void *opaque)
334
+{
335
+ CtxRunData *data = opaque;
336
+
337
+ data->cb(data->arg);
338
+ qemu_event_set(&done_event);
339
+}
340
+
341
+static void ctx_run(int i, QEMUBHFunc *cb, void *opaque)
342
+{
343
+ CtxRunData data = {
344
+ .cb = cb,
345
+ .arg = opaque
346
+ };
347
+
348
+ qemu_event_reset(&done_event);
349
+ aio_bh_schedule_oneshot(ctx[i], ctx_run_bh_cb, &data);
350
+ qemu_event_wait(&done_event);
351
+}
352
+
353
+/* Starting the iothreads. */
354
+
355
+static void set_id_cb(void *opaque)
356
+{
357
+ int *i = opaque;
358
+
359
+ id = *i;
360
+}
361
+
362
+static void create_aio_contexts(void)
363
+{
364
+ int i;
365
+
366
+ for (i = 0; i < NUM_CONTEXTS; i++) {
367
+ threads[i] = iothread_new();
368
+ ctx[i] = iothread_get_aio_context(threads[i]);
369
+ }
370
+
371
+ qemu_event_init(&done_event, false);
372
+ for (i = 0; i < NUM_CONTEXTS; i++) {
373
+ ctx_run(i, set_id_cb, &i);
374
+ }
375
+}
376
+
377
+/* Stopping the iothreads. */
378
+
379
+static void join_aio_contexts(void)
380
+{
381
+ int i;
382
+
383
+ for (i = 0; i < NUM_CONTEXTS; i++) {
384
+ aio_context_ref(ctx[i]);
385
+ }
386
+ for (i = 0; i < NUM_CONTEXTS; i++) {
387
+ iothread_join(threads[i]);
388
+ }
389
+ for (i = 0; i < NUM_CONTEXTS; i++) {
390
+ aio_context_unref(ctx[i]);
391
+ }
392
+ qemu_event_destroy(&done_event);
393
+}
394
+
395
+/* Basic test for the stuff above. */
396
+
397
+static void test_lifecycle(void)
398
+{
399
+ create_aio_contexts();
400
+ join_aio_contexts();
401
+}
402
+
403
+/* aio_co_schedule test. */
404
+
405
+static Coroutine *to_schedule[NUM_CONTEXTS];
406
+
407
+static bool now_stopping;
408
+
409
+static int count_retry;
410
+static int count_here;
411
+static int count_other;
412
+
413
+static bool schedule_next(int n)
414
+{
415
+ Coroutine *co;
416
+
417
+ co = atomic_xchg(&to_schedule[n], NULL);
418
+ if (!co) {
419
+ atomic_inc(&count_retry);
420
+ return false;
421
+ }
422
+
423
+ if (n == id) {
424
+ atomic_inc(&count_here);
425
+ } else {
426
+ atomic_inc(&count_other);
427
+ }
428
+
429
+ aio_co_schedule(ctx[n], co);
430
+ return true;
431
+}
432
+
433
+static void finish_cb(void *opaque)
434
+{
435
+ schedule_next(id);
436
+}
437
+
438
+static coroutine_fn void test_multi_co_schedule_entry(void *opaque)
439
+{
440
+ g_assert(to_schedule[id] == NULL);
441
+ atomic_mb_set(&to_schedule[id], qemu_coroutine_self());
442
+
443
+ while (!atomic_mb_read(&now_stopping)) {
444
+ int n;
445
+
446
+ n = g_test_rand_int_range(0, NUM_CONTEXTS);
447
+ schedule_next(n);
448
+ qemu_coroutine_yield();
449
+
450
+ g_assert(to_schedule[id] == NULL);
451
+ atomic_mb_set(&to_schedule[id], qemu_coroutine_self());
452
+ }
453
+}
454
+
455
+
456
+static void test_multi_co_schedule(int seconds)
457
+{
458
+ int i;
459
+
460
+ count_here = count_other = count_retry = 0;
461
+ now_stopping = false;
462
+
463
+ create_aio_contexts();
464
+ for (i = 0; i < NUM_CONTEXTS; i++) {
465
+ Coroutine *co1 = qemu_coroutine_create(test_multi_co_schedule_entry, NULL);
466
+ aio_co_schedule(ctx[i], co1);
467
+ }
468
+
469
+ g_usleep(seconds * 1000000);
470
+
471
+ atomic_mb_set(&now_stopping, true);
472
+ for (i = 0; i < NUM_CONTEXTS; i++) {
473
+ ctx_run(i, finish_cb, NULL);
474
+ to_schedule[i] = NULL;
475
+ }
476
+
477
+ join_aio_contexts();
478
+ g_test_message("scheduled %d, queued %d, retry %d, total %d\n",
479
+ count_other, count_here, count_retry,
480
+ count_here + count_other + count_retry);
481
+}
482
+
483
+static void test_multi_co_schedule_1(void)
484
+{
485
+ test_multi_co_schedule(1);
486
+}
487
+
488
+static void test_multi_co_schedule_10(void)
489
+{
490
+ test_multi_co_schedule(10);
491
+}
492
+
493
+/* End of tests. */
494
+
495
+int main(int argc, char **argv)
496
+{
497
+ init_clocks();
498
+
499
+ g_test_init(&argc, &argv, NULL);
500
+ g_test_add_func("/aio/multi/lifecycle", test_lifecycle);
501
+ if (g_test_quick()) {
502
+ g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_1);
503
+ } else {
504
+ g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_10);
505
+ }
506
+ return g_test_run();
507
+}
508
diff --git a/util/async.c b/util/async.c
509
index XXXXXXX..XXXXXXX 100644
510
--- a/util/async.c
511
+++ b/util/async.c
512
@@ -XXX,XX +XXX,XX @@
513
#include "qemu/main-loop.h"
514
#include "qemu/atomic.h"
515
#include "block/raw-aio.h"
516
+#include "qemu/coroutine_int.h"
517
+#include "trace.h"
518
519
/***********************************************************/
520
/* bottom halves (can be seen as timers which expire ASAP) */
521
@@ -XXX,XX +XXX,XX @@ aio_ctx_finalize(GSource *source)
522
}
523
#endif
524
525
+ assert(QSLIST_EMPTY(&ctx->scheduled_coroutines));
526
+ qemu_bh_delete(ctx->co_schedule_bh);
527
+
528
qemu_lockcnt_lock(&ctx->list_lock);
529
assert(!qemu_lockcnt_count(&ctx->list_lock));
530
while (ctx->first_bh) {
531
@@ -XXX,XX +XXX,XX @@ static bool event_notifier_poll(void *opaque)
532
return atomic_read(&ctx->notified);
533
}
534
535
+static void co_schedule_bh_cb(void *opaque)
536
+{
537
+ AioContext *ctx = opaque;
538
+ QSLIST_HEAD(, Coroutine) straight, reversed;
539
+
540
+ QSLIST_MOVE_ATOMIC(&reversed, &ctx->scheduled_coroutines);
541
+ QSLIST_INIT(&straight);
542
+
543
+ while (!QSLIST_EMPTY(&reversed)) {
544
+ Coroutine *co = QSLIST_FIRST(&reversed);
545
+ QSLIST_REMOVE_HEAD(&reversed, co_scheduled_next);
546
+ QSLIST_INSERT_HEAD(&straight, co, co_scheduled_next);
547
+ }
548
+
549
+ while (!QSLIST_EMPTY(&straight)) {
550
+ Coroutine *co = QSLIST_FIRST(&straight);
551
+ QSLIST_REMOVE_HEAD(&straight, co_scheduled_next);
552
+ trace_aio_co_schedule_bh_cb(ctx, co);
553
+ qemu_coroutine_enter(co);
554
+ }
555
+}
556
+
557
AioContext *aio_context_new(Error **errp)
558
{
559
int ret;
560
@@ -XXX,XX +XXX,XX @@ AioContext *aio_context_new(Error **errp)
561
}
562
g_source_set_can_recurse(&ctx->source, true);
563
qemu_lockcnt_init(&ctx->list_lock);
564
+
565
+ ctx->co_schedule_bh = aio_bh_new(ctx, co_schedule_bh_cb, ctx);
566
+ QSLIST_INIT(&ctx->scheduled_coroutines);
567
+
568
aio_set_event_notifier(ctx, &ctx->notifier,
569
false,
570
(EventNotifierHandler *)
571
@@ -XXX,XX +XXX,XX @@ fail:
572
return NULL;
573
}
574
575
+void aio_co_schedule(AioContext *ctx, Coroutine *co)
576
+{
577
+ trace_aio_co_schedule(ctx, co);
578
+ QSLIST_INSERT_HEAD_ATOMIC(&ctx->scheduled_coroutines,
579
+ co, co_scheduled_next);
580
+ qemu_bh_schedule(ctx->co_schedule_bh);
581
+}
582
+
583
+void aio_co_wake(struct Coroutine *co)
584
+{
585
+ AioContext *ctx;
586
+
587
+ /* Read coroutine before co->ctx. Matches smp_wmb in
588
+ * qemu_coroutine_enter.
589
+ */
590
+ smp_read_barrier_depends();
591
+ ctx = atomic_read(&co->ctx);
592
+
593
+ if (ctx != qemu_get_current_aio_context()) {
594
+ aio_co_schedule(ctx, co);
595
+ return;
596
+ }
597
+
598
+ if (qemu_in_coroutine()) {
599
+ Coroutine *self = qemu_coroutine_self();
600
+ assert(self != co);
601
+ QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, co, co_queue_next);
602
+ } else {
603
+ aio_context_acquire(ctx);
604
+ qemu_coroutine_enter(co);
605
+ aio_context_release(ctx);
606
+ }
607
+}
608
+
609
void aio_context_ref(AioContext *ctx)
610
{
611
g_source_ref(&ctx->source);
612
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
613
index XXXXXXX..XXXXXXX 100644
614
--- a/util/qemu-coroutine.c
615
+++ b/util/qemu-coroutine.c
616
@@ -XXX,XX +XXX,XX @@
617
#include "qemu/atomic.h"
618
#include "qemu/coroutine.h"
619
#include "qemu/coroutine_int.h"
620
+#include "block/aio.h"
621
622
enum {
623
POOL_BATCH_SIZE = 64,
624
@@ -XXX,XX +XXX,XX @@ void qemu_coroutine_enter(Coroutine *co)
625
}
626
627
co->caller = self;
628
+ co->ctx = qemu_get_current_aio_context();
629
+
630
+ /* Store co->ctx before anything that stores co. Matches
631
+ * barrier in aio_co_wake.
632
+ */
633
+ smp_wmb();
634
+
635
ret = qemu_coroutine_switch(self, co, COROUTINE_ENTER);
636
637
qemu_co_queue_run_restart(co);
638
diff --git a/util/trace-events b/util/trace-events
639
index XXXXXXX..XXXXXXX 100644
640
--- a/util/trace-events
641
+++ b/util/trace-events
642
@@ -XXX,XX +XXX,XX @@ run_poll_handlers_end(void *ctx, bool progress) "ctx %p progress %d"
643
poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
644
poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64
645
646
+# util/async.c
647
+aio_co_schedule(void *ctx, void *co) "ctx %p co %p"
648
+aio_co_schedule_bh_cb(void *ctx, void *co) "ctx %p co %p"
649
+
650
# util/thread-pool.c
651
thread_pool_submit(void *pool, void *req, void *opaque) "pool %p req %p opaque %p"
652
thread_pool_complete(void *pool, void *req, void *opaque, int ret) "pool %p req %p opaque %p ret %d"
44
--
653
--
45
2.26.2
654
2.9.3
46
655
47
656
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Mark the verify functions as "private" with a leading underscore, to
3
qcow2_create2 calls this. Do not run a nested event loop, as that
4
discourage their use. Update type signatures while we're here.
4
breaks when aio_co_wake tries to queue the coroutine on the co_queue_wakeup
5
list of the currently running one.
5
6
6
(Also, make pending patches not yet using the new entry points fail in a
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
very obvious way.)
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Reviewed-by: Fam Zheng <famz@redhat.com>
10
Message-id: 20170213135235.12274-4-pbonzini@redhat.com
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
block/block-backend.c | 12 ++++++++----
14
1 file changed, 8 insertions(+), 4 deletions(-)
8
15
9
Signed-off-by: John Snow <jsnow@redhat.com>
16
diff --git a/block/block-backend.c b/block/block-backend.c
10
Message-Id: <20200331000014.11581-14-jsnow@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
tests/qemu-iotests/iotests.py | 23 +++++++++++++----------
16
1 file changed, 13 insertions(+), 10 deletions(-)
17
18
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
19
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
20
--- a/tests/qemu-iotests/iotests.py
18
--- a/block/block-backend.c
21
+++ b/tests/qemu-iotests/iotests.py
19
+++ b/block/block-backend.c
22
@@ -XXX,XX +XXX,XX @@ def case_notrun(reason):
20
@@ -XXX,XX +XXX,XX @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
23
open('%s/%s.casenotrun' % (output_dir, seq), 'a').write(
21
{
24
' [case not run] ' + reason + '\n')
22
QEMUIOVector qiov;
25
23
struct iovec iov;
26
-def verify_image_format(supported_fmts=(), unsupported_fmts=()):
24
- Coroutine *co;
27
+def _verify_image_format(supported_fmts: Sequence[str] = (),
25
BlkRwCo rwco;
28
+ unsupported_fmts: Sequence[str] = ()) -> None:
26
29
assert not (supported_fmts and unsupported_fmts)
27
iov = (struct iovec) {
30
28
@@ -XXX,XX +XXX,XX @@ static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf,
31
if 'generic' in supported_fmts and \
29
.ret = NOT_DONE,
32
@@ -XXX,XX +XXX,XX @@ def verify_image_format(supported_fmts=(), unsupported_fmts=()):
30
};
33
if not_sup or (imgfmt in unsupported_fmts):
31
34
notrun('not suitable for this image format: %s' % imgfmt)
32
- co = qemu_coroutine_create(co_entry, &rwco);
35
33
- qemu_coroutine_enter(co);
36
-def verify_protocol(supported=(), unsupported=()):
34
- BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE);
37
+def _verify_protocol(supported: Sequence[str] = (),
35
+ if (qemu_in_coroutine()) {
38
+ unsupported: Sequence[str] = ()) -> None:
36
+ /* Fast-path if already in coroutine context */
39
assert not (supported and unsupported)
37
+ co_entry(&rwco);
40
38
+ } else {
41
if 'generic' in supported:
39
+ Coroutine *co = qemu_coroutine_create(co_entry, &rwco);
42
@@ -XXX,XX +XXX,XX @@ def verify_protocol(supported=(), unsupported=()):
40
+ qemu_coroutine_enter(co);
43
if not_sup or (imgproto in unsupported):
41
+ BDRV_POLL_WHILE(blk_bs(blk), rwco.ret == NOT_DONE);
44
notrun('not suitable for this protocol: %s' % imgproto)
42
+ }
45
43
46
-def verify_platform(supported=(), unsupported=()):
44
return rwco.ret;
47
+def _verify_platform(supported: Sequence[str] = (),
45
}
48
+ unsupported: Sequence[str] = ()) -> None:
49
if any((sys.platform.startswith(x) for x in unsupported)):
50
notrun('not suitable for this OS: %s' % sys.platform)
51
52
@@ -XXX,XX +XXX,XX @@ def verify_platform(supported=(), unsupported=()):
53
if not any((sys.platform.startswith(x) for x in supported)):
54
notrun('not suitable for this OS: %s' % sys.platform)
55
56
-def verify_cache_mode(supported_cache_modes=()):
57
+def _verify_cache_mode(supported_cache_modes: Sequence[str] = ()) -> None:
58
if supported_cache_modes and (cachemode not in supported_cache_modes):
59
notrun('not suitable for this cache mode: %s' % cachemode)
60
61
-def verify_aio_mode(supported_aio_modes=()):
62
+def _verify_aio_mode(supported_aio_modes: Sequence[str] = ()):
63
if supported_aio_modes and (aiomode not in supported_aio_modes):
64
notrun('not suitable for this aio mode: %s' % aiomode)
65
66
@@ -XXX,XX +XXX,XX @@ def execute_setup_common(supported_fmts: Sequence[str] = (),
67
sys.stderr.write('Please run this test via the "check" script\n')
68
sys.exit(os.EX_USAGE)
69
70
- verify_image_format(supported_fmts, unsupported_fmts)
71
- verify_protocol(supported_protocols, unsupported_protocols)
72
- verify_platform(supported=supported_platforms)
73
- verify_cache_mode(supported_cache_modes)
74
- verify_aio_mode(supported_aio_modes)
75
+ _verify_image_format(supported_fmts, unsupported_fmts)
76
+ _verify_protocol(supported_protocols, unsupported_protocols)
77
+ _verify_platform(supported=supported_platforms)
78
+ _verify_cache_mode(supported_cache_modes)
79
+ _verify_aio_mode(supported_aio_modes)
80
81
debug = '-d' in sys.argv
82
if debug:
83
--
46
--
84
2.26.2
47
2.9.3
85
48
86
49
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Since this one is nicely factored to use a single entry point,
3
Once the thread pool starts using aio_co_wake, it will also need
4
use script_main to run the tests.
4
qemu_get_current_aio_context(). Make test-thread-pool create
5
an AioContext with qemu_init_main_loop, so that stubs/iothread.c
6
and tests/iothread.c can provide the rest.
5
7
6
Signed-off-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
9
Message-Id: <20200331000014.11581-13-jsnow@redhat.com>
11
Message-id: 20170213135235.12274-5-pbonzini@redhat.com
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
---
13
tests/qemu-iotests/258 | 11 ++++-------
14
tests/test-thread-pool.c | 12 +++---------
14
1 file changed, 4 insertions(+), 7 deletions(-)
15
1 file changed, 3 insertions(+), 9 deletions(-)
15
16
16
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
17
diff --git a/tests/test-thread-pool.c b/tests/test-thread-pool.c
17
index XXXXXXX..XXXXXXX 100755
18
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/258
19
--- a/tests/test-thread-pool.c
19
+++ b/tests/qemu-iotests/258
20
+++ b/tests/test-thread-pool.c
20
@@ -XXX,XX +XXX,XX @@ import iotests
21
@@ -XXX,XX +XXX,XX @@
21
from iotests import log, qemu_img, qemu_io_silent, \
22
#include "qapi/error.h"
22
filter_qmp_testfiles, filter_qmp_imgfmt
23
#include "qemu/timer.h"
23
24
#include "qemu/error-report.h"
24
-# Need backing file and change-backing-file support
25
+#include "qemu/main-loop.h"
25
-iotests.script_initialize(
26
26
- supported_fmts=['qcow2', 'qed'],
27
static AioContext *ctx;
27
- supported_platforms=['linux'],
28
static ThreadPool *pool;
28
-)
29
@@ -XXX,XX +XXX,XX @@ static void test_cancel_async(void)
30
int main(int argc, char **argv)
31
{
32
int ret;
33
- Error *local_error = NULL;
34
35
- init_clocks();
29
-
36
-
30
# Returns a node for blockdev-add
37
- ctx = aio_context_new(&local_error);
31
def node(node_name, path, backing=None, fmt=None, throttle=None):
38
- if (!ctx) {
32
if fmt is None:
39
- error_reportf_err(local_error, "Failed to create AIO Context: ");
33
@@ -XXX,XX +XXX,XX @@ def main():
40
- exit(1);
34
test_concurrent_finish(False)
41
- }
35
42
+ qemu_init_main_loop(&error_abort);
36
if __name__ == '__main__':
43
+ ctx = qemu_get_current_aio_context();
37
- main()
44
pool = aio_get_thread_pool(ctx);
38
+ # Need backing file and change-backing-file support
45
39
+ iotests.script_main(main,
46
g_test_init(&argc, &argv, NULL);
40
+ supported_fmts=['qcow2', 'qed'],
47
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
41
+ supported_platforms=['linux'])
48
49
ret = g_test_run();
50
51
- aio_context_unref(ctx);
52
return ret;
53
}
42
--
54
--
43
2.26.2
55
2.9.3
44
56
45
57
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Like script_main, but doesn't require a single point of entry.
3
This is in preparation for making qio_channel_yield work on
4
Replace all existing initialization sections with this drop-in replacement.
4
AioContexts other than the main one.
5
5
6
This brings debug support to all existing script-style iotests.
6
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
7
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Signed-off-by: John Snow <jsnow@redhat.com>
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Fam Zheng <famz@redhat.com>
10
Message-Id: <20200331000014.11581-12-jsnow@redhat.com>
10
Message-id: 20170213135235.12274-6-pbonzini@redhat.com
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
[mreitz: Give 274 the same treatment]
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
12
---
15
tests/qemu-iotests/149 | 3 +-
13
include/io/channel.h | 25 +++++++++++++++++++++++++
16
tests/qemu-iotests/194 | 4 +-
14
io/channel-command.c | 13 +++++++++++++
17
tests/qemu-iotests/202 | 4 +-
15
io/channel-file.c | 11 +++++++++++
18
tests/qemu-iotests/203 | 4 +-
16
io/channel-socket.c | 16 +++++++++++-----
19
tests/qemu-iotests/206 | 2 +-
17
io/channel-tls.c | 12 ++++++++++++
20
tests/qemu-iotests/207 | 6 ++-
18
io/channel-watch.c | 6 ++++++
21
tests/qemu-iotests/208 | 2 +-
19
io/channel.c | 11 +++++++++++
22
tests/qemu-iotests/209 | 2 +-
20
7 files changed, 89 insertions(+), 5 deletions(-)
23
tests/qemu-iotests/210 | 6 ++-
21
24
tests/qemu-iotests/211 | 6 ++-
22
diff --git a/include/io/channel.h b/include/io/channel.h
25
tests/qemu-iotests/212 | 6 ++-
23
index XXXXXXX..XXXXXXX 100644
26
tests/qemu-iotests/213 | 6 ++-
24
--- a/include/io/channel.h
27
tests/qemu-iotests/216 | 4 +-
25
+++ b/include/io/channel.h
28
tests/qemu-iotests/218 | 2 +-
29
tests/qemu-iotests/219 | 2 +-
30
tests/qemu-iotests/222 | 7 ++--
31
tests/qemu-iotests/224 | 4 +-
32
tests/qemu-iotests/228 | 6 ++-
33
tests/qemu-iotests/234 | 4 +-
34
tests/qemu-iotests/235 | 4 +-
35
tests/qemu-iotests/236 | 2 +-
36
tests/qemu-iotests/237 | 2 +-
37
tests/qemu-iotests/238 | 2 +
38
tests/qemu-iotests/242 | 2 +-
39
tests/qemu-iotests/246 | 2 +-
40
tests/qemu-iotests/248 | 2 +-
41
tests/qemu-iotests/254 | 2 +-
42
tests/qemu-iotests/255 | 2 +-
43
tests/qemu-iotests/256 | 2 +-
44
tests/qemu-iotests/258 | 7 ++--
45
tests/qemu-iotests/260 | 4 +-
46
tests/qemu-iotests/262 | 4 +-
47
tests/qemu-iotests/264 | 4 +-
48
tests/qemu-iotests/274 | 4 +-
49
tests/qemu-iotests/277 | 2 +
50
tests/qemu-iotests/280 | 8 ++--
51
tests/qemu-iotests/283 | 4 +-
52
tests/qemu-iotests/iotests.py | 76 +++++++++++++++++++++++------------
53
38 files changed, 132 insertions(+), 83 deletions(-)
54
55
diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149
56
index XXXXXXX..XXXXXXX 100755
57
--- a/tests/qemu-iotests/149
58
+++ b/tests/qemu-iotests/149
59
@@ -XXX,XX +XXX,XX @@ def test_once(config, qemu_img=False):
60
61
62
# Obviously we only work with the luks image format
63
-iotests.verify_image_format(supported_fmts=['luks'])
64
-iotests.verify_platform()
65
+iotests.script_initialize(supported_fmts=['luks'])
66
67
# We need sudo in order to run cryptsetup to create
68
# dm-crypt devices. This is safe to use on any
69
diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194
70
index XXXXXXX..XXXXXXX 100755
71
--- a/tests/qemu-iotests/194
72
+++ b/tests/qemu-iotests/194
73
@@ -XXX,XX +XXX,XX @@
26
@@ -XXX,XX +XXX,XX @@
74
27
75
import iotests
28
#include "qemu-common.h"
76
29
#include "qom/object.h"
77
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed', 'raw'])
30
+#include "block/aio.h"
78
-iotests.verify_platform(['linux'])
31
79
+iotests.script_initialize(supported_fmts=['qcow2', 'qed', 'raw'],
32
#define TYPE_QIO_CHANNEL "qio-channel"
80
+ supported_platforms=['linux'])
33
#define QIO_CHANNEL(obj) \
81
34
@@ -XXX,XX +XXX,XX @@ struct QIOChannelClass {
82
with iotests.FilePath('source.img') as source_img_path, \
35
off_t offset,
83
iotests.FilePath('dest.img') as dest_img_path, \
36
int whence,
84
diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202
37
Error **errp);
85
index XXXXXXX..XXXXXXX 100755
38
+ void (*io_set_aio_fd_handler)(QIOChannel *ioc,
86
--- a/tests/qemu-iotests/202
39
+ AioContext *ctx,
87
+++ b/tests/qemu-iotests/202
40
+ IOHandler *io_read,
88
@@ -XXX,XX +XXX,XX @@
41
+ IOHandler *io_write,
89
42
+ void *opaque);
90
import iotests
43
};
91
44
92
-iotests.verify_image_format(supported_fmts=['qcow2'])
45
/* General I/O handling functions */
93
-iotests.verify_platform(['linux'])
46
@@ -XXX,XX +XXX,XX @@ void qio_channel_yield(QIOChannel *ioc,
94
+iotests.script_initialize(supported_fmts=['qcow2'],
47
void qio_channel_wait(QIOChannel *ioc,
95
+ supported_platforms=['linux'])
48
GIOCondition condition);
96
49
97
with iotests.FilePath('disk0.img') as disk0_img_path, \
50
+/**
98
iotests.FilePath('disk1.img') as disk1_img_path, \
51
+ * qio_channel_set_aio_fd_handler:
99
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
52
+ * @ioc: the channel object
100
index XXXXXXX..XXXXXXX 100755
53
+ * @ctx: the AioContext to set the handlers on
101
--- a/tests/qemu-iotests/203
54
+ * @io_read: the read handler
102
+++ b/tests/qemu-iotests/203
55
+ * @io_write: the write handler
103
@@ -XXX,XX +XXX,XX @@
56
+ * @opaque: the opaque value passed to the handler
104
57
+ *
105
import iotests
58
+ * This is used internally by qio_channel_yield(). It can
106
59
+ * be used by channel implementations to forward the handlers
107
-iotests.verify_image_format(supported_fmts=['qcow2'])
60
+ * to another channel (e.g. from #QIOChannelTLS to the
108
-iotests.verify_platform(['linux'])
61
+ * underlying socket).
109
+iotests.script_initialize(supported_fmts=['qcow2'],
62
+ */
110
+ supported_platforms=['linux'])
63
+void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
111
64
+ AioContext *ctx,
112
with iotests.FilePath('disk0.img') as disk0_img_path, \
65
+ IOHandler *io_read,
113
iotests.FilePath('disk1.img') as disk1_img_path, \
66
+ IOHandler *io_write,
114
diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206
67
+ void *opaque);
115
index XXXXXXX..XXXXXXX 100755
68
+
116
--- a/tests/qemu-iotests/206
69
#endif /* QIO_CHANNEL_H */
117
+++ b/tests/qemu-iotests/206
70
diff --git a/io/channel-command.c b/io/channel-command.c
118
@@ -XXX,XX +XXX,XX @@
71
index XXXXXXX..XXXXXXX 100644
119
import iotests
72
--- a/io/channel-command.c
120
from iotests import imgfmt
73
+++ b/io/channel-command.c
121
74
@@ -XXX,XX +XXX,XX @@ static int qio_channel_command_close(QIOChannel *ioc,
122
-iotests.verify_image_format(supported_fmts=['qcow2'])
75
}
123
+iotests.script_initialize(supported_fmts=['qcow2'])
76
124
77
125
with iotests.FilePath('t.qcow2') as disk_path, \
78
+static void qio_channel_command_set_aio_fd_handler(QIOChannel *ioc,
126
iotests.FilePath('t.qcow2.base') as backing_path, \
79
+ AioContext *ctx,
127
diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207
80
+ IOHandler *io_read,
128
index XXXXXXX..XXXXXXX 100755
81
+ IOHandler *io_write,
129
--- a/tests/qemu-iotests/207
82
+ void *opaque)
130
+++ b/tests/qemu-iotests/207
83
+{
131
@@ -XXX,XX +XXX,XX @@ import iotests
84
+ QIOChannelCommand *cioc = QIO_CHANNEL_COMMAND(ioc);
132
import subprocess
85
+ aio_set_fd_handler(ctx, cioc->readfd, false, io_read, NULL, NULL, opaque);
133
import re
86
+ aio_set_fd_handler(ctx, cioc->writefd, false, NULL, io_write, NULL, opaque);
134
87
+}
135
-iotests.verify_image_format(supported_fmts=['raw'])
88
+
136
-iotests.verify_protocol(supported=['ssh'])
89
+
137
+iotests.script_initialize(
90
static GSource *qio_channel_command_create_watch(QIOChannel *ioc,
138
+ supported_fmts=['raw'],
91
GIOCondition condition)
139
+ supported_protocols=['ssh'],
92
{
140
+)
93
@@ -XXX,XX +XXX,XX @@ static void qio_channel_command_class_init(ObjectClass *klass,
141
94
ioc_klass->io_set_blocking = qio_channel_command_set_blocking;
142
def filter_hash(qmsg):
95
ioc_klass->io_close = qio_channel_command_close;
143
def _filter(key, value):
96
ioc_klass->io_create_watch = qio_channel_command_create_watch;
144
diff --git a/tests/qemu-iotests/208 b/tests/qemu-iotests/208
97
+ ioc_klass->io_set_aio_fd_handler = qio_channel_command_set_aio_fd_handler;
145
index XXXXXXX..XXXXXXX 100755
98
}
146
--- a/tests/qemu-iotests/208
99
147
+++ b/tests/qemu-iotests/208
100
static const TypeInfo qio_channel_command_info = {
148
@@ -XXX,XX +XXX,XX @@
101
diff --git a/io/channel-file.c b/io/channel-file.c
149
102
index XXXXXXX..XXXXXXX 100644
150
import iotests
103
--- a/io/channel-file.c
151
104
+++ b/io/channel-file.c
152
-iotests.verify_image_format(supported_fmts=['generic'])
105
@@ -XXX,XX +XXX,XX @@ static int qio_channel_file_close(QIOChannel *ioc,
153
+iotests.script_initialize(supported_fmts=['generic'])
106
}
154
107
155
with iotests.FilePath('disk.img') as disk_img_path, \
108
156
iotests.FilePath('disk-snapshot.img') as disk_snapshot_img_path, \
109
+static void qio_channel_file_set_aio_fd_handler(QIOChannel *ioc,
157
diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209
110
+ AioContext *ctx,
158
index XXXXXXX..XXXXXXX 100755
111
+ IOHandler *io_read,
159
--- a/tests/qemu-iotests/209
112
+ IOHandler *io_write,
160
+++ b/tests/qemu-iotests/209
113
+ void *opaque)
161
@@ -XXX,XX +XXX,XX @@ import iotests
114
+{
162
from iotests import qemu_img_create, qemu_io, qemu_img_verbose, qemu_nbd, \
115
+ QIOChannelFile *fioc = QIO_CHANNEL_FILE(ioc);
163
file_path
116
+ aio_set_fd_handler(ctx, fioc->fd, false, io_read, io_write, NULL, opaque);
164
117
+}
165
-iotests.verify_image_format(supported_fmts=['qcow2'])
118
+
166
+iotests.script_initialize(supported_fmts=['qcow2'])
119
static GSource *qio_channel_file_create_watch(QIOChannel *ioc,
167
120
GIOCondition condition)
168
disk = file_path('disk')
121
{
169
nbd_sock = file_path('nbd-sock', base_dir=iotests.sock_dir)
122
@@ -XXX,XX +XXX,XX @@ static void qio_channel_file_class_init(ObjectClass *klass,
170
diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210
123
ioc_klass->io_seek = qio_channel_file_seek;
171
index XXXXXXX..XXXXXXX 100755
124
ioc_klass->io_close = qio_channel_file_close;
172
--- a/tests/qemu-iotests/210
125
ioc_klass->io_create_watch = qio_channel_file_create_watch;
173
+++ b/tests/qemu-iotests/210
126
+ ioc_klass->io_set_aio_fd_handler = qio_channel_file_set_aio_fd_handler;
174
@@ -XXX,XX +XXX,XX @@
127
}
175
import iotests
128
176
from iotests import imgfmt
129
static const TypeInfo qio_channel_file_info = {
177
130
diff --git a/io/channel-socket.c b/io/channel-socket.c
178
-iotests.verify_image_format(supported_fmts=['luks'])
131
index XXXXXXX..XXXXXXX 100644
179
-iotests.verify_protocol(supported=['file'])
132
--- a/io/channel-socket.c
180
+iotests.script_initialize(
133
+++ b/io/channel-socket.c
181
+ supported_fmts=['luks'],
134
@@ -XXX,XX +XXX,XX @@ qio_channel_socket_set_blocking(QIOChannel *ioc,
182
+ supported_protocols=['file'],
135
qemu_set_block(sioc->fd);
183
+)
136
} else {
184
137
qemu_set_nonblock(sioc->fd);
185
with iotests.FilePath('t.luks') as disk_path, \
138
-#ifdef WIN32
186
iotests.VM() as vm:
139
- WSAEventSelect(sioc->fd, ioc->event,
187
diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211
140
- FD_READ | FD_ACCEPT | FD_CLOSE |
188
index XXXXXXX..XXXXXXX 100755
141
- FD_CONNECT | FD_WRITE | FD_OOB);
189
--- a/tests/qemu-iotests/211
142
-#endif
190
+++ b/tests/qemu-iotests/211
143
}
191
@@ -XXX,XX +XXX,XX @@
144
return 0;
192
import iotests
145
}
193
from iotests import imgfmt
146
@@ -XXX,XX +XXX,XX @@ qio_channel_socket_shutdown(QIOChannel *ioc,
194
147
return 0;
195
-iotests.verify_image_format(supported_fmts=['vdi'])
148
}
196
-iotests.verify_protocol(supported=['file'])
149
197
+iotests.script_initialize(
150
+static void qio_channel_socket_set_aio_fd_handler(QIOChannel *ioc,
198
+ supported_fmts=['vdi'],
151
+ AioContext *ctx,
199
+ supported_protocols=['file'],
152
+ IOHandler *io_read,
200
+)
153
+ IOHandler *io_write,
201
154
+ void *opaque)
202
def blockdev_create(vm, options):
155
+{
203
error = vm.blockdev_create(options)
156
+ QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(ioc);
204
diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212
157
+ aio_set_fd_handler(ctx, sioc->fd, false, io_read, io_write, NULL, opaque);
205
index XXXXXXX..XXXXXXX 100755
158
+}
206
--- a/tests/qemu-iotests/212
159
+
207
+++ b/tests/qemu-iotests/212
160
static GSource *qio_channel_socket_create_watch(QIOChannel *ioc,
208
@@ -XXX,XX +XXX,XX @@
161
GIOCondition condition)
209
import iotests
162
{
210
from iotests import imgfmt
163
@@ -XXX,XX +XXX,XX @@ static void qio_channel_socket_class_init(ObjectClass *klass,
211
164
ioc_klass->io_set_cork = qio_channel_socket_set_cork;
212
-iotests.verify_image_format(supported_fmts=['parallels'])
165
ioc_klass->io_set_delay = qio_channel_socket_set_delay;
213
-iotests.verify_protocol(supported=['file'])
166
ioc_klass->io_create_watch = qio_channel_socket_create_watch;
214
+iotests.script_initialize(
167
+ ioc_klass->io_set_aio_fd_handler = qio_channel_socket_set_aio_fd_handler;
215
+ supported_fmts=['parallels'],
168
}
216
+ supported_protocols=['file'],
169
217
+)
170
static const TypeInfo qio_channel_socket_info = {
218
171
diff --git a/io/channel-tls.c b/io/channel-tls.c
219
with iotests.FilePath('t.parallels') as disk_path, \
172
index XXXXXXX..XXXXXXX 100644
220
iotests.VM() as vm:
173
--- a/io/channel-tls.c
221
diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213
174
+++ b/io/channel-tls.c
222
index XXXXXXX..XXXXXXX 100755
175
@@ -XXX,XX +XXX,XX @@ static int qio_channel_tls_close(QIOChannel *ioc,
223
--- a/tests/qemu-iotests/213
176
return qio_channel_close(tioc->master, errp);
224
+++ b/tests/qemu-iotests/213
177
}
225
@@ -XXX,XX +XXX,XX @@
178
226
import iotests
179
+static void qio_channel_tls_set_aio_fd_handler(QIOChannel *ioc,
227
from iotests import imgfmt
180
+ AioContext *ctx,
228
181
+ IOHandler *io_read,
229
-iotests.verify_image_format(supported_fmts=['vhdx'])
182
+ IOHandler *io_write,
230
-iotests.verify_protocol(supported=['file'])
183
+ void *opaque)
231
+iotests.script_initialize(
184
+{
232
+ supported_fmts=['vhdx'],
185
+ QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc);
233
+ supported_protocols=['file'],
186
+
234
+)
187
+ qio_channel_set_aio_fd_handler(tioc->master, ctx, io_read, io_write, opaque);
235
188
+}
236
with iotests.FilePath('t.vhdx') as disk_path, \
189
+
237
iotests.VM() as vm:
190
static GSource *qio_channel_tls_create_watch(QIOChannel *ioc,
238
diff --git a/tests/qemu-iotests/216 b/tests/qemu-iotests/216
191
GIOCondition condition)
239
index XXXXXXX..XXXXXXX 100755
192
{
240
--- a/tests/qemu-iotests/216
193
@@ -XXX,XX +XXX,XX @@ static void qio_channel_tls_class_init(ObjectClass *klass,
241
+++ b/tests/qemu-iotests/216
194
ioc_klass->io_close = qio_channel_tls_close;
242
@@ -XXX,XX +XXX,XX @@ import iotests
195
ioc_klass->io_shutdown = qio_channel_tls_shutdown;
243
from iotests import log, qemu_img, qemu_io_silent
196
ioc_klass->io_create_watch = qio_channel_tls_create_watch;
244
197
+ ioc_klass->io_set_aio_fd_handler = qio_channel_tls_set_aio_fd_handler;
245
# Need backing file support
198
}
246
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'])
199
247
-iotests.verify_platform(['linux'])
200
static const TypeInfo qio_channel_tls_info = {
248
+iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk'],
201
diff --git a/io/channel-watch.c b/io/channel-watch.c
249
+ supported_platforms=['linux'])
202
index XXXXXXX..XXXXXXX 100644
250
203
--- a/io/channel-watch.c
251
log('')
204
+++ b/io/channel-watch.c
252
log('=== Copy-on-read across nodes ===')
205
@@ -XXX,XX +XXX,XX @@ GSource *qio_channel_create_socket_watch(QIOChannel *ioc,
253
diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218
206
GSource *source;
254
index XXXXXXX..XXXXXXX 100755
207
QIOChannelSocketSource *ssource;
255
--- a/tests/qemu-iotests/218
208
256
+++ b/tests/qemu-iotests/218
209
+#ifdef WIN32
257
@@ -XXX,XX +XXX,XX @@
210
+ WSAEventSelect(socket, ioc->event,
258
import iotests
211
+ FD_READ | FD_ACCEPT | FD_CLOSE |
259
from iotests import log, qemu_img, qemu_io_silent
212
+ FD_CONNECT | FD_WRITE | FD_OOB);
260
213
+#endif
261
-iotests.verify_image_format(supported_fmts=['qcow2', 'raw'])
214
+
262
+iotests.script_initialize(supported_fmts=['qcow2', 'raw'])
215
source = g_source_new(&qio_channel_socket_source_funcs,
263
216
sizeof(QIOChannelSocketSource));
264
217
ssource = (QIOChannelSocketSource *)source;
265
# Launches the VM, adds two null-co nodes (source and target), and
218
diff --git a/io/channel.c b/io/channel.c
266
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
219
index XXXXXXX..XXXXXXX 100644
267
index XXXXXXX..XXXXXXX 100755
220
--- a/io/channel.c
268
--- a/tests/qemu-iotests/219
221
+++ b/io/channel.c
269
+++ b/tests/qemu-iotests/219
222
@@ -XXX,XX +XXX,XX @@ GSource *qio_channel_create_watch(QIOChannel *ioc,
270
@@ -XXX,XX +XXX,XX @@
223
}
271
224
272
import iotests
225
273
226
+void qio_channel_set_aio_fd_handler(QIOChannel *ioc,
274
-iotests.verify_image_format(supported_fmts=['qcow2'])
227
+ AioContext *ctx,
275
+iotests.script_initialize(supported_fmts=['qcow2'])
228
+ IOHandler *io_read,
276
229
+ IOHandler *io_write,
277
img_size = 4 * 1024 * 1024
230
+ void *opaque)
278
231
+{
279
diff --git a/tests/qemu-iotests/222 b/tests/qemu-iotests/222
232
+ QIOChannelClass *klass = QIO_CHANNEL_GET_CLASS(ioc);
280
index XXXXXXX..XXXXXXX 100755
233
+
281
--- a/tests/qemu-iotests/222
234
+ klass->io_set_aio_fd_handler(ioc, ctx, io_read, io_write, opaque);
282
+++ b/tests/qemu-iotests/222
235
+}
283
@@ -XXX,XX +XXX,XX @@
236
+
284
import iotests
237
guint qio_channel_add_watch(QIOChannel *ioc,
285
from iotests import log, qemu_img, qemu_io, qemu_io_silent
238
GIOCondition condition,
286
239
QIOChannelFunc func,
287
-iotests.verify_platform(['linux'])
288
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk',
289
- 'vhdx', 'raw'])
290
+iotests.script_initialize(
291
+ supported_fmts=['qcow2', 'qcow', 'qed', 'vmdk', 'vhdx', 'raw'],
292
+ supported_platforms=['linux'],
293
+)
294
295
patterns = [("0x5d", "0", "64k"),
296
("0xd5", "1M", "64k"),
297
diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224
298
index XXXXXXX..XXXXXXX 100755
299
--- a/tests/qemu-iotests/224
300
+++ b/tests/qemu-iotests/224
301
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \
302
import json
303
304
# Need backing file support (for arbitrary backing formats)
305
-iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed'])
306
-iotests.verify_platform(['linux'])
307
+iotests.script_initialize(supported_fmts=['qcow2', 'qcow', 'qed'],
308
+ supported_platforms=['linux'])
309
310
311
# There are two variations of this test:
312
diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228
313
index XXXXXXX..XXXXXXX 100755
314
--- a/tests/qemu-iotests/228
315
+++ b/tests/qemu-iotests/228
316
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \
317
filter_qmp_testfiles, filter_qmp_imgfmt
318
319
# Need backing file and change-backing-file support
320
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
321
-iotests.verify_platform(['linux'])
322
+iotests.script_initialize(
323
+ supported_fmts=['qcow2', 'qed'],
324
+ supported_platforms=['linux'],
325
+)
326
327
328
def log_node_info(node):
329
diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234
330
index XXXXXXX..XXXXXXX 100755
331
--- a/tests/qemu-iotests/234
332
+++ b/tests/qemu-iotests/234
333
@@ -XXX,XX +XXX,XX @@
334
import iotests
335
import os
336
337
-iotests.verify_image_format(supported_fmts=['qcow2'])
338
-iotests.verify_platform(['linux'])
339
+iotests.script_initialize(supported_fmts=['qcow2'],
340
+ supported_platforms=['linux'])
341
342
with iotests.FilePath('img') as img_path, \
343
iotests.FilePath('backing') as backing_path, \
344
diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235
345
index XXXXXXX..XXXXXXX 100755
346
--- a/tests/qemu-iotests/235
347
+++ b/tests/qemu-iotests/235
348
@@ -XXX,XX +XXX,XX @@ sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
349
350
from qemu.machine import QEMUMachine
351
352
+iotests.script_initialize(supported_fmts=['qcow2'])
353
+
354
# Note:
355
# This test was added to check that mirror dead-lock was fixed (see previous
356
# commit before this test addition).
357
@@ -XXX,XX +XXX,XX @@ from qemu.machine import QEMUMachine
358
359
size = 1 * 1024 * 1024 * 1024
360
361
-iotests.verify_image_format(supported_fmts=['qcow2'])
362
-
363
disk = file_path('disk')
364
365
# prepare source image
366
diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236
367
index XXXXXXX..XXXXXXX 100755
368
--- a/tests/qemu-iotests/236
369
+++ b/tests/qemu-iotests/236
370
@@ -XXX,XX +XXX,XX @@
371
import iotests
372
from iotests import log
373
374
-iotests.verify_image_format(supported_fmts=['generic'])
375
+iotests.script_initialize(supported_fmts=['generic'])
376
size = 64 * 1024 * 1024
377
granularity = 64 * 1024
378
379
diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237
380
index XXXXXXX..XXXXXXX 100755
381
--- a/tests/qemu-iotests/237
382
+++ b/tests/qemu-iotests/237
383
@@ -XXX,XX +XXX,XX @@ import math
384
import iotests
385
from iotests import imgfmt
386
387
-iotests.verify_image_format(supported_fmts=['vmdk'])
388
+iotests.script_initialize(supported_fmts=['vmdk'])
389
390
with iotests.FilePath('t.vmdk') as disk_path, \
391
iotests.FilePath('t.vmdk.1') as extent1_path, \
392
diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238
393
index XXXXXXX..XXXXXXX 100755
394
--- a/tests/qemu-iotests/238
395
+++ b/tests/qemu-iotests/238
396
@@ -XXX,XX +XXX,XX @@ import os
397
import iotests
398
from iotests import log
399
400
+iotests.script_initialize()
401
+
402
virtio_scsi_device = iotests.get_virtio_scsi_device()
403
404
vm = iotests.VM()
405
diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242
406
index XXXXXXX..XXXXXXX 100755
407
--- a/tests/qemu-iotests/242
408
+++ b/tests/qemu-iotests/242
409
@@ -XXX,XX +XXX,XX @@ import struct
410
from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \
411
file_path, img_info_log, log, filter_qemu_io
412
413
-iotests.verify_image_format(supported_fmts=['qcow2'])
414
+iotests.script_initialize(supported_fmts=['qcow2'])
415
416
disk = file_path('disk')
417
chunk = 256 * 1024
418
diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246
419
index XXXXXXX..XXXXXXX 100755
420
--- a/tests/qemu-iotests/246
421
+++ b/tests/qemu-iotests/246
422
@@ -XXX,XX +XXX,XX @@
423
import iotests
424
from iotests import log
425
426
-iotests.verify_image_format(supported_fmts=['qcow2'])
427
+iotests.script_initialize(supported_fmts=['qcow2'])
428
size = 64 * 1024 * 1024 * 1024
429
gran_small = 32 * 1024
430
gran_large = 128 * 1024
431
diff --git a/tests/qemu-iotests/248 b/tests/qemu-iotests/248
432
index XXXXXXX..XXXXXXX 100755
433
--- a/tests/qemu-iotests/248
434
+++ b/tests/qemu-iotests/248
435
@@ -XXX,XX +XXX,XX @@
436
import iotests
437
from iotests import qemu_img_create, qemu_io, file_path, filter_qmp_testfiles
438
439
-iotests.verify_image_format(supported_fmts=['qcow2'])
440
+iotests.script_initialize(supported_fmts=['qcow2'])
441
442
source, target = file_path('source', 'target')
443
size = 5 * 1024 * 1024
444
diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254
445
index XXXXXXX..XXXXXXX 100755
446
--- a/tests/qemu-iotests/254
447
+++ b/tests/qemu-iotests/254
448
@@ -XXX,XX +XXX,XX @@
449
import iotests
450
from iotests import qemu_img_create, file_path, log
451
452
-iotests.verify_image_format(supported_fmts=['qcow2'])
453
+iotests.script_initialize(supported_fmts=['qcow2'])
454
455
disk, top = file_path('disk', 'top')
456
size = 1024 * 1024
457
diff --git a/tests/qemu-iotests/255 b/tests/qemu-iotests/255
458
index XXXXXXX..XXXXXXX 100755
459
--- a/tests/qemu-iotests/255
460
+++ b/tests/qemu-iotests/255
461
@@ -XXX,XX +XXX,XX @@
462
import iotests
463
from iotests import imgfmt
464
465
-iotests.verify_image_format(supported_fmts=['qcow2'])
466
+iotests.script_initialize(supported_fmts=['qcow2'])
467
468
iotests.log('Finishing a commit job with background reads')
469
iotests.log('============================================')
470
diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256
471
index XXXXXXX..XXXXXXX 100755
472
--- a/tests/qemu-iotests/256
473
+++ b/tests/qemu-iotests/256
474
@@ -XXX,XX +XXX,XX @@ import os
475
import iotests
476
from iotests import log
477
478
-iotests.verify_image_format(supported_fmts=['qcow2'])
479
+iotests.script_initialize(supported_fmts=['qcow2'])
480
size = 64 * 1024 * 1024
481
482
with iotests.FilePath('img0') as img0_path, \
483
diff --git a/tests/qemu-iotests/258 b/tests/qemu-iotests/258
484
index XXXXXXX..XXXXXXX 100755
485
--- a/tests/qemu-iotests/258
486
+++ b/tests/qemu-iotests/258
487
@@ -XXX,XX +XXX,XX @@ from iotests import log, qemu_img, qemu_io_silent, \
488
filter_qmp_testfiles, filter_qmp_imgfmt
489
490
# Need backing file and change-backing-file support
491
-iotests.verify_image_format(supported_fmts=['qcow2', 'qed'])
492
-iotests.verify_platform(['linux'])
493
-
494
+iotests.script_initialize(
495
+ supported_fmts=['qcow2', 'qed'],
496
+ supported_platforms=['linux'],
497
+)
498
499
# Returns a node for blockdev-add
500
def node(node_name, path, backing=None, fmt=None, throttle=None):
501
diff --git a/tests/qemu-iotests/260 b/tests/qemu-iotests/260
502
index XXXXXXX..XXXXXXX 100755
503
--- a/tests/qemu-iotests/260
504
+++ b/tests/qemu-iotests/260
505
@@ -XXX,XX +XXX,XX @@
506
import iotests
507
from iotests import qemu_img_create, file_path, log, filter_qmp_event
508
509
-iotests.verify_image_format(supported_fmts=['qcow2'])
510
+iotests.script_initialize(
511
+ supported_fmts=['qcow2']
512
+)
513
514
base, top = file_path('base', 'top')
515
size = 64 * 1024 * 3
516
diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262
517
index XXXXXXX..XXXXXXX 100755
518
--- a/tests/qemu-iotests/262
519
+++ b/tests/qemu-iotests/262
520
@@ -XXX,XX +XXX,XX @@
521
import iotests
522
import os
523
524
-iotests.verify_image_format(supported_fmts=['qcow2'])
525
-iotests.verify_platform(['linux'])
526
+iotests.script_initialize(supported_fmts=['qcow2'],
527
+ supported_platforms=['linux'])
528
529
with iotests.FilePath('img') as img_path, \
530
iotests.FilePath('mig_fifo') as fifo, \
531
diff --git a/tests/qemu-iotests/264 b/tests/qemu-iotests/264
532
index XXXXXXX..XXXXXXX 100755
533
--- a/tests/qemu-iotests/264
534
+++ b/tests/qemu-iotests/264
535
@@ -XXX,XX +XXX,XX @@ import iotests
536
from iotests import qemu_img_create, qemu_io_silent_check, file_path, \
537
qemu_nbd_popen, log
538
539
-iotests.verify_image_format(supported_fmts=['qcow2'])
540
+iotests.script_initialize(
541
+ supported_fmts=['qcow2'],
542
+)
543
544
disk_a, disk_b, nbd_sock = file_path('disk_a', 'disk_b', 'nbd-sock')
545
nbd_uri = 'nbd+unix:///?socket=' + nbd_sock
546
diff --git a/tests/qemu-iotests/274 b/tests/qemu-iotests/274
547
index XXXXXXX..XXXXXXX 100755
548
--- a/tests/qemu-iotests/274
549
+++ b/tests/qemu-iotests/274
550
@@ -XXX,XX +XXX,XX @@
551
552
import iotests
553
554
-iotests.verify_image_format(supported_fmts=['qcow2'])
555
-iotests.verify_platform(['linux'])
556
+iotests.script_initialize(supported_fmts=['qcow2'],
557
+ supported_platforms=['linux'])
558
559
size_short = 1 * 1024 * 1024
560
size_long = 2 * 1024 * 1024
561
diff --git a/tests/qemu-iotests/277 b/tests/qemu-iotests/277
562
index XXXXXXX..XXXXXXX 100755
563
--- a/tests/qemu-iotests/277
564
+++ b/tests/qemu-iotests/277
565
@@ -XXX,XX +XXX,XX @@ import subprocess
566
import iotests
567
from iotests import file_path, log
568
569
+iotests.script_initialize()
570
+
571
572
nbd_sock, conf_file = file_path('nbd-sock', 'nbd-fault-injector.conf')
573
574
diff --git a/tests/qemu-iotests/280 b/tests/qemu-iotests/280
575
index XXXXXXX..XXXXXXX 100755
576
--- a/tests/qemu-iotests/280
577
+++ b/tests/qemu-iotests/280
578
@@ -XXX,XX +XXX,XX @@
579
import iotests
580
import os
581
582
-iotests.verify_image_format(supported_fmts=['qcow2'])
583
-iotests.verify_protocol(supported=['file'])
584
-iotests.verify_platform(['linux'])
585
+iotests.script_initialize(
586
+ supported_fmts=['qcow2'],
587
+ supported_protocols=['file'],
588
+ supported_platforms=['linux'],
589
+)
590
591
with iotests.FilePath('base') as base_path , \
592
iotests.FilePath('top') as top_path, \
593
diff --git a/tests/qemu-iotests/283 b/tests/qemu-iotests/283
594
index XXXXXXX..XXXXXXX 100644
595
--- a/tests/qemu-iotests/283
596
+++ b/tests/qemu-iotests/283
597
@@ -XXX,XX +XXX,XX @@
598
import iotests
599
600
# The test is unrelated to formats, restrict it to qcow2 to avoid extra runs
601
-iotests.verify_image_format(supported_fmts=['qcow2'])
602
+iotests.script_initialize(
603
+ supported_fmts=['qcow2'],
604
+)
605
606
size = 1024 * 1024
607
608
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
609
index XXXXXXX..XXXXXXX 100644
610
--- a/tests/qemu-iotests/iotests.py
611
+++ b/tests/qemu-iotests/iotests.py
612
@@ -XXX,XX +XXX,XX @@ import signal
613
import struct
614
import subprocess
615
import sys
616
-from typing import (Any, Callable, Dict, Iterable, List, Optional, TypeVar)
617
+from typing import (Any, Callable, Dict, Iterable,
618
+ List, Optional, Sequence, TypeVar)
619
import unittest
620
621
# pylint: disable=import-error, wrong-import-position
622
@@ -XXX,XX +XXX,XX @@ def verify_protocol(supported=(), unsupported=()):
623
if not_sup or (imgproto in unsupported):
624
notrun('not suitable for this protocol: %s' % imgproto)
625
626
-def verify_platform(supported=None, unsupported=None):
627
- if unsupported is not None:
628
- if any((sys.platform.startswith(x) for x in unsupported)):
629
- notrun('not suitable for this OS: %s' % sys.platform)
630
+def verify_platform(supported=(), unsupported=()):
631
+ if any((sys.platform.startswith(x) for x in unsupported)):
632
+ notrun('not suitable for this OS: %s' % sys.platform)
633
634
- if supported is not None:
635
+ if supported:
636
if not any((sys.platform.startswith(x) for x in supported)):
637
notrun('not suitable for this OS: %s' % sys.platform)
638
639
@@ -XXX,XX +XXX,XX @@ def skip_if_user_is_root(func):
640
return func(*args, **kwargs)
641
return func_wrapper
642
643
-def execute_unittest(output, verbosity, debug):
644
+def execute_unittest(debug=False):
645
+ """Executes unittests within the calling module."""
646
+
647
+ verbosity = 2 if debug else 1
648
+
649
+ if debug:
650
+ output = sys.stdout
651
+ else:
652
+ # We need to filter out the time taken from the output so that
653
+ # qemu-iotest can reliably diff the results against master output.
654
+ output = io.StringIO()
655
+
656
runner = unittest.TextTestRunner(stream=output, descriptions=True,
657
verbosity=verbosity)
658
try:
659
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
660
# exception
661
unittest.main(testRunner=runner)
662
finally:
663
+ # We need to filter out the time taken from the output so that
664
+ # qemu-iotest can reliably diff the results against master output.
665
if not debug:
666
out = output.getvalue()
667
out = re.sub(r'Ran (\d+) tests? in [\d.]+s', r'Ran \1 tests', out)
668
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
669
670
sys.stderr.write(out)
671
672
-def execute_test(test_function=None,
673
- supported_fmts=(),
674
- supported_platforms=None,
675
- supported_cache_modes=(), supported_aio_modes=(),
676
- unsupported_fmts=(), supported_protocols=(),
677
- unsupported_protocols=()):
678
- """Run either unittest or script-style tests."""
679
+def execute_setup_common(supported_fmts: Sequence[str] = (),
680
+ supported_platforms: Sequence[str] = (),
681
+ supported_cache_modes: Sequence[str] = (),
682
+ supported_aio_modes: Sequence[str] = (),
683
+ unsupported_fmts: Sequence[str] = (),
684
+ supported_protocols: Sequence[str] = (),
685
+ unsupported_protocols: Sequence[str] = ()) -> bool:
686
+ """
687
+ Perform necessary setup for either script-style or unittest-style tests.
688
+
689
+ :return: Bool; Whether or not debug mode has been requested via the CLI.
690
+ """
691
+ # Note: Python 3.6 and pylint do not like 'Collection' so use 'Sequence'.
692
693
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
694
# indicate that we're not being run via "check". There may be
695
@@ -XXX,XX +XXX,XX @@ def execute_test(test_function=None,
696
sys.stderr.write('Please run this test via the "check" script\n')
697
sys.exit(os.EX_USAGE)
698
699
- debug = '-d' in sys.argv
700
- verbosity = 1
701
verify_image_format(supported_fmts, unsupported_fmts)
702
verify_protocol(supported_protocols, unsupported_protocols)
703
verify_platform(supported=supported_platforms)
704
verify_cache_mode(supported_cache_modes)
705
verify_aio_mode(supported_aio_modes)
706
707
+ debug = '-d' in sys.argv
708
if debug:
709
- output = sys.stdout
710
- verbosity = 2
711
sys.argv.remove('-d')
712
- else:
713
- # We need to filter out the time taken from the output so that
714
- # qemu-iotest can reliably diff the results against master output.
715
- output = io.StringIO()
716
-
717
logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN))
718
719
+ return debug
720
+
721
+def execute_test(*args, test_function=None, **kwargs):
722
+ """Run either unittest or script-style tests."""
723
+
724
+ debug = execute_setup_common(*args, **kwargs)
725
if not test_function:
726
- execute_unittest(output, verbosity, debug)
727
+ execute_unittest(debug)
728
else:
729
test_function()
730
731
+# This is called from script-style iotests without a single point of entry
732
+def script_initialize(*args, **kwargs):
733
+ """Initialize script-style tests without running any tests."""
734
+ execute_setup_common(*args, **kwargs)
735
+
736
+# This is called from script-style iotests with a single point of entry
737
def script_main(test_function, *args, **kwargs):
738
"""Run script-style tests outside of the unittest framework"""
739
- execute_test(test_function, *args, **kwargs)
740
+ execute_test(*args, test_function=test_function, **kwargs)
741
742
+# This is called from unittest style iotests
743
def main(*args, **kwargs):
744
"""Run tests using the unittest framework"""
745
- execute_test(None, *args, **kwargs)
746
+ execute_test(*args, **kwargs)
747
--
240
--
748
2.26.2
241
2.9.3
749
242
750
243
diff view generated by jsdifflib
1
From: Maxim Levitsky <mlevitsk@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Commit f62514b3def5fb2acbef64d0e053c0c31fa45aff made qemu-img reject -o "" but this test uses it.
3
Support separate coroutines for reading and writing, and place the
4
Since this test only tries to do a dry-run run of qemu-img amend,
4
read/write handlers on the AioContext that the QIOChannel is registered
5
replace the -o "" with dummy -o "size=$size".
5
with.
6
6
7
Fixes: f62514b3def5fb2acbef64d0e053c0c31fa45aff
7
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
8
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Maxim Levitsky <mlevitsk@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Message-Id: <20200504131959.9533-1-mlevitsk@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Message-id: 20170213135235.12274-7-pbonzini@redhat.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
13
---
13
tests/qemu-iotests/153 | 2 +-
14
include/io/channel.h | 47 ++++++++++++++++++++++++++--
14
tests/qemu-iotests/153.out | 12 ++++++------
15
io/channel.c | 86 +++++++++++++++++++++++++++++++++++++++-------------
15
2 files changed, 7 insertions(+), 7 deletions(-)
16
2 files changed, 109 insertions(+), 24 deletions(-)
16
17
17
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
18
diff --git a/include/io/channel.h b/include/io/channel.h
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/153
20
+++ b/tests/qemu-iotests/153
21
@@ -XXX,XX +XXX,XX @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do
22
_run_cmd $QEMU_IMG check $L "${TEST_IMG}"
23
_run_cmd $QEMU_IMG compare $L "${TEST_IMG}" "${TEST_IMG}"
24
_run_cmd $QEMU_IMG map $L "${TEST_IMG}"
25
- _run_cmd $QEMU_IMG amend -o "" $L "${TEST_IMG}"
26
+ _run_cmd $QEMU_IMG amend -o "size=$size" $L "${TEST_IMG}"
27
_run_cmd $QEMU_IMG commit $L "${TEST_IMG}"
28
_run_cmd $QEMU_IMG resize $L "${TEST_IMG}" $size
29
_run_cmd $QEMU_IMG rebase $L "${TEST_IMG}" -b "${TEST_IMG}.base"
30
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
31
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
32
--- a/tests/qemu-iotests/153.out
20
--- a/include/io/channel.h
33
+++ b/tests/qemu-iotests/153.out
21
+++ b/include/io/channel.h
34
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper map TEST_DIR/t.qcow2
22
@@ -XXX,XX +XXX,XX @@
35
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock
23
36
Is another process using the image [TEST_DIR/t.qcow2]?
24
#include "qemu-common.h"
37
25
#include "qom/object.h"
38
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
26
+#include "qemu/coroutine.h"
39
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
27
#include "block/aio.h"
40
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
28
41
Is another process using the image [TEST_DIR/t.qcow2]?
29
#define TYPE_QIO_CHANNEL "qio-channel"
42
30
@@ -XXX,XX +XXX,XX @@ struct QIOChannel {
43
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
31
Object parent;
44
32
unsigned int features; /* bitmask of QIOChannelFeatures */
45
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
33
char *name;
46
34
+ AioContext *ctx;
47
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
35
+ Coroutine *read_coroutine;
48
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
36
+ Coroutine *write_coroutine;
49
qemu-img: unrecognized option '-U'
37
#ifdef _WIN32
50
Try 'qemu-img --help' for more information
38
HANDLE event; /* For use with GSource on Win32 */
51
39
#endif
52
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
40
@@ -XXX,XX +XXX,XX @@ guint qio_channel_add_watch(QIOChannel *ioc,
53
41
54
_qemu_img_wrapper map TEST_DIR/t.qcow2
42
55
43
/**
56
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
44
+ * qio_channel_attach_aio_context:
57
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
45
+ * @ioc: the channel object
58
qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock
46
+ * @ctx: the #AioContext to set the handlers on
59
Is another process using the image [TEST_DIR/t.qcow2]?
47
+ *
60
48
+ * Request that qio_channel_yield() sets I/O handlers on
61
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
49
+ * the given #AioContext. If @ctx is %NULL, qio_channel_yield()
62
50
+ * uses QEMU's main thread event loop.
63
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
51
+ *
64
52
+ * You can move a #QIOChannel from one #AioContext to another even if
65
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
53
+ * I/O handlers are set for a coroutine. However, #QIOChannel provides
66
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
54
+ * no synchronization between the calls to qio_channel_yield() and
67
qemu-img: unrecognized option '-U'
55
+ * qio_channel_attach_aio_context().
68
Try 'qemu-img --help' for more information
56
+ *
69
57
+ * Therefore you should first call qio_channel_detach_aio_context()
70
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
58
+ * to ensure that the coroutine is not entered concurrently. Then,
71
59
+ * while the coroutine has yielded, call qio_channel_attach_aio_context(),
72
_qemu_img_wrapper map TEST_DIR/t.qcow2
60
+ * and then aio_co_schedule() to place the coroutine on the new
73
61
+ * #AioContext. The calls to qio_channel_detach_aio_context()
74
-_qemu_img_wrapper amend -o TEST_DIR/t.qcow2
62
+ * and qio_channel_attach_aio_context() should be protected with
75
+_qemu_img_wrapper amend -o size=32M TEST_DIR/t.qcow2
63
+ * aio_context_acquire() and aio_context_release().
76
64
+ */
77
_qemu_img_wrapper commit TEST_DIR/t.qcow2
65
+void qio_channel_attach_aio_context(QIOChannel *ioc,
78
66
+ AioContext *ctx);
79
@@ -XXX,XX +XXX,XX @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2
67
+
80
68
+/**
81
_qemu_img_wrapper map -U TEST_DIR/t.qcow2
69
+ * qio_channel_detach_aio_context:
82
70
+ * @ioc: the channel object
83
-_qemu_img_wrapper amend -o -U TEST_DIR/t.qcow2
71
+ *
84
+_qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2
72
+ * Disable any I/O handlers set by qio_channel_yield(). With the
85
qemu-img: unrecognized option '-U'
73
+ * help of aio_co_schedule(), this allows moving a coroutine that was
86
Try 'qemu-img --help' for more information
74
+ * paused by qio_channel_yield() to another context.
75
+ */
76
+void qio_channel_detach_aio_context(QIOChannel *ioc);
77
+
78
+/**
79
* qio_channel_yield:
80
* @ioc: the channel object
81
* @condition: the I/O condition to wait for
82
*
83
- * Yields execution from the current coroutine until
84
- * the condition indicated by @condition becomes
85
- * available.
86
+ * Yields execution from the current coroutine until the condition
87
+ * indicated by @condition becomes available. @condition must
88
+ * be either %G_IO_IN or %G_IO_OUT; it cannot contain both. In
89
+ * addition, no two coroutine can be waiting on the same condition
90
+ * and channel at the same time.
91
*
92
* This must only be called from coroutine context
93
*/
94
diff --git a/io/channel.c b/io/channel.c
95
index XXXXXXX..XXXXXXX 100644
96
--- a/io/channel.c
97
+++ b/io/channel.c
98
@@ -XXX,XX +XXX,XX @@
99
#include "qemu/osdep.h"
100
#include "io/channel.h"
101
#include "qapi/error.h"
102
-#include "qemu/coroutine.h"
103
+#include "qemu/main-loop.h"
104
105
bool qio_channel_has_feature(QIOChannel *ioc,
106
QIOChannelFeature feature)
107
@@ -XXX,XX +XXX,XX @@ off_t qio_channel_io_seek(QIOChannel *ioc,
108
}
109
110
111
-typedef struct QIOChannelYieldData QIOChannelYieldData;
112
-struct QIOChannelYieldData {
113
- QIOChannel *ioc;
114
- Coroutine *co;
115
-};
116
+static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc);
117
118
+static void qio_channel_restart_read(void *opaque)
119
+{
120
+ QIOChannel *ioc = opaque;
121
+ Coroutine *co = ioc->read_coroutine;
122
+
123
+ ioc->read_coroutine = NULL;
124
+ qio_channel_set_aio_fd_handlers(ioc);
125
+ aio_co_wake(co);
126
+}
127
128
-static gboolean qio_channel_yield_enter(QIOChannel *ioc,
129
- GIOCondition condition,
130
- gpointer opaque)
131
+static void qio_channel_restart_write(void *opaque)
132
{
133
- QIOChannelYieldData *data = opaque;
134
- qemu_coroutine_enter(data->co);
135
- return FALSE;
136
+ QIOChannel *ioc = opaque;
137
+ Coroutine *co = ioc->write_coroutine;
138
+
139
+ ioc->write_coroutine = NULL;
140
+ qio_channel_set_aio_fd_handlers(ioc);
141
+ aio_co_wake(co);
142
}
143
144
+static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc)
145
+{
146
+ IOHandler *rd_handler = NULL, *wr_handler = NULL;
147
+ AioContext *ctx;
148
+
149
+ if (ioc->read_coroutine) {
150
+ rd_handler = qio_channel_restart_read;
151
+ }
152
+ if (ioc->write_coroutine) {
153
+ wr_handler = qio_channel_restart_write;
154
+ }
155
+
156
+ ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context();
157
+ qio_channel_set_aio_fd_handler(ioc, ctx, rd_handler, wr_handler, ioc);
158
+}
159
+
160
+void qio_channel_attach_aio_context(QIOChannel *ioc,
161
+ AioContext *ctx)
162
+{
163
+ AioContext *old_ctx;
164
+ if (ioc->ctx == ctx) {
165
+ return;
166
+ }
167
+
168
+ old_ctx = ioc->ctx ? ioc->ctx : iohandler_get_aio_context();
169
+ qio_channel_set_aio_fd_handler(ioc, old_ctx, NULL, NULL, NULL);
170
+ ioc->ctx = ctx;
171
+ qio_channel_set_aio_fd_handlers(ioc);
172
+}
173
+
174
+void qio_channel_detach_aio_context(QIOChannel *ioc)
175
+{
176
+ ioc->read_coroutine = NULL;
177
+ ioc->write_coroutine = NULL;
178
+ qio_channel_set_aio_fd_handlers(ioc);
179
+ ioc->ctx = NULL;
180
+}
181
182
void coroutine_fn qio_channel_yield(QIOChannel *ioc,
183
GIOCondition condition)
184
{
185
- QIOChannelYieldData data;
186
-
187
assert(qemu_in_coroutine());
188
- data.ioc = ioc;
189
- data.co = qemu_coroutine_self();
190
- qio_channel_add_watch(ioc,
191
- condition,
192
- qio_channel_yield_enter,
193
- &data,
194
- NULL);
195
+ if (condition == G_IO_IN) {
196
+ assert(!ioc->read_coroutine);
197
+ ioc->read_coroutine = qemu_coroutine_self();
198
+ } else if (condition == G_IO_OUT) {
199
+ assert(!ioc->write_coroutine);
200
+ ioc->write_coroutine = qemu_coroutine_self();
201
+ } else {
202
+ abort();
203
+ }
204
+ qio_channel_set_aio_fd_handlers(ioc);
205
qemu_coroutine_yield();
206
}
87
207
88
--
208
--
89
2.26.2
209
2.9.3
90
210
91
211
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
79 is the PEP8 recommendation. This recommendation works well for
3
In the client, read the reply headers from a coroutine, switching the
4
reading patch diffs in TUI email clients.
4
read side between the "read header" coroutine and the I/O coroutine that
5
reads the body of the reply.
5
6
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
In the server, if the server can read more requests it will create a new
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
"read request" coroutine as soon as a request has been read. Otherwise,
8
Message-Id: <20200331000014.11581-10-jsnow@redhat.com>
9
the new coroutine is created in nbd_request_put.
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
14
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
15
Message-id: 20170213135235.12274-8-pbonzini@redhat.com
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
17
---
12
tests/qemu-iotests/iotests.py | 64 +++++++++++++++++++++++------------
18
block/nbd-client.h | 2 +-
13
tests/qemu-iotests/pylintrc | 6 +++-
19
block/nbd-client.c | 117 ++++++++++++++++++++++++-----------------------------
14
2 files changed, 47 insertions(+), 23 deletions(-)
20
nbd/client.c | 2 +-
21
nbd/common.c | 9 +----
22
nbd/server.c | 94 +++++++++++++-----------------------------
23
5 files changed, 83 insertions(+), 141 deletions(-)
15
24
16
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
25
diff --git a/block/nbd-client.h b/block/nbd-client.h
17
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/iotests.py
27
--- a/block/nbd-client.h
19
+++ b/tests/qemu-iotests/iotests.py
28
+++ b/block/nbd-client.h
20
@@ -XXX,XX +XXX,XX @@ luks_default_key_secret_opt = 'key-secret=keysec0'
29
@@ -XXX,XX +XXX,XX @@ typedef struct NBDClientSession {
21
def qemu_img(*args):
30
22
'''Run qemu-img and return the exit code'''
31
CoMutex send_mutex;
23
devnull = open('/dev/null', 'r+')
32
CoQueue free_sema;
24
- exitcode = subprocess.call(qemu_img_args + list(args), stdin=devnull, stdout=devnull)
33
- Coroutine *send_coroutine;
25
+ exitcode = subprocess.call(qemu_img_args + list(args),
34
+ Coroutine *read_reply_co;
26
+ stdin=devnull, stdout=devnull)
35
int in_flight;
27
if exitcode < 0:
36
28
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
37
Coroutine *recv_coroutine[MAX_NBD_REQUESTS];
29
+ sys.stderr.write('qemu-img received signal %i: %s\n'
38
diff --git a/block/nbd-client.c b/block/nbd-client.c
30
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
39
index XXXXXXX..XXXXXXX 100644
31
return exitcode
40
--- a/block/nbd-client.c
32
41
+++ b/block/nbd-client.c
33
def ordered_qmp(qmsg, conv_keys=True):
42
@@ -XXX,XX +XXX,XX @@
34
@@ -XXX,XX +XXX,XX @@ def qemu_img_verbose(*args):
43
#define HANDLE_TO_INDEX(bs, handle) ((handle) ^ ((uint64_t)(intptr_t)bs))
35
'''Run qemu-img without suppressing its output and return the exit code'''
44
#define INDEX_TO_HANDLE(bs, index) ((index) ^ ((uint64_t)(intptr_t)bs))
36
exitcode = subprocess.call(qemu_img_args + list(args))
45
37
if exitcode < 0:
46
-static void nbd_recv_coroutines_enter_all(NBDClientSession *s)
38
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
47
+static void nbd_recv_coroutines_enter_all(BlockDriverState *bs)
39
+ sys.stderr.write('qemu-img received signal %i: %s\n'
48
{
40
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
49
+ NBDClientSession *s = nbd_get_client_session(bs);
41
return exitcode
50
int i;
42
51
43
def qemu_img_pipe(*args):
52
for (i = 0; i < MAX_NBD_REQUESTS; i++) {
44
@@ -XXX,XX +XXX,XX @@ def qemu_img_pipe(*args):
53
@@ -XXX,XX +XXX,XX @@ static void nbd_recv_coroutines_enter_all(NBDClientSession *s)
45
universal_newlines=True)
54
qemu_coroutine_enter(s->recv_coroutine[i]);
46
exitcode = subp.wait()
55
}
47
if exitcode < 0:
56
}
48
- sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
57
+ BDRV_POLL_WHILE(bs, s->read_reply_co);
49
+ sys.stderr.write('qemu-img received signal %i: %s\n'
58
}
50
+ % (-exitcode, ' '.join(qemu_img_args + list(args))))
59
51
return subp.communicate()[0]
60
static void nbd_teardown_connection(BlockDriverState *bs)
52
61
@@ -XXX,XX +XXX,XX @@ static void nbd_teardown_connection(BlockDriverState *bs)
53
def qemu_img_log(*args):
62
qio_channel_shutdown(client->ioc,
54
@@ -XXX,XX +XXX,XX @@ def qemu_io(*args):
63
QIO_CHANNEL_SHUTDOWN_BOTH,
55
universal_newlines=True)
64
NULL);
56
exitcode = subp.wait()
65
- nbd_recv_coroutines_enter_all(client);
57
if exitcode < 0:
66
+ nbd_recv_coroutines_enter_all(bs);
58
- sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
67
59
+ sys.stderr.write('qemu-io received signal %i: %s\n'
68
nbd_client_detach_aio_context(bs);
60
+ % (-exitcode, ' '.join(args)))
69
object_unref(OBJECT(client->sioc));
61
return subp.communicate()[0]
70
@@ -XXX,XX +XXX,XX @@ static void nbd_teardown_connection(BlockDriverState *bs)
62
71
client->ioc = NULL;
63
def qemu_io_log(*args):
72
}
64
@@ -XXX,XX +XXX,XX @@ win32_re = re.compile(r"\r")
73
65
def filter_win32(msg):
74
-static void nbd_reply_ready(void *opaque)
66
return win32_re.sub("", msg)
75
+static coroutine_fn void nbd_read_reply_entry(void *opaque)
67
76
{
68
-qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)")
77
- BlockDriverState *bs = opaque;
69
+qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* "
78
- NBDClientSession *s = nbd_get_client_session(bs);
70
+ r"\([0-9\/.inf]* [EPTGMKiBbytes]*\/sec "
79
+ NBDClientSession *s = opaque;
71
+ r"and [0-9\/.inf]* ops\/sec\)")
80
uint64_t i;
72
def filter_qemu_io(msg):
81
int ret;
73
msg = filter_win32(msg)
82
74
- return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg)
83
- if (!s->ioc) { /* Already closed */
75
+ return qemu_io_re.sub("X ops; XX:XX:XX.X "
84
- return;
76
+ "(XXX YYY/sec and XXX ops/sec)", msg)
85
- }
77
86
-
78
chown_re = re.compile(r"chown [0-9]+:[0-9]+")
87
- if (s->reply.handle == 0) {
79
def filter_chown(msg):
88
- /* No reply already in flight. Fetch a header. It is possible
80
@@ -XXX,XX +XXX,XX @@ def filter_img_info(output, filename):
89
- * that another thread has done the same thing in parallel, so
81
line = filter_testfiles(line)
90
- * the socket is not readable anymore.
82
line = line.replace(imgfmt, 'IMGFMT')
91
- */
83
line = re.sub('iters: [0-9]+', 'iters: XXX', line)
92
+ for (;;) {
84
- line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line)
93
+ assert(s->reply.handle == 0);
85
+ line = re.sub('uuid: [-a-f0-9]+',
94
ret = nbd_receive_reply(s->ioc, &s->reply);
86
+ 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX',
95
- if (ret == -EAGAIN) {
87
+ line)
96
- return;
88
line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line)
97
- }
89
lines.append(line)
98
if (ret < 0) {
90
return '\n'.join(lines)
99
- s->reply.handle = 0;
91
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
100
- goto fail;
92
self.pause_drive(drive, "write_aio")
101
+ break;
93
return
102
}
94
self.qmp('human-monitor-command',
103
- }
95
- command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
104
96
+ command_line='qemu-io %s "break %s bp_%s"'
105
- /* There's no need for a mutex on the receive side, because the
97
+ % (drive, event, drive))
106
- * handler acts as a synchronization point and ensures that only
98
107
- * one coroutine is called until the reply finishes. */
99
def resume_drive(self, drive):
108
- i = HANDLE_TO_INDEX(s, s->reply.handle);
100
self.qmp('human-monitor-command',
109
- if (i >= MAX_NBD_REQUESTS) {
101
- command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
110
- goto fail;
102
+ command_line='qemu-io %s "remove_break bp_%s"'
111
- }
103
+ % (drive, drive))
112
+ /* There's no need for a mutex on the receive side, because the
104
113
+ * handler acts as a synchronization point and ensures that only
105
def hmp_qemu_io(self, drive, cmd):
114
+ * one coroutine is called until the reply finishes.
106
'''Write to a given drive using an HMP command'''
115
+ */
107
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
116
+ i = HANDLE_TO_INDEX(s, s->reply.handle);
108
idx = int(idx)
117
+ if (i >= MAX_NBD_REQUESTS || !s->recv_coroutine[i]) {
109
118
+ break;
110
if not isinstance(d, dict) or component not in d:
119
+ }
111
- self.fail('failed path traversal for "%s" in "%s"' % (path, str(d)))
120
112
+ self.fail(f'failed path traversal for "{path}" in "{d}"')
121
- if (s->recv_coroutine[i]) {
113
d = d[component]
122
- qemu_coroutine_enter(s->recv_coroutine[i]);
114
123
- return;
115
if m:
124
+ /* We're woken up by the recv_coroutine itself. Note that there
116
if not isinstance(d, list):
125
+ * is no race between yielding and reentering read_reply_co. This
117
- self.fail('path component "%s" in "%s" is not a list in "%s"' % (component, path, str(d)))
126
+ * is because:
118
+ self.fail(f'path component "{component}" in "{path}" '
127
+ *
119
+ f'is not a list in "{d}"')
128
+ * - if recv_coroutine[i] runs on the same AioContext, it is only
120
try:
129
+ * entered after we yield
121
d = d[idx]
130
+ *
122
except IndexError:
131
+ * - if recv_coroutine[i] runs on a different AioContext, reentering
123
- self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
132
+ * read_reply_co happens through a bottom half, which can only
124
+ self.fail(f'invalid index "{idx}" in path "{path}" '
133
+ * run after we yield.
125
+ f'in "{d}"')
134
+ */
126
return d
135
+ aio_co_wake(s->recv_coroutine[i]);
127
136
+ qemu_coroutine_yield();
128
def assert_qmp_absent(self, d, path):
137
}
129
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
138
-
130
'''Asserts that the given filename is a json: filename and that its
139
-fail:
131
content is equal to the given reference object'''
140
- nbd_teardown_connection(bs);
132
self.assertEqual(json_filename[:5], 'json:')
141
-}
133
- self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
142
-
134
- self.vm.flatten_qmp_object(reference))
143
-static void nbd_restart_write(void *opaque)
135
+ self.assertEqual(
144
-{
136
+ self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
145
- BlockDriverState *bs = opaque;
137
+ self.vm.flatten_qmp_object(reference)
146
-
138
+ )
147
- qemu_coroutine_enter(nbd_get_client_session(bs)->send_coroutine);
139
148
+ s->read_reply_co = NULL;
140
- def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0):
149
}
141
+ def cancel_and_wait(self, drive='drive0', force=False,
150
142
+ resume=False, wait=60.0):
151
static int nbd_co_send_request(BlockDriverState *bs,
143
'''Cancel a block job and wait for it to finish, returning the event'''
152
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_request(BlockDriverState *bs,
144
result = self.vm.qmp('block-job-cancel', device=drive, force=force)
153
QEMUIOVector *qiov)
145
self.assert_qmp(result, 'return', {})
154
{
146
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
155
NBDClientSession *s = nbd_get_client_session(bs);
147
self.assert_no_active_block_jobs()
156
- AioContext *aio_context;
148
return result
157
int rc, ret, i;
149
158
150
- def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0,
159
qemu_co_mutex_lock(&s->send_mutex);
151
- error=None):
160
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_request(BlockDriverState *bs,
152
+ def wait_until_completed(self, drive='drive0', check_offset=True,
161
return -EPIPE;
153
+ wait=60.0, error=None):
162
}
154
'''Wait for a block job to finish, returning the event'''
163
155
while True:
164
- s->send_coroutine = qemu_coroutine_self();
156
for event in self.vm.get_qmp_events(wait=wait):
165
- aio_context = bdrv_get_aio_context(bs);
157
@@ -XXX,XX +XXX,XX @@ def verify_quorum():
166
-
158
notrun('quorum support missing')
167
- aio_set_fd_handler(aio_context, s->sioc->fd, false,
159
168
- nbd_reply_ready, nbd_restart_write, NULL, bs);
160
def qemu_pipe(*args):
169
if (qiov) {
161
- '''Run qemu with an option to print something and exit (e.g. a help option),
170
qio_channel_set_cork(s->ioc, true);
162
- and return its output'''
171
rc = nbd_send_request(s->ioc, request);
163
+ """
172
@@ -XXX,XX +XXX,XX @@ static int nbd_co_send_request(BlockDriverState *bs,
164
+ Run qemu with an option to print something and exit (e.g. a help option).
173
} else {
174
rc = nbd_send_request(s->ioc, request);
175
}
176
- aio_set_fd_handler(aio_context, s->sioc->fd, false,
177
- nbd_reply_ready, NULL, NULL, bs);
178
- s->send_coroutine = NULL;
179
qemu_co_mutex_unlock(&s->send_mutex);
180
return rc;
181
}
182
@@ -XXX,XX +XXX,XX @@ static void nbd_co_receive_reply(NBDClientSession *s,
183
{
184
int ret;
185
186
- /* Wait until we're woken up by the read handler. TODO: perhaps
187
- * peek at the next reply and avoid yielding if it's ours? */
188
+ /* Wait until we're woken up by nbd_read_reply_entry. */
189
qemu_coroutine_yield();
190
*reply = s->reply;
191
if (reply->handle != request->handle ||
192
@@ -XXX,XX +XXX,XX @@ static void nbd_coroutine_start(NBDClientSession *s,
193
/* s->recv_coroutine[i] is set as soon as we get the send_lock. */
194
}
195
196
-static void nbd_coroutine_end(NBDClientSession *s,
197
+static void nbd_coroutine_end(BlockDriverState *bs,
198
NBDRequest *request)
199
{
200
+ NBDClientSession *s = nbd_get_client_session(bs);
201
int i = HANDLE_TO_INDEX(s, request->handle);
165
+
202
+
166
+ :return: QEMU's stdout output.
203
s->recv_coroutine[i] = NULL;
167
+ """
204
- if (s->in_flight-- == MAX_NBD_REQUESTS) {
168
args = [qemu_prog] + qemu_opts + list(args)
205
- qemu_co_queue_next(&s->free_sema);
169
subp = subprocess.Popen(args, stdout=subprocess.PIPE,
206
+ s->in_flight--;
170
stderr=subprocess.STDOUT,
207
+ qemu_co_queue_next(&s->free_sema);
171
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=(), read_only=False):
208
+
172
209
+ /* Kick the read_reply_co to get the next reply. */
173
usf_list = list(set(fmts) - set(supported_formats(read_only)))
210
+ if (s->read_reply_co) {
174
if usf_list:
211
+ aio_co_wake(s->read_reply_co);
175
- test_case.case_skip('{}: formats {} are not whitelisted'.format(
212
}
176
- test_case, usf_list))
213
}
177
+ msg = f'{test_case}: formats {usf_list} are not whitelisted'
214
178
+ test_case.case_skip(msg)
215
@@ -XXX,XX +XXX,XX @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset,
179
return None
216
} else {
180
else:
217
nbd_co_receive_reply(client, &request, &reply, qiov);
181
return func(test_case, *args, **kwargs)
218
}
182
diff --git a/tests/qemu-iotests/pylintrc b/tests/qemu-iotests/pylintrc
219
- nbd_coroutine_end(client, &request);
220
+ nbd_coroutine_end(bs, &request);
221
return -reply.error;
222
}
223
224
@@ -XXX,XX +XXX,XX @@ int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset,
225
} else {
226
nbd_co_receive_reply(client, &request, &reply, NULL);
227
}
228
- nbd_coroutine_end(client, &request);
229
+ nbd_coroutine_end(bs, &request);
230
return -reply.error;
231
}
232
233
@@ -XXX,XX +XXX,XX @@ int nbd_client_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
234
} else {
235
nbd_co_receive_reply(client, &request, &reply, NULL);
236
}
237
- nbd_coroutine_end(client, &request);
238
+ nbd_coroutine_end(bs, &request);
239
return -reply.error;
240
}
241
242
@@ -XXX,XX +XXX,XX @@ int nbd_client_co_flush(BlockDriverState *bs)
243
} else {
244
nbd_co_receive_reply(client, &request, &reply, NULL);
245
}
246
- nbd_coroutine_end(client, &request);
247
+ nbd_coroutine_end(bs, &request);
248
return -reply.error;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ int nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int count)
252
} else {
253
nbd_co_receive_reply(client, &request, &reply, NULL);
254
}
255
- nbd_coroutine_end(client, &request);
256
+ nbd_coroutine_end(bs, &request);
257
return -reply.error;
258
259
}
260
261
void nbd_client_detach_aio_context(BlockDriverState *bs)
262
{
263
- aio_set_fd_handler(bdrv_get_aio_context(bs),
264
- nbd_get_client_session(bs)->sioc->fd,
265
- false, NULL, NULL, NULL, NULL);
266
+ NBDClientSession *client = nbd_get_client_session(bs);
267
+ qio_channel_detach_aio_context(QIO_CHANNEL(client->sioc));
268
}
269
270
void nbd_client_attach_aio_context(BlockDriverState *bs,
271
AioContext *new_context)
272
{
273
- aio_set_fd_handler(new_context, nbd_get_client_session(bs)->sioc->fd,
274
- false, nbd_reply_ready, NULL, NULL, bs);
275
+ NBDClientSession *client = nbd_get_client_session(bs);
276
+ qio_channel_attach_aio_context(QIO_CHANNEL(client->sioc), new_context);
277
+ aio_co_schedule(new_context, client->read_reply_co);
278
}
279
280
void nbd_client_close(BlockDriverState *bs)
281
@@ -XXX,XX +XXX,XX @@ int nbd_client_init(BlockDriverState *bs,
282
/* Now that we're connected, set the socket to be non-blocking and
283
* kick the reply mechanism. */
284
qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL);
285
-
286
+ client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client);
287
nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs));
288
289
logout("Established connection with NBD server\n");
290
diff --git a/nbd/client.c b/nbd/client.c
183
index XXXXXXX..XXXXXXX 100644
291
index XXXXXXX..XXXXXXX 100644
184
--- a/tests/qemu-iotests/pylintrc
292
--- a/nbd/client.c
185
+++ b/tests/qemu-iotests/pylintrc
293
+++ b/nbd/client.c
186
@@ -XXX,XX +XXX,XX @@ disable=invalid-name,
294
@@ -XXX,XX +XXX,XX @@ ssize_t nbd_receive_reply(QIOChannel *ioc, NBDReply *reply)
187
too-many-locals,
295
ssize_t ret;
188
too-many-public-methods,
296
189
# These are temporary, and should be removed:
297
ret = read_sync(ioc, buf, sizeof(buf));
190
- line-too-long,
298
- if (ret < 0) {
191
missing-docstring,
299
+ if (ret <= 0) {
300
return ret;
301
}
302
303
diff --git a/nbd/common.c b/nbd/common.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/nbd/common.c
306
+++ b/nbd/common.c
307
@@ -XXX,XX +XXX,XX @@ ssize_t nbd_wr_syncv(QIOChannel *ioc,
308
}
309
if (len == QIO_CHANNEL_ERR_BLOCK) {
310
if (qemu_in_coroutine()) {
311
- /* XXX figure out if we can create a variant on
312
- * qio_channel_yield() that works with AIO contexts
313
- * and consider using that in this branch */
314
- qemu_coroutine_yield();
315
- } else if (done) {
316
- /* XXX this is needed by nbd_reply_ready. */
317
- qio_channel_wait(ioc,
318
- do_read ? G_IO_IN : G_IO_OUT);
319
+ qio_channel_yield(ioc, do_read ? G_IO_IN : G_IO_OUT);
320
} else {
321
return -EAGAIN;
322
}
323
diff --git a/nbd/server.c b/nbd/server.c
324
index XXXXXXX..XXXXXXX 100644
325
--- a/nbd/server.c
326
+++ b/nbd/server.c
327
@@ -XXX,XX +XXX,XX @@ struct NBDClient {
328
CoMutex send_lock;
329
Coroutine *send_coroutine;
330
331
- bool can_read;
332
-
333
QTAILQ_ENTRY(NBDClient) next;
334
int nb_requests;
335
bool closing;
336
@@ -XXX,XX +XXX,XX @@ struct NBDClient {
337
338
/* That's all folks */
339
340
-static void nbd_set_handlers(NBDClient *client);
341
-static void nbd_unset_handlers(NBDClient *client);
342
-static void nbd_update_can_read(NBDClient *client);
343
+static void nbd_client_receive_next_request(NBDClient *client);
344
345
static gboolean nbd_negotiate_continue(QIOChannel *ioc,
346
GIOCondition condition,
347
@@ -XXX,XX +XXX,XX @@ void nbd_client_put(NBDClient *client)
348
*/
349
assert(client->closing);
350
351
- nbd_unset_handlers(client);
352
+ qio_channel_detach_aio_context(client->ioc);
353
object_unref(OBJECT(client->sioc));
354
object_unref(OBJECT(client->ioc));
355
if (client->tlscreds) {
356
@@ -XXX,XX +XXX,XX @@ static NBDRequestData *nbd_request_get(NBDClient *client)
357
358
assert(client->nb_requests <= MAX_NBD_REQUESTS - 1);
359
client->nb_requests++;
360
- nbd_update_can_read(client);
361
362
req = g_new0(NBDRequestData, 1);
363
nbd_client_get(client);
364
@@ -XXX,XX +XXX,XX @@ static void nbd_request_put(NBDRequestData *req)
365
g_free(req);
366
367
client->nb_requests--;
368
- nbd_update_can_read(client);
369
+ nbd_client_receive_next_request(client);
192
+
370
+
193
+[FORMAT]
371
nbd_client_put(client);
372
}
373
374
@@ -XXX,XX +XXX,XX @@ static void blk_aio_attached(AioContext *ctx, void *opaque)
375
exp->ctx = ctx;
376
377
QTAILQ_FOREACH(client, &exp->clients, next) {
378
- nbd_set_handlers(client);
379
+ qio_channel_attach_aio_context(client->ioc, ctx);
380
+ if (client->recv_coroutine) {
381
+ aio_co_schedule(ctx, client->recv_coroutine);
382
+ }
383
+ if (client->send_coroutine) {
384
+ aio_co_schedule(ctx, client->send_coroutine);
385
+ }
386
}
387
}
388
389
@@ -XXX,XX +XXX,XX @@ static void blk_aio_detach(void *opaque)
390
TRACE("Export %s: Detaching clients from AIO context %p\n", exp->name, exp->ctx);
391
392
QTAILQ_FOREACH(client, &exp->clients, next) {
393
- nbd_unset_handlers(client);
394
+ qio_channel_detach_aio_context(client->ioc);
395
}
396
397
exp->ctx = NULL;
398
@@ -XXX,XX +XXX,XX @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
399
g_assert(qemu_in_coroutine());
400
qemu_co_mutex_lock(&client->send_lock);
401
client->send_coroutine = qemu_coroutine_self();
402
- nbd_set_handlers(client);
403
404
if (!len) {
405
rc = nbd_send_reply(client->ioc, reply);
406
@@ -XXX,XX +XXX,XX @@ static ssize_t nbd_co_send_reply(NBDRequestData *req, NBDReply *reply,
407
}
408
409
client->send_coroutine = NULL;
410
- nbd_set_handlers(client);
411
qemu_co_mutex_unlock(&client->send_lock);
412
return rc;
413
}
414
@@ -XXX,XX +XXX,XX @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
415
ssize_t rc;
416
417
g_assert(qemu_in_coroutine());
418
- client->recv_coroutine = qemu_coroutine_self();
419
- nbd_update_can_read(client);
420
-
421
+ assert(client->recv_coroutine == qemu_coroutine_self());
422
rc = nbd_receive_request(client->ioc, request);
423
if (rc < 0) {
424
if (rc != -EAGAIN) {
425
@@ -XXX,XX +XXX,XX @@ static ssize_t nbd_co_receive_request(NBDRequestData *req,
426
427
out:
428
client->recv_coroutine = NULL;
429
- nbd_update_can_read(client);
430
+ nbd_client_receive_next_request(client);
431
432
return rc;
433
}
434
435
-static void nbd_trip(void *opaque)
436
+/* Owns a reference to the NBDClient passed as opaque. */
437
+static coroutine_fn void nbd_trip(void *opaque)
438
{
439
NBDClient *client = opaque;
440
NBDExport *exp = client->exp;
441
NBDRequestData *req;
442
- NBDRequest request;
443
+ NBDRequest request = { 0 }; /* GCC thinks it can be used uninitialized */
444
NBDReply reply;
445
ssize_t ret;
446
int flags;
447
448
TRACE("Reading request.");
449
if (client->closing) {
450
+ nbd_client_put(client);
451
return;
452
}
453
454
@@ -XXX,XX +XXX,XX @@ static void nbd_trip(void *opaque)
455
456
done:
457
nbd_request_put(req);
458
+ nbd_client_put(client);
459
return;
460
461
out:
462
nbd_request_put(req);
463
client_close(client);
464
+ nbd_client_put(client);
465
}
466
467
-static void nbd_read(void *opaque)
468
+static void nbd_client_receive_next_request(NBDClient *client)
469
{
470
- NBDClient *client = opaque;
471
-
472
- if (client->recv_coroutine) {
473
- qemu_coroutine_enter(client->recv_coroutine);
474
- } else {
475
- qemu_coroutine_enter(qemu_coroutine_create(nbd_trip, client));
476
- }
477
-}
478
-
479
-static void nbd_restart_write(void *opaque)
480
-{
481
- NBDClient *client = opaque;
482
-
483
- qemu_coroutine_enter(client->send_coroutine);
484
-}
485
-
486
-static void nbd_set_handlers(NBDClient *client)
487
-{
488
- if (client->exp && client->exp->ctx) {
489
- aio_set_fd_handler(client->exp->ctx, client->sioc->fd, true,
490
- client->can_read ? nbd_read : NULL,
491
- client->send_coroutine ? nbd_restart_write : NULL,
492
- NULL, client);
493
- }
494
-}
495
-
496
-static void nbd_unset_handlers(NBDClient *client)
497
-{
498
- if (client->exp && client->exp->ctx) {
499
- aio_set_fd_handler(client->exp->ctx, client->sioc->fd, true, NULL,
500
- NULL, NULL, NULL);
501
- }
502
-}
503
-
504
-static void nbd_update_can_read(NBDClient *client)
505
-{
506
- bool can_read = client->recv_coroutine ||
507
- client->nb_requests < MAX_NBD_REQUESTS;
508
-
509
- if (can_read != client->can_read) {
510
- client->can_read = can_read;
511
- nbd_set_handlers(client);
512
-
513
- /* There is no need to invoke aio_notify(), since aio_set_fd_handler()
514
- * in nbd_set_handlers() will have taken care of that */
515
+ if (!client->recv_coroutine && client->nb_requests < MAX_NBD_REQUESTS) {
516
+ nbd_client_get(client);
517
+ client->recv_coroutine = qemu_coroutine_create(nbd_trip, client);
518
+ aio_co_schedule(client->exp->ctx, client->recv_coroutine);
519
}
520
}
521
522
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void nbd_co_client_start(void *opaque)
523
goto out;
524
}
525
qemu_co_mutex_init(&client->send_lock);
526
- nbd_set_handlers(client);
527
528
if (exp) {
529
QTAILQ_INSERT_TAIL(&exp->clients, client, next);
530
}
194
+
531
+
195
+# Maximum number of characters on a single line.
532
+ nbd_client_receive_next_request(client);
196
+max-line-length=79
533
+
534
out:
535
g_free(data);
536
}
537
@@ -XXX,XX +XXX,XX @@ void nbd_client_new(NBDExport *exp,
538
object_ref(OBJECT(client->sioc));
539
client->ioc = QIO_CHANNEL(sioc);
540
object_ref(OBJECT(client->ioc));
541
- client->can_read = true;
542
client->close = close_fn;
543
544
data->client = client;
197
--
545
--
198
2.26.2
546
2.9.3
199
547
200
548
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Representing nested, recursive data structures in mypy is notoriously
3
As a small step towards the introduction of multiqueue, we want
4
difficult; the best we can reliably do right now is denote the leaf
4
coroutines to remain on the same AioContext that started them,
5
types as "Any" while describing the general shape of the data.
5
unless they are moved explicitly with e.g. aio_co_schedule. This patch
6
avoids that coroutines switch AioContext when they use a CoMutex.
7
For now it does not make much of a difference, because the CoMutex
8
is not thread-safe and the AioContext itself is used to protect the
9
CoMutex from concurrent access. However, this is going to change.
6
10
7
Regardless, this fully annotates the log() function.
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
14
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
15
Message-id: 20170213135235.12274-9-pbonzini@redhat.com
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
17
---
18
util/qemu-coroutine-lock.c | 5 ++---
19
util/trace-events | 1 -
20
2 files changed, 2 insertions(+), 4 deletions(-)
8
21
9
Typing notes:
22
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
10
11
TypeVar is a Type variable that can optionally be constrained by a
12
sequence of possible types. This variable is bound to a specific type
13
per-invocation, like a Generic.
14
15
log() behaves as log<Msg>() now, where the incoming type informs the
16
signature it expects for any filter arguments passed in. If Msg is a
17
str, then filter should take and return a str.
18
19
Signed-off-by: John Snow <jsnow@redhat.com>
20
Reviewed-by: Max Reitz <mreitz@redhat.com>
21
Message-Id: <20200331000014.11581-9-jsnow@redhat.com>
22
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Max Reitz <mreitz@redhat.com>
24
---
25
tests/qemu-iotests/iotests.py | 14 +++++++++++---
26
1 file changed, 11 insertions(+), 3 deletions(-)
27
28
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
29
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
30
--- a/tests/qemu-iotests/iotests.py
24
--- a/util/qemu-coroutine-lock.c
31
+++ b/tests/qemu-iotests/iotests.py
25
+++ b/util/qemu-coroutine-lock.c
32
@@ -XXX,XX +XXX,XX @@ import signal
26
@@ -XXX,XX +XXX,XX @@
33
import struct
27
#include "qemu/coroutine.h"
34
import subprocess
28
#include "qemu/coroutine_int.h"
35
import sys
29
#include "qemu/queue.h"
36
+from typing import (Any, Callable, Dict, Iterable, List, Optional, TypeVar)
30
+#include "block/aio.h"
37
import unittest
31
#include "trace.h"
38
32
39
# pylint: disable=import-error, wrong-import-position
33
void qemu_co_queue_init(CoQueue *queue)
40
@@ -XXX,XX +XXX,XX @@ def filter_qmp_imgfmt(qmsg):
34
@@ -XXX,XX +XXX,XX @@ void qemu_co_queue_run_restart(Coroutine *co)
41
return value
35
42
return filter_qmp(qmsg, _filter)
36
static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
43
37
{
44
-def log(msg, filters=(), indent=None):
38
- Coroutine *self = qemu_coroutine_self();
45
- '''Logs either a string message or a JSON serializable message (like QMP).
39
Coroutine *next;
46
- If indent is provided, JSON serializable messages are pretty-printed.'''
40
47
+
41
if (QSIMPLEQ_EMPTY(&queue->entries)) {
48
+Msg = TypeVar('Msg', Dict[str, Any], List[Any], str)
42
@@ -XXX,XX +XXX,XX @@ static bool qemu_co_queue_do_restart(CoQueue *queue, bool single)
49
+
43
50
+def log(msg: Msg,
44
while ((next = QSIMPLEQ_FIRST(&queue->entries)) != NULL) {
51
+ filters: Iterable[Callable[[Msg], Msg]] = (),
45
QSIMPLEQ_REMOVE_HEAD(&queue->entries, co_queue_next);
52
+ indent: Optional[int] = None) -> None:
46
- QSIMPLEQ_INSERT_TAIL(&self->co_queue_wakeup, next, co_queue_next);
53
+ """
47
- trace_qemu_co_queue_next(next);
54
+ Logs either a string message or a JSON serializable message (like QMP).
48
+ aio_co_wake(next);
55
+ If indent is provided, JSON serializable messages are pretty-printed.
49
if (single) {
56
+ """
50
break;
57
for flt in filters:
51
}
58
msg = flt(msg)
52
diff --git a/util/trace-events b/util/trace-events
59
if isinstance(msg, (dict, list)):
53
index XXXXXXX..XXXXXXX 100644
54
--- a/util/trace-events
55
+++ b/util/trace-events
56
@@ -XXX,XX +XXX,XX @@ qemu_coroutine_terminate(void *co) "self %p"
57
58
# util/qemu-coroutine-lock.c
59
qemu_co_queue_run_restart(void *co) "co %p"
60
-qemu_co_queue_next(void *nxt) "next %p"
61
qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p"
62
qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p"
63
qemu_co_mutex_unlock_entry(void *mutex, void *self) "mutex %p self %p"
60
--
64
--
61
2.26.2
65
2.9.3
62
66
63
67
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Run block_copy iterations in parallel in aio tasks.
3
Keep the coroutine on the same AioContext. Without this change,
4
there would be a race between yielding the coroutine and reentering it.
5
While the race cannot happen now, because the code only runs from a single
6
AioContext, this will change with multiqueue support in the block layer.
4
7
5
Changes:
8
While doing the change, replace custom bottom half with aio_co_schedule.
6
- BlockCopyTask becomes aio task structure. Add zeroes field to pass
7
it to block_copy_do_copy
8
- add call state - it's a state of one call of block_copy(), shared
9
between parallel tasks. For now used only to keep information about
10
first error: is it read or not.
11
- convert block_copy_dirty_clusters to aio-task loop.
12
9
13
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
14
Message-Id: <20200429130847.28124-6-vsementsov@virtuozzo.com>
11
Reviewed-by: Fam Zheng <famz@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
14
Message-id: 20170213135235.12274-10-pbonzini@redhat.com
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
16
---
17
block/block-copy.c | 119 ++++++++++++++++++++++++++++++++++++++++-----
17
block/blkdebug.c | 9 +--------
18
1 file changed, 106 insertions(+), 13 deletions(-)
18
1 file changed, 1 insertion(+), 8 deletions(-)
19
19
20
diff --git a/block/block-copy.c b/block/block-copy.c
20
diff --git a/block/blkdebug.c b/block/blkdebug.c
21
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
22
--- a/block/block-copy.c
22
--- a/block/blkdebug.c
23
+++ b/block/block-copy.c
23
+++ b/block/blkdebug.c
24
@@ -XXX,XX +XXX,XX @@
25
#include "block/block-copy.h"
26
#include "sysemu/block-backend.h"
27
#include "qemu/units.h"
28
+#include "qemu/coroutine.h"
29
+#include "block/aio_task.h"
30
31
#define BLOCK_COPY_MAX_COPY_RANGE (16 * MiB)
32
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
33
#define BLOCK_COPY_MAX_MEM (128 * MiB)
34
+#define BLOCK_COPY_MAX_WORKERS 64
35
+
36
+static coroutine_fn int block_copy_task_entry(AioTask *task);
37
+
38
+typedef struct BlockCopyCallState {
39
+ bool failed;
40
+ bool error_is_read;
41
+} BlockCopyCallState;
42
43
typedef struct BlockCopyTask {
44
+ AioTask task;
45
+
46
BlockCopyState *s;
47
+ BlockCopyCallState *call_state;
48
int64_t offset;
49
int64_t bytes;
50
+ bool zeroes;
51
QLIST_ENTRY(BlockCopyTask) list;
52
CoQueue wait_queue; /* coroutines blocked on this task */
53
} BlockCopyTask;
54
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
55
* the beginning of it.
56
*/
57
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
58
+ BlockCopyCallState *call_state,
59
int64_t offset, int64_t bytes)
60
{
61
BlockCopyTask *task;
62
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
63
64
task = g_new(BlockCopyTask, 1);
65
*task = (BlockCopyTask) {
66
+ .task.func = block_copy_task_entry,
67
.s = s,
68
+ .call_state = call_state,
69
.offset = offset,
70
.bytes = bytes,
71
};
72
@@ -XXX,XX +XXX,XX @@ void block_copy_set_progress_meter(BlockCopyState *s, ProgressMeter *pm)
73
s->progress = pm;
74
}
75
76
+/*
77
+ * Takes ownership of @task
78
+ *
79
+ * If pool is NULL directly run the task, otherwise schedule it into the pool.
80
+ *
81
+ * Returns: task.func return code if pool is NULL
82
+ * otherwise -ECANCELED if pool status is bad
83
+ * otherwise 0 (successfully scheduled)
84
+ */
85
+static coroutine_fn int block_copy_task_run(AioTaskPool *pool,
86
+ BlockCopyTask *task)
87
+{
88
+ if (!pool) {
89
+ int ret = task->task.func(&task->task);
90
+
91
+ g_free(task);
92
+ return ret;
93
+ }
94
+
95
+ aio_task_pool_wait_slot(pool);
96
+ if (aio_task_pool_status(pool) < 0) {
97
+ co_put_to_shres(task->s->mem, task->bytes);
98
+ block_copy_task_end(task, -ECANCELED);
99
+ g_free(task);
100
+ return -ECANCELED;
101
+ }
102
+
103
+ aio_task_pool_start_task(pool, &task->task);
104
+
105
+ return 0;
106
+}
107
+
108
/*
109
* block_copy_do_copy
110
*
111
@@ -XXX,XX +XXX,XX @@ out:
24
@@ -XXX,XX +XXX,XX @@ out:
112
return ret;
25
return ret;
113
}
26
}
114
27
115
+static coroutine_fn int block_copy_task_entry(AioTask *task)
28
-static void error_callback_bh(void *opaque)
116
+{
29
-{
117
+ BlockCopyTask *t = container_of(task, BlockCopyTask, task);
30
- Coroutine *co = opaque;
118
+ bool error_is_read;
31
- qemu_coroutine_enter(co);
119
+ int ret;
32
-}
120
+
33
-
121
+ ret = block_copy_do_copy(t->s, t->offset, t->bytes, t->zeroes,
34
static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
122
+ &error_is_read);
123
+ if (ret < 0 && !t->call_state->failed) {
124
+ t->call_state->failed = true;
125
+ t->call_state->error_is_read = error_is_read;
126
+ } else {
127
+ progress_work_done(t->s->progress, t->bytes);
128
+ t->s->progress_bytes_callback(t->bytes, t->s->progress_opaque);
129
+ }
130
+ co_put_to_shres(t->s->mem, t->bytes);
131
+ block_copy_task_end(t, ret);
132
+
133
+ return ret;
134
+}
135
+
136
static int block_copy_block_status(BlockCopyState *s, int64_t offset,
137
int64_t bytes, int64_t *pnum)
138
{
35
{
139
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
36
BDRVBlkdebugState *s = bs->opaque;
140
int ret = 0;
37
@@ -XXX,XX +XXX,XX @@ static int inject_error(BlockDriverState *bs, BlkdebugRule *rule)
141
bool found_dirty = false;
142
int64_t end = offset + bytes;
143
+ AioTaskPool *aio = NULL;
144
+ BlockCopyCallState call_state = {false, false};
145
146
/*
147
* block_copy() user is responsible for keeping source and target in same
148
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
149
assert(QEMU_IS_ALIGNED(offset, s->cluster_size));
150
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
151
152
- while (bytes) {
153
- g_autofree BlockCopyTask *task = NULL;
154
+ while (bytes && aio_task_pool_status(aio) == 0) {
155
+ BlockCopyTask *task;
156
int64_t status_bytes;
157
158
- task = block_copy_task_create(s, offset, bytes);
159
+ task = block_copy_task_create(s, &call_state, offset, bytes);
160
if (!task) {
161
/* No more dirty bits in the bitmap */
162
trace_block_copy_skip_range(s, offset, bytes);
163
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
164
}
165
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
166
block_copy_task_end(task, 0);
167
+ g_free(task);
168
progress_set_remaining(s->progress,
169
bdrv_get_dirty_count(s->copy_bitmap) +
170
s->in_flight_bytes);
171
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
172
bytes = end - offset;
173
continue;
174
}
175
+ task->zeroes = ret & BDRV_BLOCK_ZERO;
176
177
trace_block_copy_process(s, task->offset);
178
179
co_get_from_shres(s->mem, task->bytes);
180
- ret = block_copy_do_copy(s, task->offset, task->bytes,
181
- ret & BDRV_BLOCK_ZERO, error_is_read);
182
- co_put_to_shres(s->mem, task->bytes);
183
- block_copy_task_end(task, ret);
184
- if (ret < 0) {
185
- return ret;
186
- }
187
188
- progress_work_done(s->progress, task->bytes);
189
- s->progress_bytes_callback(task->bytes, s->progress_opaque);
190
offset = task_end(task);
191
bytes = end - offset;
192
+
193
+ if (!aio && bytes) {
194
+ aio = aio_task_pool_new(BLOCK_COPY_MAX_WORKERS);
195
+ }
196
+
197
+ ret = block_copy_task_run(aio, task);
198
+ if (ret < 0) {
199
+ goto out;
200
+ }
201
+ }
202
+
203
+out:
204
+ if (aio) {
205
+ aio_task_pool_wait_all(aio);
206
+
207
+ /*
208
+ * We are not really interested in -ECANCELED returned from
209
+ * block_copy_task_run. If it fails, it means some task already failed
210
+ * for real reason, let's return first failure.
211
+ * Still, assert that we don't rewrite failure by success.
212
+ */
213
+ assert(ret == 0 || aio_task_pool_status(aio) < 0);
214
+ ret = aio_task_pool_status(aio);
215
+
216
+ aio_task_pool_free(aio);
217
+ }
218
+ if (error_is_read && ret < 0) {
219
+ *error_is_read = call_state.error_is_read;
220
}
38
}
221
39
222
- return found_dirty;
40
if (!immediately) {
223
+ return ret < 0 ? ret : found_dirty;
41
- aio_bh_schedule_oneshot(bdrv_get_aio_context(bs), error_callback_bh,
224
}
42
- qemu_coroutine_self());
225
43
+ aio_co_schedule(qemu_get_current_aio_context(), qemu_coroutine_self());
226
/*
44
qemu_coroutine_yield();
45
}
46
227
--
47
--
228
2.26.2
48
2.9.3
229
49
230
50
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We no longer need to accommodate <3.4, drop this code.
3
qed_aio_start_io and qed_aio_next_io will not have to acquire/release
4
(The lines were > 79 chars and it stood out.)
4
the AioContext, while qed_aio_next_io_cb will. Split the functionality
5
and gain a little type-safety in the process.
5
6
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Message-Id: <20200331000014.11581-8-jsnow@redhat.com>
9
Reviewed-by: Fam Zheng <famz@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Message-id: 20170213135235.12274-11-pbonzini@redhat.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
13
---
12
tests/qemu-iotests/iotests.py | 5 +----
14
block/qed.c | 39 +++++++++++++++++++++++++--------------
13
1 file changed, 1 insertion(+), 4 deletions(-)
15
1 file changed, 25 insertions(+), 14 deletions(-)
14
16
15
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
17
diff --git a/block/qed.c b/block/qed.c
16
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/iotests.py
19
--- a/block/qed.c
18
+++ b/tests/qemu-iotests/iotests.py
20
+++ b/block/qed.c
19
@@ -XXX,XX +XXX,XX @@ def log(msg, filters=(), indent=None):
21
@@ -XXX,XX +XXX,XX @@ static CachedL2Table *qed_new_l2_table(BDRVQEDState *s)
20
for flt in filters:
22
return l2_table;
21
msg = flt(msg)
23
}
22
if isinstance(msg, (dict, list)):
24
23
- # Python < 3.4 needs to know not to add whitespace when pretty-printing:
25
-static void qed_aio_next_io(void *opaque, int ret);
24
- separators = (', ', ': ') if indent is None else (',', ': ')
26
+static void qed_aio_next_io(QEDAIOCB *acb, int ret);
25
# Don't sort if it's already sorted
27
+
26
do_sort = not isinstance(msg, OrderedDict)
28
+static void qed_aio_start_io(QEDAIOCB *acb)
27
- print(json.dumps(msg, sort_keys=do_sort,
29
+{
28
- indent=indent, separators=separators))
30
+ qed_aio_next_io(acb, 0);
29
+ print(json.dumps(msg, sort_keys=do_sort, indent=indent))
31
+}
30
else:
32
+
31
print(msg)
33
+static void qed_aio_next_io_cb(void *opaque, int ret)
34
+{
35
+ QEDAIOCB *acb = opaque;
36
+
37
+ qed_aio_next_io(acb, ret);
38
+}
39
40
static void qed_plug_allocating_write_reqs(BDRVQEDState *s)
41
{
42
@@ -XXX,XX +XXX,XX @@ static void qed_unplug_allocating_write_reqs(BDRVQEDState *s)
43
44
acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
45
if (acb) {
46
- qed_aio_next_io(acb, 0);
47
+ qed_aio_start_io(acb);
48
}
49
}
50
51
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete(QEDAIOCB *acb, int ret)
52
QSIMPLEQ_REMOVE_HEAD(&s->allocating_write_reqs, next);
53
acb = QSIMPLEQ_FIRST(&s->allocating_write_reqs);
54
if (acb) {
55
- qed_aio_next_io(acb, 0);
56
+ qed_aio_start_io(acb);
57
} else if (s->header.features & QED_F_NEED_CHECK) {
58
qed_start_need_check_timer(s);
59
}
60
@@ -XXX,XX +XXX,XX @@ static void qed_commit_l2_update(void *opaque, int ret)
61
acb->request.l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
62
assert(acb->request.l2_table != NULL);
63
64
- qed_aio_next_io(opaque, ret);
65
+ qed_aio_next_io(acb, ret);
66
}
67
68
/**
69
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_l2_update(QEDAIOCB *acb, int ret, uint64_t offset)
70
if (need_alloc) {
71
/* Write out the whole new L2 table */
72
qed_write_l2_table(s, &acb->request, 0, s->table_nelems, true,
73
- qed_aio_write_l1_update, acb);
74
+ qed_aio_write_l1_update, acb);
75
} else {
76
/* Write out only the updated part of the L2 table */
77
qed_write_l2_table(s, &acb->request, index, acb->cur_nclusters, false,
78
- qed_aio_next_io, acb);
79
+ qed_aio_next_io_cb, acb);
80
}
81
return;
82
83
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_main(void *opaque, int ret)
84
}
85
86
if (acb->find_cluster_ret == QED_CLUSTER_FOUND) {
87
- next_fn = qed_aio_next_io;
88
+ next_fn = qed_aio_next_io_cb;
89
} else {
90
if (s->bs->backing) {
91
next_fn = qed_aio_write_flush_before_l2_update;
92
@@ -XXX,XX +XXX,XX @@ static void qed_aio_write_alloc(QEDAIOCB *acb, size_t len)
93
if (acb->flags & QED_AIOCB_ZERO) {
94
/* Skip ahead if the clusters are already zero */
95
if (acb->find_cluster_ret == QED_CLUSTER_ZERO) {
96
- qed_aio_next_io(acb, 0);
97
+ qed_aio_start_io(acb);
98
return;
99
}
100
101
@@ -XXX,XX +XXX,XX @@ static void qed_aio_read_data(void *opaque, int ret,
102
/* Handle zero cluster and backing file reads */
103
if (ret == QED_CLUSTER_ZERO) {
104
qemu_iovec_memset(&acb->cur_qiov, 0, 0, acb->cur_qiov.size);
105
- qed_aio_next_io(acb, 0);
106
+ qed_aio_start_io(acb);
107
return;
108
} else if (ret != QED_CLUSTER_FOUND) {
109
qed_read_backing_file(s, acb->cur_pos, &acb->cur_qiov,
110
- &acb->backing_qiov, qed_aio_next_io, acb);
111
+ &acb->backing_qiov, qed_aio_next_io_cb, acb);
112
return;
113
}
114
115
BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO);
116
bdrv_aio_readv(bs->file, offset / BDRV_SECTOR_SIZE,
117
&acb->cur_qiov, acb->cur_qiov.size / BDRV_SECTOR_SIZE,
118
- qed_aio_next_io, acb);
119
+ qed_aio_next_io_cb, acb);
120
return;
121
122
err:
123
@@ -XXX,XX +XXX,XX @@ err:
124
/**
125
* Begin next I/O or complete the request
126
*/
127
-static void qed_aio_next_io(void *opaque, int ret)
128
+static void qed_aio_next_io(QEDAIOCB *acb, int ret)
129
{
130
- QEDAIOCB *acb = opaque;
131
BDRVQEDState *s = acb_to_s(acb);
132
QEDFindClusterFunc *io_fn = (acb->flags & QED_AIOCB_WRITE) ?
133
qed_aio_write_data : qed_aio_read_data;
134
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *qed_aio_setup(BlockDriverState *bs,
135
qemu_iovec_init(&acb->cur_qiov, qiov->niov);
136
137
/* Start request */
138
- qed_aio_next_io(acb, 0);
139
+ qed_aio_start_io(acb);
140
return &acb->common;
141
}
32
142
33
--
143
--
34
2.26.2
144
2.9.3
35
145
36
146
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
I had to fix a merge conflict, so do this tiny harmless thing while I'm
3
The AioContext data structures are now protected by list_lock and/or
4
here.
4
they are walked with FOREACH_RCU primitives. There is no need anymore
5
to acquire the AioContext for the entire duration of aio_dispatch.
6
Instead, just acquire it before and after invoking the callbacks.
7
The next step is then to push it further down.
5
8
6
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Message-Id: <20200331000014.11581-7-jsnow@redhat.com>
11
Reviewed-by: Fam Zheng <famz@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Message-id: 20170213135235.12274-12-pbonzini@redhat.com
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
---
15
---
13
tests/qemu-iotests/iotests.py | 18 +++++++++---------
16
util/aio-posix.c | 25 +++++++++++--------------
14
1 file changed, 9 insertions(+), 9 deletions(-)
17
util/aio-win32.c | 15 +++++++--------
18
util/async.c | 2 ++
19
3 files changed, 20 insertions(+), 22 deletions(-)
15
20
16
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
21
diff --git a/util/aio-posix.c b/util/aio-posix.c
17
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
18
--- a/tests/qemu-iotests/iotests.py
23
--- a/util/aio-posix.c
19
+++ b/tests/qemu-iotests/iotests.py
24
+++ b/util/aio-posix.c
20
@@ -XXX,XX +XXX,XX @@
25
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
21
# along with this program. If not, see <http://www.gnu.org/licenses/>.
26
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
22
#
27
aio_node_check(ctx, node->is_external) &&
23
28
node->io_read) {
24
+import atexit
29
+ aio_context_acquire(ctx);
25
+from collections import OrderedDict
30
node->io_read(node->opaque);
26
+import faulthandler
31
+ aio_context_release(ctx);
27
+import io
32
28
+import json
33
/* aio_notify() does not count as progress */
29
+import logging
34
if (node->opaque != &ctx->notifier) {
30
import os
35
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
31
import re
36
(revents & (G_IO_OUT | G_IO_ERR)) &&
32
+import signal
37
aio_node_check(ctx, node->is_external) &&
33
+import struct
38
node->io_write) {
34
import subprocess
39
+ aio_context_acquire(ctx);
35
-import unittest
40
node->io_write(node->opaque);
36
import sys
41
+ aio_context_release(ctx);
37
-import struct
42
progress = true;
38
-import json
43
}
39
-import signal
44
40
-import logging
45
@@ -XXX,XX +XXX,XX @@ bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
41
-import atexit
46
}
42
-import io
47
43
-from collections import OrderedDict
48
/* Run our timers */
44
-import faulthandler
49
+ aio_context_acquire(ctx);
45
+import unittest
50
progress |= timerlistgroup_run_timers(&ctx->tlg);
46
51
+ aio_context_release(ctx);
47
# pylint: disable=import-error, wrong-import-position
52
48
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
53
return progress;
54
}
55
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
56
int64_t timeout;
57
int64_t start = 0;
58
59
- aio_context_acquire(ctx);
60
- progress = false;
61
-
62
/* aio_notify can avoid the expensive event_notifier_set if
63
* everything (file descriptors, bottom halves, timers) will
64
* be re-evaluated before the next blocking poll(). This is
65
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
66
start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
67
}
68
69
- if (try_poll_mode(ctx, blocking)) {
70
- progress = true;
71
- } else {
72
+ aio_context_acquire(ctx);
73
+ progress = try_poll_mode(ctx, blocking);
74
+ aio_context_release(ctx);
75
+
76
+ if (!progress) {
77
assert(npfd == 0);
78
79
/* fill pollfds */
80
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
81
timeout = blocking ? aio_compute_timeout(ctx) : 0;
82
83
/* wait until next event */
84
- if (timeout) {
85
- aio_context_release(ctx);
86
- }
87
if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) {
88
AioHandler epoll_handler;
89
90
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
91
} else {
92
ret = qemu_poll_ns(pollfds, npfd, timeout);
93
}
94
- if (timeout) {
95
- aio_context_acquire(ctx);
96
- }
97
}
98
99
if (blocking) {
100
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
101
progress = true;
102
}
103
104
- aio_context_release(ctx);
105
-
106
return progress;
107
}
108
109
diff --git a/util/aio-win32.c b/util/aio-win32.c
110
index XXXXXXX..XXXXXXX 100644
111
--- a/util/aio-win32.c
112
+++ b/util/aio-win32.c
113
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
114
(revents || event_notifier_get_handle(node->e) == event) &&
115
node->io_notify) {
116
node->pfd.revents = 0;
117
+ aio_context_acquire(ctx);
118
node->io_notify(node->e);
119
+ aio_context_release(ctx);
120
121
/* aio_notify() does not count as progress */
122
if (node->e != &ctx->notifier) {
123
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
124
(node->io_read || node->io_write)) {
125
node->pfd.revents = 0;
126
if ((revents & G_IO_IN) && node->io_read) {
127
+ aio_context_acquire(ctx);
128
node->io_read(node->opaque);
129
+ aio_context_release(ctx);
130
progress = true;
131
}
132
if ((revents & G_IO_OUT) && node->io_write) {
133
+ aio_context_acquire(ctx);
134
node->io_write(node->opaque);
135
+ aio_context_release(ctx);
136
progress = true;
137
}
138
139
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
140
int count;
141
int timeout;
142
143
- aio_context_acquire(ctx);
144
progress = false;
145
146
/* aio_notify can avoid the expensive event_notifier_set if
147
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
148
149
timeout = blocking && !have_select_revents
150
? qemu_timeout_ns_to_ms(aio_compute_timeout(ctx)) : 0;
151
- if (timeout) {
152
- aio_context_release(ctx);
153
- }
154
ret = WaitForMultipleObjects(count, events, FALSE, timeout);
155
if (blocking) {
156
assert(first);
157
atomic_sub(&ctx->notify_me, 2);
158
}
159
- if (timeout) {
160
- aio_context_acquire(ctx);
161
- }
162
163
if (first) {
164
aio_notify_accept(ctx);
165
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
166
progress |= aio_dispatch_handlers(ctx, event);
167
} while (count > 0);
168
169
+ aio_context_acquire(ctx);
170
progress |= timerlistgroup_run_timers(&ctx->tlg);
171
-
172
aio_context_release(ctx);
173
return progress;
174
}
175
diff --git a/util/async.c b/util/async.c
176
index XXXXXXX..XXXXXXX 100644
177
--- a/util/async.c
178
+++ b/util/async.c
179
@@ -XXX,XX +XXX,XX @@ int aio_bh_poll(AioContext *ctx)
180
ret = 1;
181
}
182
bh->idle = 0;
183
+ aio_context_acquire(ctx);
184
aio_bh_call(bh);
185
+ aio_context_release(ctx);
186
}
187
if (bh->deleted) {
188
deleted = true;
49
--
189
--
50
2.26.2
190
2.9.3
51
191
52
192
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
It's bad hygiene: if we modify this list, it will be modified across all
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
invocations.
4
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5
5
Reviewed-by: Fam Zheng <famz@redhat.com>
6
(Remaining bad usages are fixed in a subsequent patch which changes the
6
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
7
function signature anyway.)
7
Message-id: 20170213135235.12274-13-pbonzini@redhat.com
8
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: John Snow <jsnow@redhat.com>
10
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
11
Reviewed-by: Max Reitz <mreitz@redhat.com>
12
Message-Id: <20200331000014.11581-5-jsnow@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
9
---
16
tests/qemu-iotests/iotests.py | 24 ++++++++++++------------
10
block/qed.h | 3 +++
17
1 file changed, 12 insertions(+), 12 deletions(-)
11
block/curl.c | 2 ++
18
12
block/io.c | 5 +++++
19
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
13
block/iscsi.c | 8 ++++++--
20
index XXXXXXX..XXXXXXX 100644
14
block/null.c | 4 ++++
21
--- a/tests/qemu-iotests/iotests.py
15
block/qed.c | 12 ++++++++++++
22
+++ b/tests/qemu-iotests/iotests.py
16
block/throttle-groups.c | 2 ++
23
@@ -XXX,XX +XXX,XX @@ def qemu_img_log(*args):
17
util/aio-posix.c | 2 --
24
log(result, filters=[filter_testfiles])
18
util/aio-win32.c | 2 --
25
return result
19
util/qemu-coroutine-sleep.c | 2 +-
26
20
10 files changed, 35 insertions(+), 7 deletions(-)
27
-def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
21
28
+def img_info_log(filename, filter_path=None, imgopts=False, extra_args=()):
22
diff --git a/block/qed.h b/block/qed.h
29
args = ['info']
23
index XXXXXXX..XXXXXXX 100644
30
if imgopts:
24
--- a/block/qed.h
31
args.append('--image-opts')
25
+++ b/block/qed.h
32
@@ -XXX,XX +XXX,XX @@ def filter_qmp_imgfmt(qmsg):
26
@@ -XXX,XX +XXX,XX @@ enum {
33
return value
27
*/
34
return filter_qmp(qmsg, _filter)
28
typedef void QEDFindClusterFunc(void *opaque, int ret, uint64_t offset, size_t len);
35
29
36
-def log(msg, filters=[], indent=None):
30
+void qed_acquire(BDRVQEDState *s);
37
+def log(msg, filters=(), indent=None):
31
+void qed_release(BDRVQEDState *s);
38
'''Logs either a string message or a JSON serializable message (like QMP).
32
+
39
If indent is provided, JSON serializable messages are pretty-printed.'''
33
/**
40
for flt in filters:
34
* Generic callback for chaining async callbacks
41
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
35
*/
42
result.append(filter_qmp_event(ev))
36
diff --git a/block/curl.c b/block/curl.c
43
return result
37
index XXXXXXX..XXXXXXX 100644
44
38
--- a/block/curl.c
45
- def qmp_log(self, cmd, filters=[], indent=None, **kwargs):
39
+++ b/block/curl.c
46
+ def qmp_log(self, cmd, filters=(), indent=None, **kwargs):
40
@@ -XXX,XX +XXX,XX @@ static void curl_multi_timeout_do(void *arg)
47
full_cmd = OrderedDict((
41
return;
48
("execute", cmd),
42
}
49
("arguments", ordered_qmp(kwargs))
43
50
@@ -XXX,XX +XXX,XX @@ def case_notrun(reason):
44
+ aio_context_acquire(s->aio_context);
51
open('%s/%s.casenotrun' % (output_dir, seq), 'a').write(
45
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
52
' [case not run] ' + reason + '\n')
46
53
47
curl_multi_check_completion(s);
54
-def verify_image_format(supported_fmts=[], unsupported_fmts=[]):
48
+ aio_context_release(s->aio_context);
55
+def verify_image_format(supported_fmts=(), unsupported_fmts=()):
49
#else
56
assert not (supported_fmts and unsupported_fmts)
50
abort();
57
51
#endif
58
if 'generic' in supported_fmts and \
52
diff --git a/block/io.c b/block/io.c
59
@@ -XXX,XX +XXX,XX @@ def verify_image_format(supported_fmts=[], unsupported_fmts=[]):
53
index XXXXXXX..XXXXXXX 100644
60
if not_sup or (imgfmt in unsupported_fmts):
54
--- a/block/io.c
61
notrun('not suitable for this image format: %s' % imgfmt)
55
+++ b/block/io.c
62
56
@@ -XXX,XX +XXX,XX @@ void bdrv_aio_cancel(BlockAIOCB *acb)
63
-def verify_protocol(supported=[], unsupported=[]):
57
if (acb->aiocb_info->get_aio_context) {
64
+def verify_protocol(supported=(), unsupported=()):
58
aio_poll(acb->aiocb_info->get_aio_context(acb), true);
65
assert not (supported and unsupported)
59
} else if (acb->bs) {
66
60
+ /* qemu_aio_ref and qemu_aio_unref are not thread-safe, so
67
if 'generic' in supported:
61
+ * assert that we're not using an I/O thread. Thread-safe
68
@@ -XXX,XX +XXX,XX @@ def verify_platform(supported=None, unsupported=None):
62
+ * code should use bdrv_aio_cancel_async exclusively.
69
if not any((sys.platform.startswith(x) for x in supported)):
63
+ */
70
notrun('not suitable for this OS: %s' % sys.platform)
64
+ assert(bdrv_get_aio_context(acb->bs) == qemu_get_aio_context());
71
65
aio_poll(bdrv_get_aio_context(acb->bs), true);
72
-def verify_cache_mode(supported_cache_modes=[]):
66
} else {
73
+def verify_cache_mode(supported_cache_modes=()):
67
abort();
74
if supported_cache_modes and (cachemode not in supported_cache_modes):
68
diff --git a/block/iscsi.c b/block/iscsi.c
75
notrun('not suitable for this cache mode: %s' % cachemode)
69
index XXXXXXX..XXXXXXX 100644
76
70
--- a/block/iscsi.c
77
-def verify_aio_mode(supported_aio_modes=[]):
71
+++ b/block/iscsi.c
78
+def verify_aio_mode(supported_aio_modes=()):
72
@@ -XXX,XX +XXX,XX @@ static void iscsi_retry_timer_expired(void *opaque)
79
if supported_aio_modes and (aiomode not in supported_aio_modes):
73
struct IscsiTask *iTask = opaque;
80
notrun('not suitable for this aio mode: %s' % aiomode)
74
iTask->complete = 1;
81
75
if (iTask->co) {
82
@@ -XXX,XX +XXX,XX @@ def supported_formats(read_only=False):
76
- qemu_coroutine_enter(iTask->co);
83
77
+ aio_co_wake(iTask->co);
84
return supported_formats.formats[read_only]
78
}
85
79
}
86
-def skip_if_unsupported(required_formats=[], read_only=False):
80
87
+def skip_if_unsupported(required_formats=(), read_only=False):
81
@@ -XXX,XX +XXX,XX @@ static void iscsi_nop_timed_event(void *opaque)
88
'''Skip Test Decorator
82
{
89
Runs the test if all the required formats are whitelisted'''
83
IscsiLun *iscsilun = opaque;
90
def skip_test_decorator(func):
84
91
@@ -XXX,XX +XXX,XX @@ def execute_unittest(output, verbosity, debug):
85
+ aio_context_acquire(iscsilun->aio_context);
92
sys.stderr.write(out)
86
if (iscsi_get_nops_in_flight(iscsilun->iscsi) >= MAX_NOP_FAILURES) {
93
87
error_report("iSCSI: NOP timeout. Reconnecting...");
94
def execute_test(test_function=None,
88
iscsilun->request_timed_out = true;
95
- supported_fmts=[],
89
} else if (iscsi_nop_out_async(iscsilun->iscsi, NULL, NULL, 0, NULL) != 0) {
96
+ supported_fmts=(),
90
error_report("iSCSI: failed to sent NOP-Out. Disabling NOP messages.");
97
supported_platforms=None,
91
- return;
98
- supported_cache_modes=[], supported_aio_modes={},
92
+ goto out;
99
- unsupported_fmts=[], supported_protocols=[],
93
}
100
- unsupported_protocols=[]):
94
101
+ supported_cache_modes=(), supported_aio_modes=(),
95
timer_mod(iscsilun->nop_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + NOP_INTERVAL);
102
+ unsupported_fmts=(), supported_protocols=(),
96
iscsi_set_events(iscsilun);
103
+ unsupported_protocols=()):
97
+
104
"""Run either unittest or script-style tests."""
98
+out:
105
99
+ aio_context_release(iscsilun->aio_context);
106
# We are using TEST_DIR and QEMU_DEFAULT_MACHINE as proxies to
100
}
101
102
static void iscsi_readcapacity_sync(IscsiLun *iscsilun, Error **errp)
103
diff --git a/block/null.c b/block/null.c
104
index XXXXXXX..XXXXXXX 100644
105
--- a/block/null.c
106
+++ b/block/null.c
107
@@ -XXX,XX +XXX,XX @@ static void null_bh_cb(void *opaque)
108
static void null_timer_cb(void *opaque)
109
{
110
NullAIOCB *acb = opaque;
111
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
112
+
113
+ aio_context_acquire(ctx);
114
acb->common.cb(acb->common.opaque, 0);
115
+ aio_context_release(ctx);
116
timer_deinit(&acb->timer);
117
qemu_aio_unref(acb);
118
}
119
diff --git a/block/qed.c b/block/qed.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/block/qed.c
122
+++ b/block/qed.c
123
@@ -XXX,XX +XXX,XX @@ static void qed_need_check_timer_cb(void *opaque)
124
125
trace_qed_need_check_timer_cb(s);
126
127
+ qed_acquire(s);
128
qed_plug_allocating_write_reqs(s);
129
130
/* Ensure writes are on disk before clearing flag */
131
bdrv_aio_flush(s->bs->file->bs, qed_clear_need_check, s);
132
+ qed_release(s);
133
+}
134
+
135
+void qed_acquire(BDRVQEDState *s)
136
+{
137
+ aio_context_acquire(bdrv_get_aio_context(s->bs));
138
+}
139
+
140
+void qed_release(BDRVQEDState *s)
141
+{
142
+ aio_context_release(bdrv_get_aio_context(s->bs));
143
}
144
145
static void qed_start_need_check_timer(BDRVQEDState *s)
146
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
147
index XXXXXXX..XXXXXXX 100644
148
--- a/block/throttle-groups.c
149
+++ b/block/throttle-groups.c
150
@@ -XXX,XX +XXX,XX @@ static void timer_cb(BlockBackend *blk, bool is_write)
151
qemu_mutex_unlock(&tg->lock);
152
153
/* Run the request that was waiting for this timer */
154
+ aio_context_acquire(blk_get_aio_context(blk));
155
empty_queue = !qemu_co_enter_next(&blkp->throttled_reqs[is_write]);
156
+ aio_context_release(blk_get_aio_context(blk));
157
158
/* If the request queue was empty then we have to take care of
159
* scheduling the next one */
160
diff --git a/util/aio-posix.c b/util/aio-posix.c
161
index XXXXXXX..XXXXXXX 100644
162
--- a/util/aio-posix.c
163
+++ b/util/aio-posix.c
164
@@ -XXX,XX +XXX,XX @@ bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
165
}
166
167
/* Run our timers */
168
- aio_context_acquire(ctx);
169
progress |= timerlistgroup_run_timers(&ctx->tlg);
170
- aio_context_release(ctx);
171
172
return progress;
173
}
174
diff --git a/util/aio-win32.c b/util/aio-win32.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/util/aio-win32.c
177
+++ b/util/aio-win32.c
178
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
179
progress |= aio_dispatch_handlers(ctx, event);
180
} while (count > 0);
181
182
- aio_context_acquire(ctx);
183
progress |= timerlistgroup_run_timers(&ctx->tlg);
184
- aio_context_release(ctx);
185
return progress;
186
}
187
188
diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c
189
index XXXXXXX..XXXXXXX 100644
190
--- a/util/qemu-coroutine-sleep.c
191
+++ b/util/qemu-coroutine-sleep.c
192
@@ -XXX,XX +XXX,XX @@ static void co_sleep_cb(void *opaque)
193
{
194
CoSleepCB *sleep_cb = opaque;
195
196
- qemu_coroutine_enter(sleep_cb->co);
197
+ aio_co_wake(sleep_cb->co);
198
}
199
200
void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
107
--
201
--
108
2.26.2
202
2.9.3
109
203
110
204
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Instead of just relying on the comment "Called only on full-dirty
3
This covers both file descriptor callbacks and polling callbacks,
4
region" in block_copy_task_create() let's move initial dirty area
4
since they execute related code.
5
search directly to block_copy_task_create(). Let's also use effective
6
bdrv_dirty_bitmap_next_dirty_area instead of looping through all
7
non-dirty clusters.
8
5
9
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
11
Message-Id: <20200429130847.28124-5-vsementsov@virtuozzo.com>
8
Reviewed-by: Fam Zheng <famz@redhat.com>
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
10
Message-id: 20170213135235.12274-14-pbonzini@redhat.com
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
12
---
14
block/block-copy.c | 80 ++++++++++++++++++++++++++--------------------
13
block/curl.c | 16 +++++++++++++---
15
1 file changed, 46 insertions(+), 34 deletions(-)
14
block/iscsi.c | 4 ++++
15
block/linux-aio.c | 4 ++++
16
block/nfs.c | 6 ++++++
17
block/sheepdog.c | 29 +++++++++++++++--------------
18
block/ssh.c | 29 +++++++++--------------------
19
block/win32-aio.c | 10 ++++++----
20
hw/block/virtio-blk.c | 5 ++++-
21
hw/scsi/virtio-scsi.c | 7 +++++++
22
util/aio-posix.c | 7 -------
23
util/aio-win32.c | 6 ------
24
11 files changed, 68 insertions(+), 55 deletions(-)
16
25
17
diff --git a/block/block-copy.c b/block/block-copy.c
26
diff --git a/block/curl.c b/block/curl.c
18
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
19
--- a/block/block-copy.c
28
--- a/block/curl.c
20
+++ b/block/block-copy.c
29
+++ b/block/curl.c
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyTask {
30
@@ -XXX,XX +XXX,XX @@ static void curl_multi_check_completion(BDRVCURLState *s)
22
CoQueue wait_queue; /* coroutines blocked on this task */
31
}
23
} BlockCopyTask;
32
}
24
33
25
+static int64_t task_end(BlockCopyTask *task)
34
-static void curl_multi_do(void *arg)
35
+static void curl_multi_do_locked(CURLState *s)
36
{
37
- CURLState *s = (CURLState *)arg;
38
CURLSocket *socket, *next_socket;
39
int running;
40
int r;
41
@@ -XXX,XX +XXX,XX @@ static void curl_multi_do(void *arg)
42
}
43
}
44
45
+static void curl_multi_do(void *arg)
26
+{
46
+{
27
+ return task->offset + task->bytes;
47
+ CURLState *s = (CURLState *)arg;
48
+
49
+ aio_context_acquire(s->s->aio_context);
50
+ curl_multi_do_locked(s);
51
+ aio_context_release(s->s->aio_context);
28
+}
52
+}
29
+
53
+
30
typedef struct BlockCopyState {
54
static void curl_multi_read(void *arg)
31
/*
55
{
32
* BdrvChild objects are not owned or managed by block-copy. They are
56
CURLState *s = (CURLState *)arg;
33
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
57
58
- curl_multi_do(arg);
59
+ aio_context_acquire(s->s->aio_context);
60
+ curl_multi_do_locked(s);
61
curl_multi_check_completion(s->s);
62
+ aio_context_release(s->s->aio_context);
63
}
64
65
static void curl_multi_timeout_do(void *arg)
66
diff --git a/block/iscsi.c b/block/iscsi.c
67
index XXXXXXX..XXXXXXX 100644
68
--- a/block/iscsi.c
69
+++ b/block/iscsi.c
70
@@ -XXX,XX +XXX,XX @@ iscsi_process_read(void *arg)
71
IscsiLun *iscsilun = arg;
72
struct iscsi_context *iscsi = iscsilun->iscsi;
73
74
+ aio_context_acquire(iscsilun->aio_context);
75
iscsi_service(iscsi, POLLIN);
76
iscsi_set_events(iscsilun);
77
+ aio_context_release(iscsilun->aio_context);
78
}
79
80
static void
81
@@ -XXX,XX +XXX,XX @@ iscsi_process_write(void *arg)
82
IscsiLun *iscsilun = arg;
83
struct iscsi_context *iscsi = iscsilun->iscsi;
84
85
+ aio_context_acquire(iscsilun->aio_context);
86
iscsi_service(iscsi, POLLOUT);
87
iscsi_set_events(iscsilun);
88
+ aio_context_release(iscsilun->aio_context);
89
}
90
91
static int64_t sector_lun2qemu(int64_t sector, IscsiLun *iscsilun)
92
diff --git a/block/linux-aio.c b/block/linux-aio.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/block/linux-aio.c
95
+++ b/block/linux-aio.c
96
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_completion_cb(EventNotifier *e)
97
LinuxAioState *s = container_of(e, LinuxAioState, e);
98
99
if (event_notifier_test_and_clear(&s->e)) {
100
+ aio_context_acquire(s->aio_context);
101
qemu_laio_process_completions_and_submit(s);
102
+ aio_context_release(s->aio_context);
103
}
104
}
105
106
@@ -XXX,XX +XXX,XX @@ static bool qemu_laio_poll_cb(void *opaque)
107
return false;
108
}
109
110
+ aio_context_acquire(s->aio_context);
111
qemu_laio_process_completions_and_submit(s);
112
+ aio_context_release(s->aio_context);
34
return true;
113
return true;
35
}
114
}
36
115
37
-/* Called only on full-dirty region */
116
diff --git a/block/nfs.c b/block/nfs.c
38
+/*
117
index XXXXXXX..XXXXXXX 100644
39
+ * Search for the first dirty area in offset/bytes range and create task at
118
--- a/block/nfs.c
40
+ * the beginning of it.
119
+++ b/block/nfs.c
120
@@ -XXX,XX +XXX,XX @@ static void nfs_set_events(NFSClient *client)
121
static void nfs_process_read(void *arg)
122
{
123
NFSClient *client = arg;
124
+
125
+ aio_context_acquire(client->aio_context);
126
nfs_service(client->context, POLLIN);
127
nfs_set_events(client);
128
+ aio_context_release(client->aio_context);
129
}
130
131
static void nfs_process_write(void *arg)
132
{
133
NFSClient *client = arg;
134
+
135
+ aio_context_acquire(client->aio_context);
136
nfs_service(client->context, POLLOUT);
137
nfs_set_events(client);
138
+ aio_context_release(client->aio_context);
139
}
140
141
static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
142
diff --git a/block/sheepdog.c b/block/sheepdog.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/sheepdog.c
145
+++ b/block/sheepdog.c
146
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int send_co_req(int sockfd, SheepdogReq *hdr, void *data,
147
return ret;
148
}
149
150
-static void restart_co_req(void *opaque)
151
-{
152
- Coroutine *co = opaque;
153
-
154
- qemu_coroutine_enter(co);
155
-}
156
-
157
typedef struct SheepdogReqCo {
158
int sockfd;
159
BlockDriverState *bs;
160
@@ -XXX,XX +XXX,XX @@ typedef struct SheepdogReqCo {
161
unsigned int *rlen;
162
int ret;
163
bool finished;
164
+ Coroutine *co;
165
} SheepdogReqCo;
166
167
+static void restart_co_req(void *opaque)
168
+{
169
+ SheepdogReqCo *srco = opaque;
170
+
171
+ aio_co_wake(srco->co);
172
+}
173
+
174
static coroutine_fn void do_co_req(void *opaque)
175
{
176
int ret;
177
- Coroutine *co;
178
SheepdogReqCo *srco = opaque;
179
int sockfd = srco->sockfd;
180
SheepdogReq *hdr = srco->hdr;
181
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void do_co_req(void *opaque)
182
unsigned int *wlen = srco->wlen;
183
unsigned int *rlen = srco->rlen;
184
185
- co = qemu_coroutine_self();
186
+ srco->co = qemu_coroutine_self();
187
aio_set_fd_handler(srco->aio_context, sockfd, false,
188
- NULL, restart_co_req, NULL, co);
189
+ NULL, restart_co_req, NULL, srco);
190
191
ret = send_co_req(sockfd, hdr, data, wlen);
192
if (ret < 0) {
193
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void do_co_req(void *opaque)
194
}
195
196
aio_set_fd_handler(srco->aio_context, sockfd, false,
197
- restart_co_req, NULL, NULL, co);
198
+ restart_co_req, NULL, NULL, srco);
199
200
ret = qemu_co_recv(sockfd, hdr, sizeof(*hdr));
201
if (ret != sizeof(*hdr)) {
202
@@ -XXX,XX +XXX,XX @@ out:
203
aio_set_fd_handler(srco->aio_context, sockfd, false,
204
NULL, NULL, NULL, NULL);
205
206
+ srco->co = NULL;
207
srco->ret = ret;
208
srco->finished = true;
209
if (srco->bs) {
210
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn aio_read_response(void *opaque)
211
* We've finished all requests which belong to the AIOCB, so
212
* we can switch back to sd_co_readv/writev now.
213
*/
214
- qemu_coroutine_enter(acb->coroutine);
215
+ aio_co_wake(acb->coroutine);
216
}
217
218
return;
219
@@ -XXX,XX +XXX,XX @@ static void co_read_response(void *opaque)
220
s->co_recv = qemu_coroutine_create(aio_read_response, opaque);
221
}
222
223
- qemu_coroutine_enter(s->co_recv);
224
+ aio_co_wake(s->co_recv);
225
}
226
227
static void co_write_request(void *opaque)
228
{
229
BDRVSheepdogState *s = opaque;
230
231
- qemu_coroutine_enter(s->co_send);
232
+ aio_co_wake(s->co_send);
233
}
234
235
/*
236
diff --git a/block/ssh.c b/block/ssh.c
237
index XXXXXXX..XXXXXXX 100644
238
--- a/block/ssh.c
239
+++ b/block/ssh.c
240
@@ -XXX,XX +XXX,XX @@ static void restart_coroutine(void *opaque)
241
242
DPRINTF("co=%p", co);
243
244
- qemu_coroutine_enter(co);
245
+ aio_co_wake(co);
246
}
247
248
-static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
249
+/* A non-blocking call returned EAGAIN, so yield, ensuring the
250
+ * handlers are set up so that we'll be rescheduled when there is an
251
+ * interesting event on the socket.
41
+ */
252
+ */
42
static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
253
+static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
43
int64_t offset, int64_t bytes)
254
{
44
{
255
int r;
45
- BlockCopyTask *task = g_new(BlockCopyTask, 1);
256
IOHandler *rd_handler = NULL, *wr_handler = NULL;
46
+ BlockCopyTask *task;
257
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void set_fd_handler(BDRVSSHState *s, BlockDriverState *bs)
47
258
48
+ if (!bdrv_dirty_bitmap_next_dirty_area(s->copy_bitmap,
259
aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
49
+ offset, offset + bytes,
260
false, rd_handler, wr_handler, NULL, co);
50
+ s->copy_size, &offset, &bytes))
261
-}
51
+ {
262
-
52
+ return NULL;
263
-static coroutine_fn void clear_fd_handler(BDRVSSHState *s,
53
+ }
264
- BlockDriverState *bs)
54
+
265
-{
55
+ /* region is dirty, so no existent tasks possible in it */
266
- DPRINTF("s->sock=%d", s->sock);
56
assert(!find_conflicting_task(s, offset, bytes));
267
- aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock,
57
268
- false, NULL, NULL, NULL, NULL);
58
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
269
-}
59
s->in_flight_bytes += bytes;
270
-
60
271
-/* A non-blocking call returned EAGAIN, so yield, ensuring the
61
+ task = g_new(BlockCopyTask, 1);
272
- * handlers are set up so that we'll be rescheduled when there is an
62
*task = (BlockCopyTask) {
273
- * interesting event on the socket.
63
.s = s,
274
- */
64
.offset = offset,
275
-static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs)
65
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
276
-{
66
{
277
- set_fd_handler(s, bs);
67
int ret = 0;
278
qemu_coroutine_yield();
68
bool found_dirty = false;
279
- clear_fd_handler(s, bs);
69
+ int64_t end = offset + bytes;
280
+ DPRINTF("s->sock=%d - back", s->sock);
70
281
+ aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, false,
71
/*
282
+ NULL, NULL, NULL, NULL);
72
* block_copy() user is responsible for keeping source and target in same
283
}
73
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
284
74
285
/* SFTP has a function `libssh2_sftp_seek64' which seeks to a position
75
while (bytes) {
286
diff --git a/block/win32-aio.c b/block/win32-aio.c
76
g_autofree BlockCopyTask *task = NULL;
287
index XXXXXXX..XXXXXXX 100644
77
- int64_t next_zero, cur_bytes, status_bytes;
288
--- a/block/win32-aio.c
78
+ int64_t status_bytes;
289
+++ b/block/win32-aio.c
79
290
@@ -XXX,XX +XXX,XX @@ struct QEMUWin32AIOState {
80
- if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
291
HANDLE hIOCP;
81
- trace_block_copy_skip(s, offset);
292
EventNotifier e;
82
- offset += s->cluster_size;
293
int count;
83
- bytes -= s->cluster_size;
294
- bool is_aio_context_attached;
84
- continue; /* already copied */
295
+ AioContext *aio_ctx;
85
+ task = block_copy_task_create(s, offset, bytes);
296
};
86
+ if (!task) {
297
87
+ /* No more dirty bits in the bitmap */
298
typedef struct QEMUWin32AIOCB {
88
+ trace_block_copy_skip_range(s, offset, bytes);
299
@@ -XXX,XX +XXX,XX @@ static void win32_aio_process_completion(QEMUWin32AIOState *s,
89
+ break;
300
}
90
+ }
301
91
+ if (task->offset > offset) {
302
92
+ trace_block_copy_skip_range(s, offset, task->offset - offset);
303
+ aio_context_acquire(s->aio_ctx);
304
waiocb->common.cb(waiocb->common.opaque, ret);
305
+ aio_context_release(s->aio_ctx);
306
qemu_aio_unref(waiocb);
307
}
308
309
@@ -XXX,XX +XXX,XX @@ void win32_aio_detach_aio_context(QEMUWin32AIOState *aio,
310
AioContext *old_context)
311
{
312
aio_set_event_notifier(old_context, &aio->e, false, NULL, NULL);
313
- aio->is_aio_context_attached = false;
314
+ aio->aio_ctx = NULL;
315
}
316
317
void win32_aio_attach_aio_context(QEMUWin32AIOState *aio,
318
AioContext *new_context)
319
{
320
- aio->is_aio_context_attached = true;
321
+ aio->aio_ctx = new_context;
322
aio_set_event_notifier(new_context, &aio->e, false,
323
win32_aio_completion_cb, NULL);
324
}
325
@@ -XXX,XX +XXX,XX @@ out_free_state:
326
327
void win32_aio_cleanup(QEMUWin32AIOState *aio)
328
{
329
- assert(!aio->is_aio_context_attached);
330
+ assert(!aio->aio_ctx);
331
CloseHandle(aio->hIOCP);
332
event_notifier_cleanup(&aio->e);
333
g_free(aio);
334
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
335
index XXXXXXX..XXXXXXX 100644
336
--- a/hw/block/virtio-blk.c
337
+++ b/hw/block/virtio-blk.c
338
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_ioctl_complete(void *opaque, int status)
339
{
340
VirtIOBlockIoctlReq *ioctl_req = opaque;
341
VirtIOBlockReq *req = ioctl_req->req;
342
- VirtIODevice *vdev = VIRTIO_DEVICE(req->dev);
343
+ VirtIOBlock *s = req->dev;
344
+ VirtIODevice *vdev = VIRTIO_DEVICE(s);
345
struct virtio_scsi_inhdr *scsi;
346
struct sg_io_hdr *hdr;
347
348
@@ -XXX,XX +XXX,XX @@ bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
349
MultiReqBuffer mrb = {};
350
bool progress = false;
351
352
+ aio_context_acquire(blk_get_aio_context(s->blk));
353
blk_io_plug(s->blk);
354
355
do {
356
@@ -XXX,XX +XXX,XX @@ bool virtio_blk_handle_vq(VirtIOBlock *s, VirtQueue *vq)
357
}
358
359
blk_io_unplug(s->blk);
360
+ aio_context_release(blk_get_aio_context(s->blk));
361
return progress;
362
}
363
364
diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c
365
index XXXXXXX..XXXXXXX 100644
366
--- a/hw/scsi/virtio-scsi.c
367
+++ b/hw/scsi/virtio-scsi.c
368
@@ -XXX,XX +XXX,XX @@ bool virtio_scsi_handle_ctrl_vq(VirtIOSCSI *s, VirtQueue *vq)
369
VirtIOSCSIReq *req;
370
bool progress = false;
371
372
+ virtio_scsi_acquire(s);
373
while ((req = virtio_scsi_pop_req(s, vq))) {
374
progress = true;
375
virtio_scsi_handle_ctrl_req(s, req);
376
}
377
+ virtio_scsi_release(s);
378
return progress;
379
}
380
381
@@ -XXX,XX +XXX,XX @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
382
383
QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs);
384
385
+ virtio_scsi_acquire(s);
386
do {
387
virtio_queue_set_notification(vq, 0);
388
389
@@ -XXX,XX +XXX,XX @@ bool virtio_scsi_handle_cmd_vq(VirtIOSCSI *s, VirtQueue *vq)
390
QTAILQ_FOREACH_SAFE(req, &reqs, next, next) {
391
virtio_scsi_handle_cmd_req_submit(s, req);
392
}
393
+ virtio_scsi_release(s);
394
return progress;
395
}
396
397
@@ -XXX,XX +XXX,XX @@ out:
398
399
bool virtio_scsi_handle_event_vq(VirtIOSCSI *s, VirtQueue *vq)
400
{
401
+ virtio_scsi_acquire(s);
402
if (s->events_dropped) {
403
virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0);
404
+ virtio_scsi_release(s);
405
return true;
406
}
407
+ virtio_scsi_release(s);
408
return false;
409
}
410
411
diff --git a/util/aio-posix.c b/util/aio-posix.c
412
index XXXXXXX..XXXXXXX 100644
413
--- a/util/aio-posix.c
414
+++ b/util/aio-posix.c
415
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
416
(revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) &&
417
aio_node_check(ctx, node->is_external) &&
418
node->io_read) {
419
- aio_context_acquire(ctx);
420
node->io_read(node->opaque);
421
- aio_context_release(ctx);
422
423
/* aio_notify() does not count as progress */
424
if (node->opaque != &ctx->notifier) {
425
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
426
(revents & (G_IO_OUT | G_IO_ERR)) &&
427
aio_node_check(ctx, node->is_external) &&
428
node->io_write) {
429
- aio_context_acquire(ctx);
430
node->io_write(node->opaque);
431
- aio_context_release(ctx);
432
progress = true;
93
}
433
}
94
434
95
found_dirty = true;
435
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
96
436
start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
97
- cur_bytes = MIN(bytes, s->copy_size);
437
}
438
439
- aio_context_acquire(ctx);
440
progress = try_poll_mode(ctx, blocking);
441
- aio_context_release(ctx);
98
-
442
-
99
- next_zero = bdrv_dirty_bitmap_next_zero(s->copy_bitmap, offset,
443
if (!progress) {
100
- cur_bytes);
444
assert(npfd == 0);
101
- if (next_zero >= 0) {
445
102
- assert(next_zero > offset); /* offset is dirty */
446
diff --git a/util/aio-win32.c b/util/aio-win32.c
103
- assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
447
index XXXXXXX..XXXXXXX 100644
104
- cur_bytes = next_zero - offset;
448
--- a/util/aio-win32.c
105
- }
449
+++ b/util/aio-win32.c
106
- task = block_copy_task_create(s, offset, cur_bytes);
450
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
107
-
451
(revents || event_notifier_get_handle(node->e) == event) &&
108
- ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
452
node->io_notify) {
109
+ ret = block_copy_block_status(s, task->offset, task->bytes,
453
node->pfd.revents = 0;
110
+ &status_bytes);
454
- aio_context_acquire(ctx);
111
assert(ret >= 0); /* never fail */
455
node->io_notify(node->e);
112
- cur_bytes = MIN(cur_bytes, status_bytes);
456
- aio_context_release(ctx);
113
- block_copy_task_shrink(task, cur_bytes);
457
114
+ if (status_bytes < task->bytes) {
458
/* aio_notify() does not count as progress */
115
+ block_copy_task_shrink(task, status_bytes);
459
if (node->e != &ctx->notifier) {
116
+ }
460
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
117
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
461
(node->io_read || node->io_write)) {
118
block_copy_task_end(task, 0);
462
node->pfd.revents = 0;
119
progress_set_remaining(s->progress,
463
if ((revents & G_IO_IN) && node->io_read) {
120
bdrv_get_dirty_count(s->copy_bitmap) +
464
- aio_context_acquire(ctx);
121
s->in_flight_bytes);
465
node->io_read(node->opaque);
122
- trace_block_copy_skip_range(s, offset, status_bytes);
466
- aio_context_release(ctx);
123
- offset += status_bytes;
467
progress = true;
124
- bytes -= status_bytes;
468
}
125
+ trace_block_copy_skip_range(s, task->offset, task->bytes);
469
if ((revents & G_IO_OUT) && node->io_write) {
126
+ offset = task_end(task);
470
- aio_context_acquire(ctx);
127
+ bytes = end - offset;
471
node->io_write(node->opaque);
128
continue;
472
- aio_context_release(ctx);
129
}
473
progress = true;
130
474
}
131
- trace_block_copy_process(s, offset);
475
132
+ trace_block_copy_process(s, task->offset);
133
134
- co_get_from_shres(s->mem, cur_bytes);
135
- ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
136
- error_is_read);
137
- co_put_to_shres(s->mem, cur_bytes);
138
+ co_get_from_shres(s->mem, task->bytes);
139
+ ret = block_copy_do_copy(s, task->offset, task->bytes,
140
+ ret & BDRV_BLOCK_ZERO, error_is_read);
141
+ co_put_to_shres(s->mem, task->bytes);
142
block_copy_task_end(task, ret);
143
if (ret < 0) {
144
return ret;
145
}
146
147
- progress_work_done(s->progress, cur_bytes);
148
- s->progress_bytes_callback(cur_bytes, s->progress_opaque);
149
- offset += cur_bytes;
150
- bytes -= cur_bytes;
151
+ progress_work_done(s->progress, task->bytes);
152
+ s->progress_bytes_callback(task->bytes, s->progress_opaque);
153
+ offset = task_end(task);
154
+ bytes = end - offset;
155
}
156
157
return found_dirty;
158
--
476
--
159
2.26.2
477
2.9.3
160
478
161
479
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
It shadows (with a different type) the built-in format.
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Use something else.
4
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5
Reviewed-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
7
Message-id: 20170213135235.12274-15-pbonzini@redhat.com
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
block/archipelago.c | 3 +++
11
block/blkreplay.c | 2 +-
12
block/block-backend.c | 6 ++++++
13
block/curl.c | 26 ++++++++++++++++++--------
14
block/gluster.c | 9 +--------
15
block/io.c | 6 +++++-
16
block/iscsi.c | 6 +++++-
17
block/linux-aio.c | 15 +++++++++------
18
block/nfs.c | 3 ++-
19
block/null.c | 4 ++++
20
block/qed.c | 3 +++
21
block/rbd.c | 4 ++++
22
dma-helpers.c | 2 ++
23
hw/block/virtio-blk.c | 2 ++
24
hw/scsi/scsi-bus.c | 2 ++
25
util/async.c | 4 ++--
26
util/thread-pool.c | 2 ++
27
17 files changed, 71 insertions(+), 28 deletions(-)
5
28
6
Signed-off-by: John Snow <jsnow@redhat.com>
29
diff --git a/block/archipelago.c b/block/archipelago.c
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
30
index XXXXXXX..XXXXXXX 100644
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
31
--- a/block/archipelago.c
9
Message-Id: <20200331000014.11581-3-jsnow@redhat.com>
32
+++ b/block/archipelago.c
10
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
33
@@ -XXX,XX +XXX,XX @@ static void qemu_archipelago_complete_aio(void *opaque)
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
34
{
12
---
35
AIORequestData *reqdata = (AIORequestData *) opaque;
13
tests/qemu-iotests/055 | 3 ++-
36
ArchipelagoAIOCB *aio_cb = (ArchipelagoAIOCB *) reqdata->aio_cb;
14
tests/qemu-iotests/iotests.py | 6 +++---
37
+ AioContext *ctx = bdrv_get_aio_context(aio_cb->common.bs);
15
2 files changed, 5 insertions(+), 4 deletions(-)
38
16
39
+ aio_context_acquire(ctx);
17
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
40
aio_cb->common.cb(aio_cb->common.opaque, aio_cb->ret);
41
+ aio_context_release(ctx);
42
aio_cb->status = 0;
43
44
qemu_aio_unref(aio_cb);
45
diff --git a/block/blkreplay.c b/block/blkreplay.c
18
index XXXXXXX..XXXXXXX 100755
46
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/055
47
--- a/block/blkreplay.c
20
+++ b/tests/qemu-iotests/055
48
+++ b/block/blkreplay.c
21
@@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase):
49
@@ -XXX,XX +XXX,XX @@ static int64_t blkreplay_getlength(BlockDriverState *bs)
22
qemu_img('create', '-f', fmt, blockdev_target_img,
50
static void blkreplay_bh_cb(void *opaque)
23
str(TestDriveCompression.image_len), *args)
51
{
24
if attach_target:
52
Request *req = opaque;
25
- self.vm.add_drive(blockdev_target_img, format=fmt, interface="none")
53
- qemu_coroutine_enter(req->co);
26
+ self.vm.add_drive(blockdev_target_img,
54
+ aio_co_wake(req->co);
27
+ img_format=fmt, interface="none")
55
qemu_bh_delete(req->bh);
28
56
g_free(req);
29
self.vm.launch()
57
}
30
58
diff --git a/block/block-backend.c b/block/block-backend.c
31
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
59
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
60
--- a/block/block-backend.c
33
--- a/tests/qemu-iotests/iotests.py
61
+++ b/block/block-backend.c
34
+++ b/tests/qemu-iotests/iotests.py
62
@@ -XXX,XX +XXX,XX @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
35
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
63
static void error_callback_bh(void *opaque)
36
self._args.append(opts)
64
{
37
return self
65
struct BlockBackendAIOCB *acb = opaque;
38
66
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
39
- def add_drive(self, path, opts='', interface='virtio', format=imgfmt):
67
40
+ def add_drive(self, path, opts='', interface='virtio', img_format=imgfmt):
68
bdrv_dec_in_flight(acb->common.bs);
41
'''Add a virtio-blk drive to the VM'''
69
+ aio_context_acquire(ctx);
42
options = ['if=%s' % interface,
70
acb->common.cb(acb->common.opaque, acb->ret);
43
'id=drive%d' % self._num_drives]
71
+ aio_context_release(ctx);
44
72
qemu_aio_unref(acb);
45
if path is not None:
73
}
46
options.append('file=%s' % path)
74
47
- options.append('format=%s' % format)
75
@@ -XXX,XX +XXX,XX @@ static void blk_aio_complete(BlkAioEmAIOCB *acb)
48
+ options.append('format=%s' % img_format)
76
static void blk_aio_complete_bh(void *opaque)
49
options.append('cache=%s' % cachemode)
77
{
50
options.append('aio=%s' % aiomode)
78
BlkAioEmAIOCB *acb = opaque;
51
79
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
52
if opts:
80
53
options.append(opts)
81
assert(acb->has_returned);
54
82
+ aio_context_acquire(ctx);
55
- if format == 'luks' and 'key-secret' not in opts:
83
blk_aio_complete(acb);
56
+ if img_format == 'luks' and 'key-secret' not in opts:
84
+ aio_context_release(ctx);
57
# default luks support
85
}
58
if luks_default_secret_object not in self._args:
86
59
self.add_object(luks_default_secret_object)
87
static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
88
diff --git a/block/curl.c b/block/curl.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/block/curl.c
91
+++ b/block/curl.c
92
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
93
{
94
CURLState *state;
95
int running;
96
+ int ret = -EINPROGRESS;
97
98
CURLAIOCB *acb = p;
99
- BDRVCURLState *s = acb->common.bs->opaque;
100
+ BlockDriverState *bs = acb->common.bs;
101
+ BDRVCURLState *s = bs->opaque;
102
+ AioContext *ctx = bdrv_get_aio_context(bs);
103
104
size_t start = acb->sector_num * BDRV_SECTOR_SIZE;
105
size_t end;
106
107
+ aio_context_acquire(ctx);
108
+
109
// In case we have the requested data already (e.g. read-ahead),
110
// we can just call the callback and be done.
111
switch (curl_find_buf(s, start, acb->nb_sectors * BDRV_SECTOR_SIZE, acb)) {
112
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
113
qemu_aio_unref(acb);
114
// fall through
115
case FIND_RET_WAIT:
116
- return;
117
+ goto out;
118
default:
119
break;
120
}
121
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
122
// No cache found, so let's start a new request
123
state = curl_init_state(acb->common.bs, s);
124
if (!state) {
125
- acb->common.cb(acb->common.opaque, -EIO);
126
- qemu_aio_unref(acb);
127
- return;
128
+ ret = -EIO;
129
+ goto out;
130
}
131
132
acb->start = 0;
133
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
134
state->orig_buf = g_try_malloc(state->buf_len);
135
if (state->buf_len && state->orig_buf == NULL) {
136
curl_clean_state(state);
137
- acb->common.cb(acb->common.opaque, -ENOMEM);
138
- qemu_aio_unref(acb);
139
- return;
140
+ ret = -ENOMEM;
141
+ goto out;
142
}
143
state->acb[0] = acb;
144
145
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
146
147
/* Tell curl it needs to kick things off */
148
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
149
+
150
+out:
151
+ if (ret != -EINPROGRESS) {
152
+ acb->common.cb(acb->common.opaque, ret);
153
+ qemu_aio_unref(acb);
154
+ }
155
+ aio_context_release(ctx);
156
}
157
158
static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
159
diff --git a/block/gluster.c b/block/gluster.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/block/gluster.c
162
+++ b/block/gluster.c
163
@@ -XXX,XX +XXX,XX @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf,
164
return qemu_gluster_glfs_init(gconf, errp);
165
}
166
167
-static void qemu_gluster_complete_aio(void *opaque)
168
-{
169
- GlusterAIOCB *acb = (GlusterAIOCB *)opaque;
170
-
171
- qemu_coroutine_enter(acb->coroutine);
172
-}
173
-
174
/*
175
* AIO callback routine called from GlusterFS thread.
176
*/
177
@@ -XXX,XX +XXX,XX @@ static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg)
178
acb->ret = -EIO; /* Partial read/write - fail it */
179
}
180
181
- aio_bh_schedule_oneshot(acb->aio_context, qemu_gluster_complete_aio, acb);
182
+ aio_co_schedule(acb->aio_context, acb->coroutine);
183
}
184
185
static void qemu_gluster_parse_flags(int bdrv_flags, int *open_flags)
186
diff --git a/block/io.c b/block/io.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/block/io.c
189
+++ b/block/io.c
190
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
191
bdrv_dec_in_flight(bs);
192
bdrv_drained_begin(bs);
193
data->done = true;
194
- qemu_coroutine_enter(co);
195
+ aio_co_wake(co);
196
}
197
198
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs)
199
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_complete(BlockAIOCBCoroutine *acb)
200
static void bdrv_co_em_bh(void *opaque)
201
{
202
BlockAIOCBCoroutine *acb = opaque;
203
+ BlockDriverState *bs = acb->common.bs;
204
+ AioContext *ctx = bdrv_get_aio_context(bs);
205
206
assert(!acb->need_bh);
207
+ aio_context_acquire(ctx);
208
bdrv_co_complete(acb);
209
+ aio_context_release(ctx);
210
}
211
212
static void bdrv_co_maybe_schedule_bh(BlockAIOCBCoroutine *acb)
213
diff --git a/block/iscsi.c b/block/iscsi.c
214
index XXXXXXX..XXXXXXX 100644
215
--- a/block/iscsi.c
216
+++ b/block/iscsi.c
217
@@ -XXX,XX +XXX,XX @@ static void
218
iscsi_bh_cb(void *p)
219
{
220
IscsiAIOCB *acb = p;
221
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
222
223
qemu_bh_delete(acb->bh);
224
225
g_free(acb->buf);
226
acb->buf = NULL;
227
228
+ aio_context_acquire(ctx);
229
acb->common.cb(acb->common.opaque, acb->status);
230
+ aio_context_release(ctx);
231
232
if (acb->task != NULL) {
233
scsi_free_scsi_task(acb->task);
234
@@ -XXX,XX +XXX,XX @@ iscsi_schedule_bh(IscsiAIOCB *acb)
235
static void iscsi_co_generic_bh_cb(void *opaque)
236
{
237
struct IscsiTask *iTask = opaque;
238
+
239
iTask->complete = 1;
240
- qemu_coroutine_enter(iTask->co);
241
+ aio_co_wake(iTask->co);
242
}
243
244
static void iscsi_retry_timer_expired(void *opaque)
245
diff --git a/block/linux-aio.c b/block/linux-aio.c
246
index XXXXXXX..XXXXXXX 100644
247
--- a/block/linux-aio.c
248
+++ b/block/linux-aio.c
249
@@ -XXX,XX +XXX,XX @@ struct LinuxAioState {
250
io_context_t ctx;
251
EventNotifier e;
252
253
- /* io queue for submit at batch */
254
+ /* io queue for submit at batch. Protected by AioContext lock. */
255
LaioQueue io_q;
256
257
- /* I/O completion processing */
258
+ /* I/O completion processing. Only runs in I/O thread. */
259
QEMUBH *completion_bh;
260
int event_idx;
261
int event_max;
262
@@ -XXX,XX +XXX,XX @@ static inline ssize_t io_event_ret(struct io_event *ev)
263
*/
264
static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
265
{
266
+ LinuxAioState *s = laiocb->ctx;
267
int ret;
268
269
ret = laiocb->ret;
270
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
271
}
272
273
laiocb->ret = ret;
274
+ aio_context_acquire(s->aio_context);
275
if (laiocb->co) {
276
/* If the coroutine is already entered it must be in ioq_submit() and
277
* will notice laio->ret has been filled in when it eventually runs
278
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
279
laiocb->common.cb(laiocb->common.opaque, ret);
280
qemu_aio_unref(laiocb);
281
}
282
+ aio_context_release(s->aio_context);
283
}
284
285
/**
286
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completions(LinuxAioState *s)
287
static void qemu_laio_process_completions_and_submit(LinuxAioState *s)
288
{
289
qemu_laio_process_completions(s);
290
+
291
+ aio_context_acquire(s->aio_context);
292
if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) {
293
ioq_submit(s);
294
}
295
+ aio_context_release(s->aio_context);
296
}
297
298
static void qemu_laio_completion_bh(void *opaque)
299
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_completion_cb(EventNotifier *e)
300
LinuxAioState *s = container_of(e, LinuxAioState, e);
301
302
if (event_notifier_test_and_clear(&s->e)) {
303
- aio_context_acquire(s->aio_context);
304
qemu_laio_process_completions_and_submit(s);
305
- aio_context_release(s->aio_context);
306
}
307
}
308
309
@@ -XXX,XX +XXX,XX @@ static bool qemu_laio_poll_cb(void *opaque)
310
return false;
311
}
312
313
- aio_context_acquire(s->aio_context);
314
qemu_laio_process_completions_and_submit(s);
315
- aio_context_release(s->aio_context);
316
return true;
317
}
318
319
@@ -XXX,XX +XXX,XX @@ void laio_detach_aio_context(LinuxAioState *s, AioContext *old_context)
320
{
321
aio_set_event_notifier(old_context, &s->e, false, NULL, NULL);
322
qemu_bh_delete(s->completion_bh);
323
+ s->aio_context = NULL;
324
}
325
326
void laio_attach_aio_context(LinuxAioState *s, AioContext *new_context)
327
diff --git a/block/nfs.c b/block/nfs.c
328
index XXXXXXX..XXXXXXX 100644
329
--- a/block/nfs.c
330
+++ b/block/nfs.c
331
@@ -XXX,XX +XXX,XX @@ static void nfs_co_init_task(BlockDriverState *bs, NFSRPC *task)
332
static void nfs_co_generic_bh_cb(void *opaque)
333
{
334
NFSRPC *task = opaque;
335
+
336
task->complete = 1;
337
- qemu_coroutine_enter(task->co);
338
+ aio_co_wake(task->co);
339
}
340
341
static void
342
diff --git a/block/null.c b/block/null.c
343
index XXXXXXX..XXXXXXX 100644
344
--- a/block/null.c
345
+++ b/block/null.c
346
@@ -XXX,XX +XXX,XX @@ static const AIOCBInfo null_aiocb_info = {
347
static void null_bh_cb(void *opaque)
348
{
349
NullAIOCB *acb = opaque;
350
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
351
+
352
+ aio_context_acquire(ctx);
353
acb->common.cb(acb->common.opaque, 0);
354
+ aio_context_release(ctx);
355
qemu_aio_unref(acb);
356
}
357
358
diff --git a/block/qed.c b/block/qed.c
359
index XXXXXXX..XXXXXXX 100644
360
--- a/block/qed.c
361
+++ b/block/qed.c
362
@@ -XXX,XX +XXX,XX @@ static void qed_update_l2_table(BDRVQEDState *s, QEDTable *table, int index,
363
static void qed_aio_complete_bh(void *opaque)
364
{
365
QEDAIOCB *acb = opaque;
366
+ BDRVQEDState *s = acb_to_s(acb);
367
BlockCompletionFunc *cb = acb->common.cb;
368
void *user_opaque = acb->common.opaque;
369
int ret = acb->bh_ret;
370
@@ -XXX,XX +XXX,XX @@ static void qed_aio_complete_bh(void *opaque)
371
qemu_aio_unref(acb);
372
373
/* Invoke callback */
374
+ qed_acquire(s);
375
cb(user_opaque, ret);
376
+ qed_release(s);
377
}
378
379
static void qed_aio_complete(QEDAIOCB *acb, int ret)
380
diff --git a/block/rbd.c b/block/rbd.c
381
index XXXXXXX..XXXXXXX 100644
382
--- a/block/rbd.c
383
+++ b/block/rbd.c
384
@@ -XXX,XX +XXX,XX @@ shutdown:
385
static void qemu_rbd_complete_aio(RADOSCB *rcb)
386
{
387
RBDAIOCB *acb = rcb->acb;
388
+ AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
389
int64_t r;
390
391
r = rcb->ret;
392
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
393
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
394
}
395
qemu_vfree(acb->bounce);
396
+
397
+ aio_context_acquire(ctx);
398
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
399
+ aio_context_release(ctx);
400
401
qemu_aio_unref(acb);
402
}
403
diff --git a/dma-helpers.c b/dma-helpers.c
404
index XXXXXXX..XXXXXXX 100644
405
--- a/dma-helpers.c
406
+++ b/dma-helpers.c
407
@@ -XXX,XX +XXX,XX @@ static void dma_blk_cb(void *opaque, int ret)
408
QEMU_ALIGN_DOWN(dbs->iov.size, dbs->align));
409
}
410
411
+ aio_context_acquire(dbs->ctx);
412
dbs->acb = dbs->io_func(dbs->offset, &dbs->iov,
413
dma_blk_cb, dbs, dbs->io_func_opaque);
414
+ aio_context_release(dbs->ctx);
415
assert(dbs->acb);
416
}
417
418
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
419
index XXXXXXX..XXXXXXX 100644
420
--- a/hw/block/virtio-blk.c
421
+++ b/hw/block/virtio-blk.c
422
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque)
423
424
s->rq = NULL;
425
426
+ aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
427
while (req) {
428
VirtIOBlockReq *next = req->next;
429
if (virtio_blk_handle_request(req, &mrb)) {
430
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_dma_restart_bh(void *opaque)
431
if (mrb.num_reqs) {
432
virtio_blk_submit_multireq(s->blk, &mrb);
433
}
434
+ aio_context_release(blk_get_aio_context(s->conf.conf.blk));
435
}
436
437
static void virtio_blk_dma_restart_cb(void *opaque, int running,
438
diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c
439
index XXXXXXX..XXXXXXX 100644
440
--- a/hw/scsi/scsi-bus.c
441
+++ b/hw/scsi/scsi-bus.c
442
@@ -XXX,XX +XXX,XX @@ static void scsi_dma_restart_bh(void *opaque)
443
qemu_bh_delete(s->bh);
444
s->bh = NULL;
445
446
+ aio_context_acquire(blk_get_aio_context(s->conf.blk));
447
QTAILQ_FOREACH_SAFE(req, &s->requests, next, next) {
448
scsi_req_ref(req);
449
if (req->retry) {
450
@@ -XXX,XX +XXX,XX @@ static void scsi_dma_restart_bh(void *opaque)
451
}
452
scsi_req_unref(req);
453
}
454
+ aio_context_release(blk_get_aio_context(s->conf.blk));
455
}
456
457
void scsi_req_retry(SCSIRequest *req)
458
diff --git a/util/async.c b/util/async.c
459
index XXXXXXX..XXXXXXX 100644
460
--- a/util/async.c
461
+++ b/util/async.c
462
@@ -XXX,XX +XXX,XX @@ int aio_bh_poll(AioContext *ctx)
463
ret = 1;
464
}
465
bh->idle = 0;
466
- aio_context_acquire(ctx);
467
aio_bh_call(bh);
468
- aio_context_release(ctx);
469
}
470
if (bh->deleted) {
471
deleted = true;
472
@@ -XXX,XX +XXX,XX @@ static void co_schedule_bh_cb(void *opaque)
473
Coroutine *co = QSLIST_FIRST(&straight);
474
QSLIST_REMOVE_HEAD(&straight, co_scheduled_next);
475
trace_aio_co_schedule_bh_cb(ctx, co);
476
+ aio_context_acquire(ctx);
477
qemu_coroutine_enter(co);
478
+ aio_context_release(ctx);
479
}
480
}
481
482
diff --git a/util/thread-pool.c b/util/thread-pool.c
483
index XXXXXXX..XXXXXXX 100644
484
--- a/util/thread-pool.c
485
+++ b/util/thread-pool.c
486
@@ -XXX,XX +XXX,XX @@ static void thread_pool_completion_bh(void *opaque)
487
ThreadPool *pool = opaque;
488
ThreadPoolElement *elem, *next;
489
490
+ aio_context_acquire(pool->ctx);
491
restart:
492
QLIST_FOREACH_SAFE(elem, &pool->head, all, next) {
493
if (elem->state != THREAD_DONE) {
494
@@ -XXX,XX +XXX,XX @@ restart:
495
qemu_aio_unref(elem);
496
}
497
}
498
+ aio_context_release(pool->ctx);
499
}
500
501
static void thread_pool_cancel(BlockAIOCB *acb)
60
--
502
--
61
2.26.2
503
2.9.3
62
504
63
505
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
There are several callers that need to create a new block backend from
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
an existing BDS; make the task slightly easier with a common helper
4
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5
routine.
5
Reviewed-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
7
Message-id: 20170213135235.12274-16-pbonzini@redhat.com
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
block/archipelago.c | 3 ---
11
block/block-backend.c | 7 -------
12
block/curl.c | 2 +-
13
block/io.c | 6 +-----
14
block/iscsi.c | 3 ---
15
block/linux-aio.c | 5 +----
16
block/mirror.c | 12 +++++++++---
17
block/null.c | 8 --------
18
block/qed-cluster.c | 2 ++
19
block/qed-table.c | 12 ++++++++++--
20
block/qed.c | 4 ++--
21
block/rbd.c | 4 ----
22
block/win32-aio.c | 3 ---
23
hw/block/virtio-blk.c | 12 +++++++++++-
24
hw/scsi/scsi-disk.c | 15 +++++++++++++++
25
hw/scsi/scsi-generic.c | 20 +++++++++++++++++---
26
util/thread-pool.c | 4 +++-
27
17 files changed, 72 insertions(+), 50 deletions(-)
6
28
7
Suggested-by: Max Reitz <mreitz@redhat.com>
29
diff --git a/block/archipelago.c b/block/archipelago.c
8
Signed-off-by: Eric Blake <eblake@redhat.com>
30
index XXXXXXX..XXXXXXX 100644
9
Message-Id: <20200424190903.522087-2-eblake@redhat.com>
31
--- a/block/archipelago.c
10
[mreitz: Set @ret only in error paths, see
32
+++ b/block/archipelago.c
11
https://lists.nongnu.org/archive/html/qemu-block/2020-04/msg01216.html]
33
@@ -XXX,XX +XXX,XX @@ static void qemu_archipelago_complete_aio(void *opaque)
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
34
{
13
Message-Id: <20200428192648.749066-2-eblake@redhat.com>
35
AIORequestData *reqdata = (AIORequestData *) opaque;
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
36
ArchipelagoAIOCB *aio_cb = (ArchipelagoAIOCB *) reqdata->aio_cb;
15
---
37
- AioContext *ctx = bdrv_get_aio_context(aio_cb->common.bs);
16
include/sysemu/block-backend.h | 2 ++
38
17
block/block-backend.c | 23 +++++++++++++++++++++++
39
- aio_context_acquire(ctx);
18
block/crypto.c | 9 ++++-----
40
aio_cb->common.cb(aio_cb->common.opaque, aio_cb->ret);
19
block/parallels.c | 8 ++++----
41
- aio_context_release(ctx);
20
block/qcow.c | 8 ++++----
42
aio_cb->status = 0;
21
block/qcow2.c | 18 ++++++++----------
43
22
block/qed.c | 8 ++++----
44
qemu_aio_unref(aio_cb);
23
block/sheepdog.c | 10 +++++-----
24
block/vdi.c | 8 ++++----
25
block/vhdx.c | 8 ++++----
26
block/vmdk.c | 9 ++++-----
27
block/vpc.c | 8 ++++----
28
blockdev.c | 8 +++-----
29
blockjob.c | 7 ++-----
30
14 files changed, 75 insertions(+), 59 deletions(-)
31
32
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
33
index XXXXXXX..XXXXXXX 100644
34
--- a/include/sysemu/block-backend.h
35
+++ b/include/sysemu/block-backend.h
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockBackendPublic {
37
} BlockBackendPublic;
38
39
BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm);
40
+BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm,
41
+ uint64_t shared_perm, Error **errp);
42
BlockBackend *blk_new_open(const char *filename, const char *reference,
43
QDict *options, int flags, Error **errp);
44
int blk_get_refcnt(BlockBackend *blk);
45
diff --git a/block/block-backend.c b/block/block-backend.c
45
diff --git a/block/block-backend.c b/block/block-backend.c
46
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
47
--- a/block/block-backend.c
47
--- a/block/block-backend.c
48
+++ b/block/block-backend.c
48
+++ b/block/block-backend.c
49
@@ -XXX,XX +XXX,XX @@ BlockBackend *blk_new(AioContext *ctx, uint64_t perm, uint64_t shared_perm)
49
@@ -XXX,XX +XXX,XX @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags)
50
return blk;
50
static void error_callback_bh(void *opaque)
51
}
51
{
52
52
struct BlockBackendAIOCB *acb = opaque;
53
+/*
53
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
54
+ * Create a new BlockBackend connected to an existing BlockDriverState.
54
55
+ *
55
bdrv_dec_in_flight(acb->common.bs);
56
+ * @perm is a bitmasks of BLK_PERM_* constants which describes the
56
- aio_context_acquire(ctx);
57
+ * permissions to request for @bs that is attached to this
57
acb->common.cb(acb->common.opaque, acb->ret);
58
+ * BlockBackend. @shared_perm is a bitmask which describes which
58
- aio_context_release(ctx);
59
+ * permissions may be granted to other users of the attached node.
59
qemu_aio_unref(acb);
60
+ * Both sets of permissions can be changed later using blk_set_perm().
60
}
61
+ *
61
62
+ * Return the new BlockBackend on success, null on failure.
62
@@ -XXX,XX +XXX,XX @@ static void blk_aio_complete(BlkAioEmAIOCB *acb)
63
+ */
63
static void blk_aio_complete_bh(void *opaque)
64
+BlockBackend *blk_new_with_bs(BlockDriverState *bs, uint64_t perm,
64
{
65
+ uint64_t shared_perm, Error **errp)
65
BlkAioEmAIOCB *acb = opaque;
66
+{
66
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
67
+ BlockBackend *blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm);
68
+
69
+ if (blk_insert_bs(blk, bs, errp) < 0) {
70
+ blk_unref(blk);
71
+ return NULL;
72
+ }
73
+ return blk;
74
+}
75
+
76
/*
77
* Creates a new BlockBackend, opens a new BlockDriverState, and connects both.
78
* The new BlockBackend is in the main AioContext.
79
diff --git a/block/crypto.c b/block/crypto.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/crypto.c
82
+++ b/block/crypto.c
83
@@ -XXX,XX +XXX,XX @@ static int block_crypto_co_create_generic(BlockDriverState *bs,
84
QCryptoBlock *crypto = NULL;
85
struct BlockCryptoCreateData data;
86
87
- blk = blk_new(bdrv_get_aio_context(bs),
88
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
89
-
67
-
90
- ret = blk_insert_bs(blk, bs, errp);
68
assert(acb->has_returned);
91
- if (ret < 0) {
69
- aio_context_acquire(ctx);
92
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
70
blk_aio_complete(acb);
93
+ errp);
71
- aio_context_release(ctx);
94
+ if (!blk) {
72
}
95
+ ret = -EPERM;
73
96
goto cleanup;
74
static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset, int bytes,
97
}
75
diff --git a/block/curl.c b/block/curl.c
98
76
index XXXXXXX..XXXXXXX 100644
99
diff --git a/block/parallels.c b/block/parallels.c
77
--- a/block/curl.c
100
index XXXXXXX..XXXXXXX 100644
78
+++ b/block/curl.c
101
--- a/block/parallels.c
79
@@ -XXX,XX +XXX,XX @@ static void curl_readv_bh_cb(void *p)
102
+++ b/block/parallels.c
80
curl_multi_socket_action(s->multi, CURL_SOCKET_TIMEOUT, 0, &running);
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
81
104
return -EIO;
82
out:
105
}
83
+ aio_context_release(ctx);
106
84
if (ret != -EINPROGRESS) {
107
- blk = blk_new(bdrv_get_aio_context(bs),
85
acb->common.cb(acb->common.opaque, ret);
108
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
86
qemu_aio_unref(acb);
109
- ret = blk_insert_bs(blk, bs, errp);
87
}
110
- if (ret < 0) {
88
- aio_context_release(ctx);
111
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
89
}
112
+ errp);
90
113
+ if (!blk) {
91
static BlockAIOCB *curl_aio_readv(BlockDriverState *bs,
114
+ ret = -EPERM;
92
diff --git a/block/io.c b/block/io.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/block/io.c
95
+++ b/block/io.c
96
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_io_em_complete(void *opaque, int ret)
97
CoroutineIOCompletion *co = opaque;
98
99
co->ret = ret;
100
- qemu_coroutine_enter(co->coroutine);
101
+ aio_co_wake(co->coroutine);
102
}
103
104
static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs,
105
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_complete(BlockAIOCBCoroutine *acb)
106
static void bdrv_co_em_bh(void *opaque)
107
{
108
BlockAIOCBCoroutine *acb = opaque;
109
- BlockDriverState *bs = acb->common.bs;
110
- AioContext *ctx = bdrv_get_aio_context(bs);
111
112
assert(!acb->need_bh);
113
- aio_context_acquire(ctx);
114
bdrv_co_complete(acb);
115
- aio_context_release(ctx);
116
}
117
118
static void bdrv_co_maybe_schedule_bh(BlockAIOCBCoroutine *acb)
119
diff --git a/block/iscsi.c b/block/iscsi.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/block/iscsi.c
122
+++ b/block/iscsi.c
123
@@ -XXX,XX +XXX,XX @@ static void
124
iscsi_bh_cb(void *p)
125
{
126
IscsiAIOCB *acb = p;
127
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
128
129
qemu_bh_delete(acb->bh);
130
131
g_free(acb->buf);
132
acb->buf = NULL;
133
134
- aio_context_acquire(ctx);
135
acb->common.cb(acb->common.opaque, acb->status);
136
- aio_context_release(ctx);
137
138
if (acb->task != NULL) {
139
scsi_free_scsi_task(acb->task);
140
diff --git a/block/linux-aio.c b/block/linux-aio.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/block/linux-aio.c
143
+++ b/block/linux-aio.c
144
@@ -XXX,XX +XXX,XX @@ static inline ssize_t io_event_ret(struct io_event *ev)
145
*/
146
static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
147
{
148
- LinuxAioState *s = laiocb->ctx;
149
int ret;
150
151
ret = laiocb->ret;
152
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
153
}
154
155
laiocb->ret = ret;
156
- aio_context_acquire(s->aio_context);
157
if (laiocb->co) {
158
/* If the coroutine is already entered it must be in ioq_submit() and
159
* will notice laio->ret has been filled in when it eventually runs
160
@@ -XXX,XX +XXX,XX @@ static void qemu_laio_process_completion(struct qemu_laiocb *laiocb)
161
* that!
162
*/
163
if (!qemu_coroutine_entered(laiocb->co)) {
164
- qemu_coroutine_enter(laiocb->co);
165
+ aio_co_wake(laiocb->co);
166
}
167
} else {
168
laiocb->common.cb(laiocb->common.opaque, ret);
169
qemu_aio_unref(laiocb);
170
}
171
- aio_context_release(s->aio_context);
172
}
173
174
/**
175
diff --git a/block/mirror.c b/block/mirror.c
176
index XXXXXXX..XXXXXXX 100644
177
--- a/block/mirror.c
178
+++ b/block/mirror.c
179
@@ -XXX,XX +XXX,XX @@ static void mirror_write_complete(void *opaque, int ret)
180
{
181
MirrorOp *op = opaque;
182
MirrorBlockJob *s = op->s;
183
+
184
+ aio_context_acquire(blk_get_aio_context(s->common.blk));
185
if (ret < 0) {
186
BlockErrorAction action;
187
188
@@ -XXX,XX +XXX,XX @@ static void mirror_write_complete(void *opaque, int ret)
189
}
190
}
191
mirror_iteration_done(op, ret);
192
+ aio_context_release(blk_get_aio_context(s->common.blk));
193
}
194
195
static void mirror_read_complete(void *opaque, int ret)
196
{
197
MirrorOp *op = opaque;
198
MirrorBlockJob *s = op->s;
199
+
200
+ aio_context_acquire(blk_get_aio_context(s->common.blk));
201
if (ret < 0) {
202
BlockErrorAction action;
203
204
@@ -XXX,XX +XXX,XX @@ static void mirror_read_complete(void *opaque, int ret)
205
}
206
207
mirror_iteration_done(op, ret);
208
- return;
209
+ } else {
210
+ blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov,
211
+ 0, mirror_write_complete, op);
212
}
213
- blk_aio_pwritev(s->target, op->sector_num * BDRV_SECTOR_SIZE, &op->qiov,
214
- 0, mirror_write_complete, op);
215
+ aio_context_release(blk_get_aio_context(s->common.blk));
216
}
217
218
static inline void mirror_clip_sectors(MirrorBlockJob *s,
219
diff --git a/block/null.c b/block/null.c
220
index XXXXXXX..XXXXXXX 100644
221
--- a/block/null.c
222
+++ b/block/null.c
223
@@ -XXX,XX +XXX,XX @@ static const AIOCBInfo null_aiocb_info = {
224
static void null_bh_cb(void *opaque)
225
{
226
NullAIOCB *acb = opaque;
227
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
228
-
229
- aio_context_acquire(ctx);
230
acb->common.cb(acb->common.opaque, 0);
231
- aio_context_release(ctx);
232
qemu_aio_unref(acb);
233
}
234
235
static void null_timer_cb(void *opaque)
236
{
237
NullAIOCB *acb = opaque;
238
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
239
-
240
- aio_context_acquire(ctx);
241
acb->common.cb(acb->common.opaque, 0);
242
- aio_context_release(ctx);
243
timer_deinit(&acb->timer);
244
qemu_aio_unref(acb);
245
}
246
diff --git a/block/qed-cluster.c b/block/qed-cluster.c
247
index XXXXXXX..XXXXXXX 100644
248
--- a/block/qed-cluster.c
249
+++ b/block/qed-cluster.c
250
@@ -XXX,XX +XXX,XX @@ static void qed_find_cluster_cb(void *opaque, int ret)
251
unsigned int index;
252
unsigned int n;
253
254
+ qed_acquire(s);
255
if (ret) {
115
goto out;
256
goto out;
116
}
257
}
117
blk_set_allow_write_beyond_eof(blk, true);
258
@@ -XXX,XX +XXX,XX @@ static void qed_find_cluster_cb(void *opaque, int ret)
118
diff --git a/block/qcow.c b/block/qcow.c
259
119
index XXXXXXX..XXXXXXX 100644
260
out:
120
--- a/block/qcow.c
261
find_cluster_cb->cb(find_cluster_cb->opaque, ret, offset, len);
121
+++ b/block/qcow.c
262
+ qed_release(s);
122
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
263
g_free(find_cluster_cb);
123
return -EIO;
264
}
124
}
265
125
266
diff --git a/block/qed-table.c b/block/qed-table.c
126
- qcow_blk = blk_new(bdrv_get_aio_context(bs),
267
index XXXXXXX..XXXXXXX 100644
127
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
268
--- a/block/qed-table.c
128
- ret = blk_insert_bs(qcow_blk, bs, errp);
269
+++ b/block/qed-table.c
129
- if (ret < 0) {
270
@@ -XXX,XX +XXX,XX @@ static void qed_read_table_cb(void *opaque, int ret)
130
+ qcow_blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE,
271
{
131
+ BLK_PERM_ALL, errp);
272
QEDReadTableCB *read_table_cb = opaque;
132
+ if (!qcow_blk) {
273
QEDTable *table = read_table_cb->table;
133
+ ret = -EPERM;
274
+ BDRVQEDState *s = read_table_cb->s;
134
goto exit;
275
int noffsets = read_table_cb->qiov.size / sizeof(uint64_t);
135
}
276
int i;
136
blk_set_allow_write_beyond_eof(qcow_blk, true);
277
137
diff --git a/block/qcow2.c b/block/qcow2.c
278
@@ -XXX,XX +XXX,XX @@ static void qed_read_table_cb(void *opaque, int ret)
138
index XXXXXXX..XXXXXXX 100644
279
}
139
--- a/block/qcow2.c
280
140
+++ b/block/qcow2.c
281
/* Byteswap offsets */
141
@@ -XXX,XX +XXX,XX @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp)
282
+ qed_acquire(s);
142
}
283
for (i = 0; i < noffsets; i++) {
143
284
table->offsets[i] = le64_to_cpu(table->offsets[i]);
144
/* Create BlockBackend to write to the image */
285
}
145
- blk = blk_new(bdrv_get_aio_context(bs),
286
+ qed_release(s);
146
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
287
147
- ret = blk_insert_bs(blk, bs, errp);
288
out:
148
- if (ret < 0) {
289
/* Completion */
149
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
290
- trace_qed_read_table_cb(read_table_cb->s, read_table_cb->table, ret);
150
+ errp);
291
+ trace_qed_read_table_cb(s, read_table_cb->table, ret);
151
+ if (!blk) {
292
gencb_complete(&read_table_cb->gencb, ret);
152
+ ret = -EPERM;
293
}
153
goto out;
294
154
}
295
@@ -XXX,XX +XXX,XX @@ typedef struct {
155
blk_set_allow_write_beyond_eof(blk, true);
296
static void qed_write_table_cb(void *opaque, int ret)
156
@@ -XXX,XX +XXX,XX @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts,
297
{
157
}
298
QEDWriteTableCB *write_table_cb = opaque;
158
299
+ BDRVQEDState *s = write_table_cb->s;
159
if (new_size) {
300
160
- BlockBackend *blk = blk_new(bdrv_get_aio_context(bs),
301
- trace_qed_write_table_cb(write_table_cb->s,
161
- BLK_PERM_RESIZE, BLK_PERM_ALL);
302
+ trace_qed_write_table_cb(s,
162
- ret = blk_insert_bs(blk, bs, errp);
303
write_table_cb->orig_table,
163
- if (ret < 0) {
304
write_table_cb->flush,
164
- blk_unref(blk);
305
ret);
165
- return ret;
306
@@ -XXX,XX +XXX,XX @@ static void qed_write_table_cb(void *opaque, int ret)
166
+ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL,
307
if (write_table_cb->flush) {
167
+ errp);
308
/* We still need to flush first */
168
+ if (!blk) {
309
write_table_cb->flush = false;
169
+ return -EPERM;
310
+ qed_acquire(s);
170
}
311
bdrv_aio_flush(write_table_cb->s->bs, qed_write_table_cb,
171
312
write_table_cb);
172
/*
313
+ qed_release(s);
314
return;
315
}
316
317
@@ -XXX,XX +XXX,XX @@ static void qed_read_l2_table_cb(void *opaque, int ret)
318
CachedL2Table *l2_table = request->l2_table;
319
uint64_t l2_offset = read_l2_table_cb->l2_offset;
320
321
+ qed_acquire(s);
322
if (ret) {
323
/* can't trust loaded L2 table anymore */
324
qed_unref_l2_cache_entry(l2_table);
325
@@ -XXX,XX +XXX,XX @@ static void qed_read_l2_table_cb(void *opaque, int ret)
326
request->l2_table = qed_find_l2_cache_entry(&s->l2_cache, l2_offset);
327
assert(request->l2_table != NULL);
328
}
329
+ qed_release(s);
330
331
gencb_complete(&read_l2_table_cb->gencb, ret);
332
}
173
diff --git a/block/qed.c b/block/qed.c
333
diff --git a/block/qed.c b/block/qed.c
174
index XXXXXXX..XXXXXXX 100644
334
index XXXXXXX..XXXXXXX 100644
175
--- a/block/qed.c
335
--- a/block/qed.c
176
+++ b/block/qed.c
336
+++ b/block/qed.c
177
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
337
@@ -XXX,XX +XXX,XX @@ static void qed_is_allocated_cb(void *opaque, int ret, uint64_t offset, size_t l
178
return -EIO;
338
}
179
}
339
180
340
if (cb->co) {
181
- blk = blk_new(bdrv_get_aio_context(bs),
341
- qemu_coroutine_enter(cb->co);
182
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
342
+ aio_co_wake(cb->co);
183
- ret = blk_insert_bs(blk, bs, errp);
343
}
184
- if (ret < 0) {
344
}
185
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
345
186
+ errp);
346
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qed_co_pwrite_zeroes_cb(void *opaque, int ret)
187
+ if (!blk) {
347
cb->done = true;
188
+ ret = -EPERM;
348
cb->ret = ret;
189
goto out;
349
if (cb->co) {
190
}
350
- qemu_coroutine_enter(cb->co);
191
blk_set_allow_write_beyond_eof(blk, true);
351
+ aio_co_wake(cb->co);
192
diff --git a/block/sheepdog.c b/block/sheepdog.c
352
}
193
index XXXXXXX..XXXXXXX 100644
353
}
194
--- a/block/sheepdog.c
354
195
+++ b/block/sheepdog.c
355
diff --git a/block/rbd.c b/block/rbd.c
196
@@ -XXX,XX +XXX,XX @@ static int sd_prealloc(BlockDriverState *bs, int64_t old_size, int64_t new_size,
356
index XXXXXXX..XXXXXXX 100644
197
void *buf = NULL;
357
--- a/block/rbd.c
198
int ret;
358
+++ b/block/rbd.c
199
359
@@ -XXX,XX +XXX,XX @@ shutdown:
200
- blk = blk_new(bdrv_get_aio_context(bs),
360
static void qemu_rbd_complete_aio(RADOSCB *rcb)
201
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
361
{
202
- BLK_PERM_ALL);
362
RBDAIOCB *acb = rcb->acb;
203
+ blk = blk_new_with_bs(bs,
363
- AioContext *ctx = bdrv_get_aio_context(acb->common.bs);
204
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
364
int64_t r;
205
+ BLK_PERM_ALL, errp);
365
206
366
r = rcb->ret;
207
- ret = blk_insert_bs(blk, bs, errp);
367
@@ -XXX,XX +XXX,XX @@ static void qemu_rbd_complete_aio(RADOSCB *rcb)
208
- if (ret < 0) {
368
qemu_iovec_from_buf(acb->qiov, 0, acb->bounce, acb->qiov->size);
209
+ if (!blk) {
369
}
210
+ ret = -EPERM;
370
qemu_vfree(acb->bounce);
211
goto out_with_err_set;
371
-
212
}
372
- aio_context_acquire(ctx);
213
373
acb->common.cb(acb->common.opaque, (acb->ret > 0 ? 0 : acb->ret));
214
diff --git a/block/vdi.c b/block/vdi.c
374
- aio_context_release(ctx);
215
index XXXXXXX..XXXXXXX 100644
375
216
--- a/block/vdi.c
376
qemu_aio_unref(acb);
217
+++ b/block/vdi.c
377
}
218
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
378
diff --git a/block/win32-aio.c b/block/win32-aio.c
219
goto exit;
379
index XXXXXXX..XXXXXXX 100644
220
}
380
--- a/block/win32-aio.c
221
381
+++ b/block/win32-aio.c
222
- blk = blk_new(bdrv_get_aio_context(bs_file),
382
@@ -XXX,XX +XXX,XX @@ static void win32_aio_process_completion(QEMUWin32AIOState *s,
223
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
383
qemu_vfree(waiocb->buf);
224
- ret = blk_insert_bs(blk, bs_file, errp);
384
}
225
- if (ret < 0) {
385
226
+ blk = blk_new_with_bs(bs_file, BLK_PERM_WRITE | BLK_PERM_RESIZE,
386
-
227
+ BLK_PERM_ALL, errp);
387
- aio_context_acquire(s->aio_ctx);
228
+ if (!blk) {
388
waiocb->common.cb(waiocb->common.opaque, ret);
229
+ ret = -EPERM;
389
- aio_context_release(s->aio_ctx);
230
goto exit;
390
qemu_aio_unref(waiocb);
231
}
391
}
232
392
233
diff --git a/block/vhdx.c b/block/vhdx.c
393
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
234
index XXXXXXX..XXXXXXX 100644
394
index XXXXXXX..XXXXXXX 100644
235
--- a/block/vhdx.c
395
--- a/hw/block/virtio-blk.c
236
+++ b/block/vhdx.c
396
+++ b/hw/block/virtio-blk.c
237
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
397
@@ -XXX,XX +XXX,XX @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error,
238
return -EIO;
398
static void virtio_blk_rw_complete(void *opaque, int ret)
239
}
399
{
240
400
VirtIOBlockReq *next = opaque;
241
- blk = blk_new(bdrv_get_aio_context(bs),
401
+ VirtIOBlock *s = next->dev;
242
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
402
243
- ret = blk_insert_bs(blk, bs, errp);
403
+ aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
244
- if (ret < 0) {
404
while (next) {
245
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
405
VirtIOBlockReq *req = next;
246
+ errp);
406
next = req->mr_next;
247
+ if (!blk) {
407
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_rw_complete(void *opaque, int ret)
248
+ ret = -EPERM;
408
block_acct_done(blk_get_stats(req->dev->blk), &req->acct);
249
goto delete_and_exit;
409
virtio_blk_free_request(req);
250
}
410
}
251
blk_set_allow_write_beyond_eof(blk, true);
411
+ aio_context_release(blk_get_aio_context(s->conf.conf.blk));
252
diff --git a/block/vmdk.c b/block/vmdk.c
412
}
253
index XXXXXXX..XXXXXXX 100644
413
254
--- a/block/vmdk.c
414
static void virtio_blk_flush_complete(void *opaque, int ret)
255
+++ b/block/vmdk.c
415
{
256
@@ -XXX,XX +XXX,XX @@ static BlockBackend *vmdk_co_create_cb(int64_t size, int idx,
416
VirtIOBlockReq *req = opaque;
257
if (!bs) {
417
+ VirtIOBlock *s = req->dev;
258
return NULL;
418
259
}
419
+ aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
260
- blk = blk_new(bdrv_get_aio_context(bs),
420
if (ret) {
261
- BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
421
if (virtio_blk_handle_rw_error(req, -ret, 0)) {
262
- BLK_PERM_ALL);
422
- return;
263
- if (blk_insert_bs(blk, bs, errp)) {
423
+ goto out;
264
- bdrv_unref(bs);
424
}
265
+ blk = blk_new_with_bs(bs,
425
}
266
+ BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE,
426
267
+ BLK_PERM_ALL, errp);
427
virtio_blk_req_complete(req, VIRTIO_BLK_S_OK);
268
+ if (!blk) {
428
block_acct_done(blk_get_stats(req->dev->blk), &req->acct);
269
return NULL;
429
virtio_blk_free_request(req);
270
}
430
+
271
blk_set_allow_write_beyond_eof(blk, true);
431
+out:
272
diff --git a/block/vpc.c b/block/vpc.c
432
+ aio_context_release(blk_get_aio_context(s->conf.conf.blk));
273
index XXXXXXX..XXXXXXX 100644
433
}
274
--- a/block/vpc.c
434
275
+++ b/block/vpc.c
435
#ifdef __linux__
276
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
436
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_ioctl_complete(void *opaque, int status)
277
return -EIO;
437
virtio_stl_p(vdev, &scsi->data_len, hdr->dxfer_len);
278
}
279
280
- blk = blk_new(bdrv_get_aio_context(bs),
281
- BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
282
- ret = blk_insert_bs(blk, bs, errp);
283
- if (ret < 0) {
284
+ blk = blk_new_with_bs(bs, BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL,
285
+ errp);
286
+ if (!blk) {
287
+ ret = -EPERM;
288
goto out;
289
}
290
blk_set_allow_write_beyond_eof(blk, true);
291
diff --git a/blockdev.c b/blockdev.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockdev.c
294
+++ b/blockdev.c
295
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
296
BlockBackend *blk = NULL;
297
BlockDriverState *bs;
298
AioContext *aio_context;
299
- int ret;
300
301
bs = bdrv_lookup_bs(has_device ? device : NULL,
302
has_node_name ? node_name : NULL,
303
@@ -XXX,XX +XXX,XX @@ void qmp_block_resize(bool has_device, const char *device,
304
goto out;
305
}
306
307
- blk = blk_new(bdrv_get_aio_context(bs), BLK_PERM_RESIZE, BLK_PERM_ALL);
308
- ret = blk_insert_bs(blk, bs, errp);
309
- if (ret < 0) {
310
+ blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL, errp);
311
+ if (!blk) {
312
goto out;
313
}
314
315
bdrv_drained_begin(bs);
316
- ret = blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
317
+ blk_truncate(blk, size, false, PREALLOC_MODE_OFF, 0, errp);
318
bdrv_drained_end(bs);
319
438
320
out:
439
out:
321
diff --git a/blockjob.c b/blockjob.c
440
+ aio_context_acquire(blk_get_aio_context(s->conf.conf.blk));
322
index XXXXXXX..XXXXXXX 100644
441
virtio_blk_req_complete(req, status);
323
--- a/blockjob.c
442
virtio_blk_free_request(req);
324
+++ b/blockjob.c
443
+ aio_context_release(blk_get_aio_context(s->conf.conf.blk));
325
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
444
g_free(ioctl_req);
326
{
445
}
327
BlockBackend *blk;
446
328
BlockJob *job;
447
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
329
- int ret;
448
index XXXXXXX..XXXXXXX 100644
330
449
--- a/hw/scsi/scsi-disk.c
331
if (job_id == NULL && !(flags & JOB_INTERNAL)) {
450
+++ b/hw/scsi/scsi-disk.c
332
job_id = bdrv_get_device_name(bs);
451
@@ -XXX,XX +XXX,XX @@ static void scsi_aio_complete(void *opaque, int ret)
333
}
452
334
453
assert(r->req.aiocb != NULL);
335
- blk = blk_new(bdrv_get_aio_context(bs), perm, shared_perm);
454
r->req.aiocb = NULL;
336
- ret = blk_insert_bs(blk, bs, errp);
455
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
337
- if (ret < 0) {
456
if (scsi_disk_req_check_error(r, ret, true)) {
338
- blk_unref(blk);
457
goto done;
339
+ blk = blk_new_with_bs(bs, perm, shared_perm, errp);
458
}
340
+ if (!blk) {
459
@@ -XXX,XX +XXX,XX @@ static void scsi_aio_complete(void *opaque, int ret)
341
return NULL;
460
scsi_req_complete(&r->req, GOOD);
342
}
461
343
462
done:
463
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
464
scsi_req_unref(&r->req);
465
}
466
467
@@ -XXX,XX +XXX,XX @@ static void scsi_dma_complete(void *opaque, int ret)
468
assert(r->req.aiocb != NULL);
469
r->req.aiocb = NULL;
470
471
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
472
if (ret < 0) {
473
block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
474
} else {
475
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
476
}
477
scsi_dma_complete_noio(r, ret);
478
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
479
}
480
481
static void scsi_read_complete(void * opaque, int ret)
482
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
483
484
assert(r->req.aiocb != NULL);
485
r->req.aiocb = NULL;
486
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
487
if (scsi_disk_req_check_error(r, ret, true)) {
488
goto done;
489
}
490
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
491
492
done:
493
scsi_req_unref(&r->req);
494
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
495
}
496
497
/* Actually issue a read to the block device. */
498
@@ -XXX,XX +XXX,XX @@ static void scsi_do_read_cb(void *opaque, int ret)
499
assert (r->req.aiocb != NULL);
500
r->req.aiocb = NULL;
501
502
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
503
if (ret < 0) {
504
block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
505
} else {
506
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
507
}
508
scsi_do_read(opaque, ret);
509
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
510
}
511
512
/* Read more data from scsi device into buffer. */
513
@@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret)
514
assert (r->req.aiocb != NULL);
515
r->req.aiocb = NULL;
516
517
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
518
if (ret < 0) {
519
block_acct_failed(blk_get_stats(s->qdev.conf.blk), &r->acct);
520
} else {
521
block_acct_done(blk_get_stats(s->qdev.conf.blk), &r->acct);
522
}
523
scsi_write_complete_noio(r, ret);
524
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
525
}
526
527
static void scsi_write_data(SCSIRequest *req)
528
@@ -XXX,XX +XXX,XX @@ static void scsi_unmap_complete(void *opaque, int ret)
529
{
530
UnmapCBData *data = opaque;
531
SCSIDiskReq *r = data->r;
532
+ SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev);
533
534
assert(r->req.aiocb != NULL);
535
r->req.aiocb = NULL;
536
537
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
538
scsi_unmap_complete_noio(data, ret);
539
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
540
}
541
542
static void scsi_disk_emulate_unmap(SCSIDiskReq *r, uint8_t *inbuf)
543
@@ -XXX,XX +XXX,XX @@ static void scsi_write_same_complete(void *opaque, int ret)
544
545
assert(r->req.aiocb != NULL);
546
r->req.aiocb = NULL;
547
+ aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk));
548
if (scsi_disk_req_check_error(r, ret, true)) {
549
goto done;
550
}
551
@@ -XXX,XX +XXX,XX @@ done:
552
scsi_req_unref(&r->req);
553
qemu_vfree(data->iov.iov_base);
554
g_free(data);
555
+ aio_context_release(blk_get_aio_context(s->qdev.conf.blk));
556
}
557
558
static void scsi_disk_emulate_write_same(SCSIDiskReq *r, uint8_t *inbuf)
559
diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c
560
index XXXXXXX..XXXXXXX 100644
561
--- a/hw/scsi/scsi-generic.c
562
+++ b/hw/scsi/scsi-generic.c
563
@@ -XXX,XX +XXX,XX @@ done:
564
static void scsi_command_complete(void *opaque, int ret)
565
{
566
SCSIGenericReq *r = (SCSIGenericReq *)opaque;
567
+ SCSIDevice *s = r->req.dev;
568
569
assert(r->req.aiocb != NULL);
570
r->req.aiocb = NULL;
571
+
572
+ aio_context_acquire(blk_get_aio_context(s->conf.blk));
573
scsi_command_complete_noio(r, ret);
574
+ aio_context_release(blk_get_aio_context(s->conf.blk));
575
}
576
577
static int execute_command(BlockBackend *blk,
578
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
579
assert(r->req.aiocb != NULL);
580
r->req.aiocb = NULL;
581
582
+ aio_context_acquire(blk_get_aio_context(s->conf.blk));
583
+
584
if (ret || r->req.io_canceled) {
585
scsi_command_complete_noio(r, ret);
586
- return;
587
+ goto done;
588
}
589
590
len = r->io_header.dxfer_len - r->io_header.resid;
591
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
592
r->len = -1;
593
if (len == 0) {
594
scsi_command_complete_noio(r, 0);
595
- return;
596
+ goto done;
597
}
598
599
/* Snoop READ CAPACITY output to set the blocksize. */
600
@@ -XXX,XX +XXX,XX @@ static void scsi_read_complete(void * opaque, int ret)
601
}
602
scsi_req_data(&r->req, len);
603
scsi_req_unref(&r->req);
604
+
605
+done:
606
+ aio_context_release(blk_get_aio_context(s->conf.blk));
607
}
608
609
/* Read more data from scsi device into buffer. */
610
@@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret)
611
assert(r->req.aiocb != NULL);
612
r->req.aiocb = NULL;
613
614
+ aio_context_acquire(blk_get_aio_context(s->conf.blk));
615
+
616
if (ret || r->req.io_canceled) {
617
scsi_command_complete_noio(r, ret);
618
- return;
619
+ goto done;
620
}
621
622
if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 &&
623
@@ -XXX,XX +XXX,XX @@ static void scsi_write_complete(void * opaque, int ret)
624
}
625
626
scsi_command_complete_noio(r, ret);
627
+
628
+done:
629
+ aio_context_release(blk_get_aio_context(s->conf.blk));
630
}
631
632
/* Write data to a scsi device. Returns nonzero on failure.
633
diff --git a/util/thread-pool.c b/util/thread-pool.c
634
index XXXXXXX..XXXXXXX 100644
635
--- a/util/thread-pool.c
636
+++ b/util/thread-pool.c
637
@@ -XXX,XX +XXX,XX @@ restart:
638
*/
639
qemu_bh_schedule(pool->completion_bh);
640
641
+ aio_context_release(pool->ctx);
642
elem->common.cb(elem->common.opaque, elem->ret);
643
+ aio_context_acquire(pool->ctx);
644
qemu_aio_unref(elem);
645
goto restart;
646
} else {
647
@@ -XXX,XX +XXX,XX @@ static void thread_pool_co_cb(void *opaque, int ret)
648
ThreadPoolCo *co = opaque;
649
650
co->ret = ret;
651
- qemu_coroutine_enter(co->co);
652
+ aio_co_wake(co->co);
653
}
654
655
int coroutine_fn thread_pool_submit_co(ThreadPool *pool, ThreadPoolFunc *func,
344
--
656
--
345
2.26.2
657
2.9.3
346
658
347
659
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We are going to use aio-task-pool API and extend in-flight request
3
This patch prepares for the removal of unnecessary lockcnt inc/dec pairs.
4
structure to be a successor of AioTask, so rename things appropriately.
4
Extract the dispatching loop for file descriptor handlers into a new
5
function aio_dispatch_handlers, and then inline aio_dispatch into
6
aio_poll.
5
7
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
aio_dispatch can now become void.
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
8
Message-Id: <20200429130847.28124-2-vsementsov@virtuozzo.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
12
Reviewed-by: Fam Zheng <famz@redhat.com>
13
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
14
Message-id: 20170213135235.12274-17-pbonzini@redhat.com
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
16
---
11
block/block-copy.c | 98 +++++++++++++++++++++++-----------------------
17
include/block/aio.h | 6 +-----
12
1 file changed, 48 insertions(+), 50 deletions(-)
18
util/aio-posix.c | 44 ++++++++++++++------------------------------
19
util/aio-win32.c | 13 ++++---------
20
util/async.c | 2 +-
21
4 files changed, 20 insertions(+), 45 deletions(-)
13
22
14
diff --git a/block/block-copy.c b/block/block-copy.c
23
diff --git a/include/block/aio.h b/include/block/aio.h
15
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-copy.c
25
--- a/include/block/aio.h
17
+++ b/block/block-copy.c
26
+++ b/include/block/aio.h
18
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@ bool aio_pending(AioContext *ctx);
19
#define BLOCK_COPY_MAX_BUFFER (1 * MiB)
28
/* Dispatch any pending callbacks from the GSource attached to the AioContext.
20
#define BLOCK_COPY_MAX_MEM (128 * MiB)
29
*
21
30
* This is used internally in the implementation of the GSource.
22
-typedef struct BlockCopyInFlightReq {
31
- *
23
+typedef struct BlockCopyTask {
32
- * @dispatch_fds: true to process fds, false to skip them
24
int64_t offset;
33
- * (can be used as an optimization by callers that know there
25
int64_t bytes;
34
- * are no fds ready)
26
- QLIST_ENTRY(BlockCopyInFlightReq) list;
35
*/
27
- CoQueue wait_queue; /* coroutines blocked on this request */
36
-bool aio_dispatch(AioContext *ctx, bool dispatch_fds);
28
-} BlockCopyInFlightReq;
37
+void aio_dispatch(AioContext *ctx);
29
+ QLIST_ENTRY(BlockCopyTask) list;
38
30
+ CoQueue wait_queue; /* coroutines blocked on this task */
39
/* Progress in completing AIO work to occur. This can issue new pending
31
+} BlockCopyTask;
40
* aio as a result of executing I/O completion or bh callbacks.
32
41
diff --git a/util/aio-posix.c b/util/aio-posix.c
33
typedef struct BlockCopyState {
42
index XXXXXXX..XXXXXXX 100644
34
/*
43
--- a/util/aio-posix.c
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
44
+++ b/util/aio-posix.c
36
bool use_copy_range;
45
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
37
int64_t copy_size;
46
AioHandler *node, *tmp;
38
uint64_t len;
47
bool progress = false;
39
- QLIST_HEAD(, BlockCopyInFlightReq) inflight_reqs;
48
40
+ QLIST_HEAD(, BlockCopyTask) tasks;
49
- /*
41
50
- * We have to walk very carefully in case aio_set_fd_handler is
42
BdrvRequestFlags write_flags;
51
- * called while we're walking.
43
52
- */
44
@@ -XXX,XX +XXX,XX @@ typedef struct BlockCopyState {
53
- qemu_lockcnt_inc(&ctx->list_lock);
45
SharedResource *mem;
54
-
46
} BlockCopyState;
55
QLIST_FOREACH_SAFE_RCU(node, &ctx->aio_handlers, node, tmp) {
47
56
int revents;
48
-static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
57
49
- int64_t offset,
58
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
50
- int64_t bytes)
51
+static BlockCopyTask *find_conflicting_task(BlockCopyState *s,
52
+ int64_t offset, int64_t bytes)
53
{
54
- BlockCopyInFlightReq *req;
55
+ BlockCopyTask *t;
56
57
- QLIST_FOREACH(req, &s->inflight_reqs, list) {
58
- if (offset + bytes > req->offset && offset < req->offset + req->bytes) {
59
- return req;
60
+ QLIST_FOREACH(t, &s->tasks, list) {
61
+ if (offset + bytes > t->offset && offset < t->offset + t->bytes) {
62
+ return t;
63
}
59
}
64
}
60
}
65
61
66
@@ -XXX,XX +XXX,XX @@ static BlockCopyInFlightReq *find_conflicting_inflight_req(BlockCopyState *s,
62
- qemu_lockcnt_dec(&ctx->list_lock);
63
return progress;
67
}
64
}
68
65
69
/*
66
-/*
70
- * If there are no intersecting requests return false. Otherwise, wait for the
67
- * Note that dispatch_fds == false has the side-effect of post-poning the
71
- * first found intersecting request to finish and return true.
68
- * freeing of deleted handlers.
72
+ * If there are no intersecting tasks return false. Otherwise, wait for the
69
- */
73
+ * first found intersecting tasks to finish and return true.
70
-bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
74
*/
71
+void aio_dispatch(AioContext *ctx)
75
static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
76
int64_t bytes)
77
{
72
{
78
- BlockCopyInFlightReq *req = find_conflicting_inflight_req(s, offset, bytes);
73
- bool progress;
79
+ BlockCopyTask *task = find_conflicting_task(s, offset, bytes);
74
+ aio_bh_poll(ctx);
80
75
81
- if (!req) {
76
- /*
82
+ if (!task) {
77
- * If there are callbacks left that have been queued, we need to call them.
83
return false;
78
- * Do not call select in this case, because it is possible that the caller
79
- * does not need a complete flush (as is the case for aio_poll loops).
80
- */
81
- progress = aio_bh_poll(ctx);
82
+ qemu_lockcnt_inc(&ctx->list_lock);
83
+ aio_dispatch_handlers(ctx);
84
+ qemu_lockcnt_dec(&ctx->list_lock);
85
86
- if (dispatch_fds) {
87
- progress |= aio_dispatch_handlers(ctx);
88
- }
89
-
90
- /* Run our timers */
91
- progress |= timerlistgroup_run_timers(&ctx->tlg);
92
-
93
- return progress;
94
+ timerlistgroup_run_timers(&ctx->tlg);
95
}
96
97
/* These thread-local variables are used only in a small part of aio_poll
98
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
99
npfd = 0;
100
qemu_lockcnt_dec(&ctx->list_lock);
101
102
- /* Run dispatch even if there were no readable fds to run timers */
103
- if (aio_dispatch(ctx, ret > 0)) {
104
- progress = true;
105
+ progress |= aio_bh_poll(ctx);
106
+
107
+ if (ret > 0) {
108
+ qemu_lockcnt_inc(&ctx->list_lock);
109
+ progress |= aio_dispatch_handlers(ctx);
110
+ qemu_lockcnt_dec(&ctx->list_lock);
84
}
111
}
85
112
86
- qemu_co_queue_wait(&req->wait_queue, NULL);
113
+ progress |= timerlistgroup_run_timers(&ctx->tlg);
87
+ qemu_co_queue_wait(&task->wait_queue, NULL);
114
+
88
115
return progress;
116
}
117
118
diff --git a/util/aio-win32.c b/util/aio-win32.c
119
index XXXXXXX..XXXXXXX 100644
120
--- a/util/aio-win32.c
121
+++ b/util/aio-win32.c
122
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
123
return progress;
124
}
125
126
-bool aio_dispatch(AioContext *ctx, bool dispatch_fds)
127
+void aio_dispatch(AioContext *ctx)
128
{
129
- bool progress;
130
-
131
- progress = aio_bh_poll(ctx);
132
- if (dispatch_fds) {
133
- progress |= aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
134
- }
135
- progress |= timerlistgroup_run_timers(&ctx->tlg);
136
- return progress;
137
+ aio_bh_poll(ctx);
138
+ aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
139
+ timerlistgroup_run_timers(&ctx->tlg);
140
}
141
142
bool aio_poll(AioContext *ctx, bool blocking)
143
diff --git a/util/async.c b/util/async.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/util/async.c
146
+++ b/util/async.c
147
@@ -XXX,XX +XXX,XX @@ aio_ctx_dispatch(GSource *source,
148
AioContext *ctx = (AioContext *) source;
149
150
assert(callback == NULL);
151
- aio_dispatch(ctx, true);
152
+ aio_dispatch(ctx);
89
return true;
153
return true;
90
}
154
}
91
155
92
/* Called only on full-dirty region */
93
-static void block_copy_inflight_req_begin(BlockCopyState *s,
94
- BlockCopyInFlightReq *req,
95
- int64_t offset, int64_t bytes)
96
+static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
97
+ int64_t offset, int64_t bytes)
98
{
99
- assert(!find_conflicting_inflight_req(s, offset, bytes));
100
+ assert(!find_conflicting_task(s, offset, bytes));
101
102
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
103
s->in_flight_bytes += bytes;
104
105
- req->offset = offset;
106
- req->bytes = bytes;
107
- qemu_co_queue_init(&req->wait_queue);
108
- QLIST_INSERT_HEAD(&s->inflight_reqs, req, list);
109
+ task->offset = offset;
110
+ task->bytes = bytes;
111
+ qemu_co_queue_init(&task->wait_queue);
112
+ QLIST_INSERT_HEAD(&s->tasks, task, list);
113
}
114
115
/*
116
- * block_copy_inflight_req_shrink
117
+ * block_copy_task_shrink
118
*
119
- * Drop the tail of the request to be handled later. Set dirty bits back and
120
- * wake up all requests waiting for us (may be some of them are not intersecting
121
- * with shrunk request)
122
+ * Drop the tail of the task to be handled later. Set dirty bits back and
123
+ * wake up all tasks waiting for us (may be some of them are not intersecting
124
+ * with shrunk task)
125
*/
126
-static void coroutine_fn block_copy_inflight_req_shrink(BlockCopyState *s,
127
- BlockCopyInFlightReq *req, int64_t new_bytes)
128
+static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
129
+ BlockCopyTask *task,
130
+ int64_t new_bytes)
131
{
132
- if (new_bytes == req->bytes) {
133
+ if (new_bytes == task->bytes) {
134
return;
135
}
136
137
- assert(new_bytes > 0 && new_bytes < req->bytes);
138
+ assert(new_bytes > 0 && new_bytes < task->bytes);
139
140
- s->in_flight_bytes -= req->bytes - new_bytes;
141
+ s->in_flight_bytes -= task->bytes - new_bytes;
142
bdrv_set_dirty_bitmap(s->copy_bitmap,
143
- req->offset + new_bytes, req->bytes - new_bytes);
144
+ task->offset + new_bytes, task->bytes - new_bytes);
145
146
- req->bytes = new_bytes;
147
- qemu_co_queue_restart_all(&req->wait_queue);
148
+ task->bytes = new_bytes;
149
+ qemu_co_queue_restart_all(&task->wait_queue);
150
}
151
152
-static void coroutine_fn block_copy_inflight_req_end(BlockCopyState *s,
153
- BlockCopyInFlightReq *req,
154
- int ret)
155
+static void coroutine_fn block_copy_task_end(BlockCopyState *s,
156
+ BlockCopyTask *task, int ret)
157
{
158
- s->in_flight_bytes -= req->bytes;
159
+ s->in_flight_bytes -= task->bytes;
160
if (ret < 0) {
161
- bdrv_set_dirty_bitmap(s->copy_bitmap, req->offset, req->bytes);
162
+ bdrv_set_dirty_bitmap(s->copy_bitmap, task->offset, task->bytes);
163
}
164
- QLIST_REMOVE(req, list);
165
- qemu_co_queue_restart_all(&req->wait_queue);
166
+ QLIST_REMOVE(task, list);
167
+ qemu_co_queue_restart_all(&task->wait_queue);
168
}
169
170
void block_copy_state_free(BlockCopyState *s)
171
@@ -XXX,XX +XXX,XX @@ BlockCopyState *block_copy_state_new(BdrvChild *source, BdrvChild *target,
172
s->copy_size = MAX(s->cluster_size, BLOCK_COPY_MAX_BUFFER);
173
}
174
175
- QLIST_INIT(&s->inflight_reqs);
176
+ QLIST_INIT(&s->tasks);
177
178
return s;
179
}
180
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
181
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
182
183
while (bytes) {
184
- BlockCopyInFlightReq req;
185
+ BlockCopyTask task;
186
int64_t next_zero, cur_bytes, status_bytes;
187
188
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
189
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
190
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
191
cur_bytes = next_zero - offset;
192
}
193
- block_copy_inflight_req_begin(s, &req, offset, cur_bytes);
194
+ block_copy_task_begin(s, &task, offset, cur_bytes);
195
196
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
197
assert(ret >= 0); /* never fail */
198
cur_bytes = MIN(cur_bytes, status_bytes);
199
- block_copy_inflight_req_shrink(s, &req, cur_bytes);
200
+ block_copy_task_shrink(s, &task, cur_bytes);
201
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
202
- block_copy_inflight_req_end(s, &req, 0);
203
+ block_copy_task_end(s, &task, 0);
204
progress_set_remaining(s->progress,
205
bdrv_get_dirty_count(s->copy_bitmap) +
206
s->in_flight_bytes);
207
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
208
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
209
error_is_read);
210
co_put_to_shres(s->mem, cur_bytes);
211
- block_copy_inflight_req_end(s, &req, ret);
212
+ block_copy_task_end(s, &task, ret);
213
if (ret < 0) {
214
return ret;
215
}
216
--
156
--
217
2.26.2
157
2.9.3
218
158
219
159
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Our comment did not actually match the code. Rewrite the comment to
3
Pull the increment/decrement pair out of aio_bh_poll and into the
4
be less sensitive to any future changes to qcow2-bitmap.c that might
4
callers.
5
implement scenarios that we currently reject.
6
5
7
Signed-off-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
9
Message-Id: <20200428192648.749066-4-eblake@redhat.com>
8
Reviewed-by: Fam Zheng <famz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
10
Message-id: 20170213135235.12274-18-pbonzini@redhat.com
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
---
12
block/qcow2.c | 2 +-
13
util/aio-posix.c | 8 +++-----
13
1 file changed, 1 insertion(+), 1 deletion(-)
14
util/aio-win32.c | 8 ++++----
15
util/async.c | 12 ++++++------
16
3 files changed, 13 insertions(+), 15 deletions(-)
14
17
15
diff --git a/block/qcow2.c b/block/qcow2.c
18
diff --git a/util/aio-posix.c b/util/aio-posix.c
16
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
17
--- a/block/qcow2.c
20
--- a/util/aio-posix.c
18
+++ b/block/qcow2.c
21
+++ b/util/aio-posix.c
19
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
22
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx)
20
goto fail;
23
24
void aio_dispatch(AioContext *ctx)
25
{
26
+ qemu_lockcnt_inc(&ctx->list_lock);
27
aio_bh_poll(ctx);
28
-
29
- qemu_lockcnt_inc(&ctx->list_lock);
30
aio_dispatch_handlers(ctx);
31
qemu_lockcnt_dec(&ctx->list_lock);
32
33
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
21
}
34
}
22
35
23
- /* cannot proceed if image has bitmaps */
36
npfd = 0;
24
+ /* See qcow2-bitmap.c for which bitmap scenarios prevent a resize. */
37
- qemu_lockcnt_dec(&ctx->list_lock);
25
if (qcow2_truncate_bitmaps_check(bs, errp)) {
38
26
ret = -ENOTSUP;
39
progress |= aio_bh_poll(ctx);
27
goto fail;
40
41
if (ret > 0) {
42
- qemu_lockcnt_inc(&ctx->list_lock);
43
progress |= aio_dispatch_handlers(ctx);
44
- qemu_lockcnt_dec(&ctx->list_lock);
45
}
46
47
+ qemu_lockcnt_dec(&ctx->list_lock);
48
+
49
progress |= timerlistgroup_run_timers(&ctx->tlg);
50
51
return progress;
52
diff --git a/util/aio-win32.c b/util/aio-win32.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/util/aio-win32.c
55
+++ b/util/aio-win32.c
56
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
57
bool progress = false;
58
AioHandler *tmp;
59
60
- qemu_lockcnt_inc(&ctx->list_lock);
61
-
62
/*
63
* We have to walk very carefully in case aio_set_fd_handler is
64
* called while we're walking.
65
@@ -XXX,XX +XXX,XX @@ static bool aio_dispatch_handlers(AioContext *ctx, HANDLE event)
66
}
67
}
68
69
- qemu_lockcnt_dec(&ctx->list_lock);
70
return progress;
71
}
72
73
void aio_dispatch(AioContext *ctx)
74
{
75
+ qemu_lockcnt_inc(&ctx->list_lock);
76
aio_bh_poll(ctx);
77
aio_dispatch_handlers(ctx, INVALID_HANDLE_VALUE);
78
+ qemu_lockcnt_dec(&ctx->list_lock);
79
timerlistgroup_run_timers(&ctx->tlg);
80
}
81
82
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
83
}
84
}
85
86
- qemu_lockcnt_dec(&ctx->list_lock);
87
first = true;
88
89
/* ctx->notifier is always registered. */
90
@@ -XXX,XX +XXX,XX @@ bool aio_poll(AioContext *ctx, bool blocking)
91
progress |= aio_dispatch_handlers(ctx, event);
92
} while (count > 0);
93
94
+ qemu_lockcnt_dec(&ctx->list_lock);
95
+
96
progress |= timerlistgroup_run_timers(&ctx->tlg);
97
return progress;
98
}
99
diff --git a/util/async.c b/util/async.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/util/async.c
102
+++ b/util/async.c
103
@@ -XXX,XX +XXX,XX @@ void aio_bh_call(QEMUBH *bh)
104
bh->cb(bh->opaque);
105
}
106
107
-/* Multiple occurrences of aio_bh_poll cannot be called concurrently */
108
+/* Multiple occurrences of aio_bh_poll cannot be called concurrently.
109
+ * The count in ctx->list_lock is incremented before the call, and is
110
+ * not affected by the call.
111
+ */
112
int aio_bh_poll(AioContext *ctx)
113
{
114
QEMUBH *bh, **bhp, *next;
115
int ret;
116
bool deleted = false;
117
118
- qemu_lockcnt_inc(&ctx->list_lock);
119
-
120
ret = 0;
121
for (bh = atomic_rcu_read(&ctx->first_bh); bh; bh = next) {
122
next = atomic_rcu_read(&bh->next);
123
@@ -XXX,XX +XXX,XX @@ int aio_bh_poll(AioContext *ctx)
124
125
/* remove deleted bhs */
126
if (!deleted) {
127
- qemu_lockcnt_dec(&ctx->list_lock);
128
return ret;
129
}
130
131
- if (qemu_lockcnt_dec_and_lock(&ctx->list_lock)) {
132
+ if (qemu_lockcnt_dec_if_lock(&ctx->list_lock)) {
133
bhp = &ctx->first_bh;
134
while (*bhp) {
135
bh = *bhp;
136
@@ -XXX,XX +XXX,XX @@ int aio_bh_poll(AioContext *ctx)
137
bhp = &bh->next;
138
}
139
}
140
- qemu_lockcnt_unlock(&ctx->list_lock);
141
+ qemu_lockcnt_inc_and_unlock(&ctx->list_lock);
142
}
143
return ret;
144
}
28
--
145
--
29
2.26.2
146
2.9.3
30
147
31
148
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
This doesn't fix everything in here, but it does help clean up the
3
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
pylint report considerably.
4
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
5
Reviewed-by: Fam Zheng <famz@redhat.com>
6
Reviewed-by: Daniel P. Berrange <berrange@redhat.com>
7
Message-id: 20170213135235.12274-19-pbonzini@redhat.com
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
include/block/block_int.h | 64 +++++++++++++++++++++++++-----------------
11
include/sysemu/block-backend.h | 14 ++++++---
12
2 files changed, 49 insertions(+), 29 deletions(-)
5
13
6
This should be 100% style changes only; the intent is to make pylint
14
diff --git a/include/block/block_int.h b/include/block/block_int.h
7
more useful by working on establishing a baseline for iotests that we
8
can gate against in the future.
9
10
Signed-off-by: John Snow <jsnow@redhat.com>
11
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
12
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20200331000014.11581-2-jsnow@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
tests/qemu-iotests/iotests.py | 83 ++++++++++++++++++-----------------
18
1 file changed, 43 insertions(+), 40 deletions(-)
19
20
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
21
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
22
--- a/tests/qemu-iotests/iotests.py
16
--- a/include/block/block_int.h
23
+++ b/tests/qemu-iotests/iotests.py
17
+++ b/include/block/block_int.h
24
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ struct BdrvChild {
25
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
* copied as well.
26
#
20
*/
27
21
struct BlockDriverState {
28
-import errno
22
- int64_t total_sectors; /* if we are reading a disk image, give its
29
import os
23
- size in sectors */
30
import re
24
+ /* Protected by big QEMU lock or read-only after opening. No special
31
import subprocess
25
+ * locking needed during I/O...
32
-import string
26
+ */
33
import unittest
27
int open_flags; /* flags used to open the file, re-used for re-open */
34
import sys
28
bool read_only; /* if true, the media is read only */
35
import struct
29
bool encrypted; /* if true, the media is encrypted */
36
@@ -XXX,XX +XXX,XX @@ import faulthandler
30
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
37
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
31
bool sg; /* if true, the device is a /dev/sg* */
38
from qemu import qtest
32
bool probed; /* if true, format was probed rather than specified */
39
33
40
-assert sys.version_info >= (3,6)
34
- int copy_on_read; /* if nonzero, copy read backing sectors into image.
41
+assert sys.version_info >= (3, 6)
35
- note this is a reference count */
42
36
-
43
faulthandler.enable()
37
- CoQueue flush_queue; /* Serializing flush queue */
44
38
- bool active_flush_req; /* Flush request in flight? */
45
@@ -XXX,XX +XXX,XX @@ def qemu_img_log(*args):
39
- unsigned int write_gen; /* Current data generation */
46
return result
40
- unsigned int flushed_gen; /* Flushed write generation */
47
41
-
48
def img_info_log(filename, filter_path=None, imgopts=False, extra_args=[]):
42
BlockDriver *drv; /* NULL means no media */
49
- args = [ 'info' ]
43
void *opaque;
50
+ args = ['info']
44
51
if imgopts:
45
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
52
args.append('--image-opts')
46
BdrvChild *backing;
53
else:
47
BdrvChild *file;
54
- args += [ '-f', imgfmt ]
48
55
+ args += ['-f', imgfmt]
49
- /* Callback before write request is processed */
56
args += extra_args
50
- NotifierWithReturnList before_write_notifiers;
57
args.append(filename)
51
-
58
52
- /* number of in-flight requests; overall and serialising */
59
@@ -XXX,XX +XXX,XX @@ class QemuIoInteractive:
53
- unsigned int in_flight;
60
# quit command is in close(), '\n' is added automatically
54
- unsigned int serialising_in_flight;
61
assert '\n' not in cmd
55
-
62
cmd = cmd.strip()
56
- bool wakeup;
63
- assert cmd != 'q' and cmd != 'quit'
57
-
64
+ assert cmd not in ('q', 'quit')
58
- /* Offset after the highest byte written to */
65
self._p.stdin.write(cmd + '\n')
59
- uint64_t wr_highest_offset;
66
self._p.stdin.flush()
60
-
67
return self._read_output()
61
/* I/O Limits */
68
@@ -XXX,XX +XXX,XX @@ def qemu_nbd_early_pipe(*args):
62
BlockLimits bl;
69
sys.stderr.write('qemu-nbd received signal %i: %s\n' %
63
70
(-exitcode,
64
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
71
' '.join(qemu_nbd_args + ['--fork'] + list(args))))
65
QTAILQ_ENTRY(BlockDriverState) bs_list;
72
- if exitcode == 0:
66
/* element of the list of monitor-owned BDS */
73
- return exitcode, ''
67
QTAILQ_ENTRY(BlockDriverState) monitor_list;
74
- else:
68
- QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
75
- return exitcode, subp.communicate()[0]
69
int refcnt;
70
71
- QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
72
-
73
/* operation blockers */
74
QLIST_HEAD(, BdrvOpBlocker) op_blockers[BLOCK_OP_TYPE_MAX];
75
76
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
77
/* The error object in use for blocking operations on backing_hd */
78
Error *backing_blocker;
79
80
+ /* Protected by AioContext lock */
76
+
81
+
77
+ return exitcode, subp.communicate()[0] if exitcode else ''
82
+ /* If true, copy read backing sectors into image. Can be >1 if more
78
83
+ * than one client has requested copy-on-read.
79
def qemu_nbd_popen(*args):
84
+ */
80
'''Run qemu-nbd in daemon mode and return the parent's exit code'''
85
+ int copy_on_read;
81
@@ -XXX,XX +XXX,XX @@ def filter_qmp(qmsg, filter_fn):
82
items = qmsg.items()
83
84
for k, v in items:
85
- if isinstance(v, list) or isinstance(v, dict):
86
+ if isinstance(v, (dict, list)):
87
qmsg[k] = filter_qmp(v, filter_fn)
88
else:
89
qmsg[k] = filter_fn(k, v)
90
@@ -XXX,XX +XXX,XX @@ def filter_testfiles(msg):
91
return msg.replace(prefix, 'TEST_DIR/PID-')
92
93
def filter_qmp_testfiles(qmsg):
94
- def _filter(key, value):
95
+ def _filter(_key, value):
96
if is_str(value):
97
return filter_testfiles(value)
98
return value
99
@@ -XXX,XX +XXX,XX @@ def filter_imgfmt(msg):
100
return msg.replace(imgfmt, 'IMGFMT')
101
102
def filter_qmp_imgfmt(qmsg):
103
- def _filter(key, value):
104
+ def _filter(_key, value):
105
if is_str(value):
106
return filter_imgfmt(value)
107
return value
108
@@ -XXX,XX +XXX,XX @@ def log(msg, filters=[], indent=None):
109
If indent is provided, JSON serializable messages are pretty-printed.'''
110
for flt in filters:
111
msg = flt(msg)
112
- if isinstance(msg, dict) or isinstance(msg, list):
113
+ if isinstance(msg, (dict, list)):
114
# Python < 3.4 needs to know not to add whitespace when pretty-printing:
115
separators = (', ', ': ') if indent is None else (',', ': ')
116
# Don't sort if it's already sorted
117
@@ -XXX,XX +XXX,XX @@ def log(msg, filters=[], indent=None):
118
print(msg)
119
120
class Timeout:
121
- def __init__(self, seconds, errmsg = "Timeout"):
122
+ def __init__(self, seconds, errmsg="Timeout"):
123
self.seconds = seconds
124
self.errmsg = errmsg
125
def __enter__(self):
126
signal.signal(signal.SIGALRM, self.timeout)
127
signal.setitimer(signal.ITIMER_REAL, self.seconds)
128
return self
129
- def __exit__(self, type, value, traceback):
130
+ def __exit__(self, exc_type, value, traceback):
131
signal.setitimer(signal.ITIMER_REAL, 0)
132
return False
133
def timeout(self, signum, frame):
134
@@ -XXX,XX +XXX,XX @@ class Timeout:
135
def file_pattern(name):
136
return "{0}-{1}".format(os.getpid(), name)
137
138
-class FilePaths(object):
139
+class FilePaths:
140
"""
141
FilePaths is an auto-generated filename that cleans itself up.
142
143
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
144
self.pause_drive(drive, "write_aio")
145
return
146
self.qmp('human-monitor-command',
147
- command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
148
+ command_line='qemu-io %s "break %s bp_%s"' % (drive, event, drive))
149
150
def resume_drive(self, drive):
151
self.qmp('human-monitor-command',
152
- command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
153
+ command_line='qemu-io %s "remove_break bp_%s"' % (drive, drive))
154
155
def hmp_qemu_io(self, drive, cmd):
156
'''Write to a given drive using an HMP command'''
157
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
158
if output is None:
159
output = dict()
160
if isinstance(obj, list):
161
- for i in range(len(obj)):
162
- self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
163
+ for i, item in enumerate(obj):
164
+ self.flatten_qmp_object(item, output, basestr + str(i) + '.')
165
elif isinstance(obj, dict):
166
for key in obj:
167
self.flatten_qmp_object(obj[key], output, basestr + key + '.')
168
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
169
170
for bitmap in bitmaps[node_name]:
171
if bitmap.get('name', '') == bitmap_name:
172
- if recording is None:
173
- return bitmap
174
- elif bitmap.get('recording') == recording:
175
+ if recording is None or bitmap.get('recording') == recording:
176
return bitmap
177
return None
178
179
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
180
assert node is not None, 'Cannot follow path %s%s' % (root, path)
181
182
try:
183
- node_id = next(edge['child'] for edge in graph['edges'] \
184
- if edge['parent'] == node['id'] and
185
- edge['name'] == child_name)
186
+ node_id = next(edge['child'] for edge in graph['edges']
187
+ if (edge['parent'] == node['id'] and
188
+ edge['name'] == child_name))
189
+
86
+
190
+ node = next(node for node in graph['nodes']
87
+ /* If we are reading a disk image, give its size in sectors.
191
+ if node['id'] == node_id)
88
+ * Generally read-only; it is written to by load_vmstate and save_vmstate,
192
89
+ * but the block layer is quiescent during those.
193
- node = next(node for node in graph['nodes'] \
90
+ */
194
- if node['id'] == node_id)
91
+ int64_t total_sectors;
195
except StopIteration:
196
node = None
197
198
@@ -XXX,XX +XXX,XX @@ index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
199
class QMPTestCase(unittest.TestCase):
200
'''Abstract base class for QMP test cases'''
201
202
+ def __init__(self, *args, **kwargs):
203
+ super().__init__(*args, **kwargs)
204
+ # Many users of this class set a VM property we rely on heavily
205
+ # in the methods below.
206
+ self.vm = None
207
+
92
+
208
def dictpath(self, d, path):
93
+ /* Callback before write request is processed */
209
'''Traverse a path in a nested dict'''
94
+ NotifierWithReturnList before_write_notifiers;
210
for component in path.split('/'):
95
+
211
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
96
+ /* number of in-flight requests; overall and serialising */
212
else:
97
+ unsigned int in_flight;
213
self.assertEqual(result, value,
98
+ unsigned int serialising_in_flight;
214
'"%s" is "%s", expected "%s"'
99
+
215
- % (path, str(result), str(value)))
100
+ bool wakeup;
216
+ % (path, str(result), str(value)))
101
+
217
102
+ /* Offset after the highest byte written to */
218
def assert_no_active_block_jobs(self):
103
+ uint64_t wr_highest_offset;
219
result = self.vm.qmp('query-block-jobs')
104
+
220
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
105
/* threshold limit for writes, in bytes. "High water mark". */
221
"""Issue a query-named-block-nodes and assert node_name and/or
106
uint64_t write_threshold_offset;
222
file_name is present in the result"""
107
NotifierWithReturn write_threshold_notifier;
223
def check_equal_or_none(a, b):
108
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
224
- return a == None or b == None or a == b
109
/* counter for nested bdrv_io_plug */
225
+ return a is None or b is None or a == b
110
unsigned io_plugged;
226
assert node_name or file_name
111
227
result = self.vm.qmp('query-named-block-nodes')
112
+ QLIST_HEAD(, BdrvTrackedRequest) tracked_requests;
228
for x in result["return"]:
113
+ CoQueue flush_queue; /* Serializing flush queue */
229
if check_equal_or_none(x.get("node-name"), node_name) and \
114
+ bool active_flush_req; /* Flush request in flight? */
230
check_equal_or_none(x.get("file"), file_name):
115
+ unsigned int write_gen; /* Current data generation */
231
return
116
+ unsigned int flushed_gen; /* Flushed write generation */
232
- self.assertTrue(False, "Cannot find %s %s in result:\n%s" % \
117
+
233
- (node_name, file_name, result))
118
+ QLIST_HEAD(, BdrvDirtyBitmap) dirty_bitmaps;
234
+ self.fail("Cannot find %s %s in result:\n%s" %
119
+
235
+ (node_name, file_name, result))
120
+ /* do we need to tell the quest if we have a volatile write cache? */
236
121
+ int enable_write_cache;
237
def assert_json_filename_equal(self, json_filename, reference):
122
+
238
'''Asserts that the given filename is a json: filename and that its
123
int quiesce_counter;
239
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
124
};
240
self.assert_qmp(event, 'data/error', error)
125
241
self.assert_no_active_block_jobs()
126
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
242
return event
127
index XXXXXXX..XXXXXXX 100644
243
- elif event['event'] == 'JOB_STATUS_CHANGE':
128
--- a/include/sysemu/block-backend.h
244
+ if event['event'] == 'JOB_STATUS_CHANGE':
129
+++ b/include/sysemu/block-backend.h
245
self.assert_qmp(event, 'data/id', drive)
130
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDevOps {
246
131
* fields that must be public. This is in particular for QLIST_ENTRY() and
247
def wait_ready(self, drive='drive0'):
132
* friends so that BlockBackends can be kept in lists outside block-backend.c */
248
- '''Wait until a block job BLOCK_JOB_READY event'''
133
typedef struct BlockBackendPublic {
249
- f = {'data': {'type': 'mirror', 'device': drive } }
134
- /* I/O throttling.
250
- event = self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
135
- * throttle_state tells us if this BlockBackend has I/O limits configured.
251
+ """Wait until a BLOCK_JOB_READY event, and return the event."""
136
- * io_limits_disabled tells us if they are currently being enforced */
252
+ f = {'data': {'type': 'mirror', 'device': drive}}
137
+ /* I/O throttling has its own locking, but also some fields are
253
+ return self.vm.event_wait(name='BLOCK_JOB_READY', match=f)
138
+ * protected by the AioContext lock.
254
139
+ */
255
def wait_ready_and_cancel(self, drive='drive0'):
140
+
256
self.wait_ready(drive=drive)
141
+ /* Protected by AioContext lock. */
257
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
142
CoQueue throttled_reqs[2];
258
for job in result['return']:
143
+
259
if job['device'] == job_id:
144
+ /* Nonzero if the I/O limits are currently being ignored; generally
260
found = True
145
+ * it is zero. */
261
- if job['paused'] == True and job['busy'] == False:
146
unsigned int io_limits_disabled;
262
+ if job['paused'] and not job['busy']:
147
263
return job
148
/* The following fields are protected by the ThrottleGroup lock.
264
break
149
- * See the ThrottleGroup documentation for details. */
265
assert found
150
+ * See the ThrottleGroup documentation for details.
266
@@ -XXX,XX +XXX,XX @@ def qemu_pipe(*args):
151
+ * throttle_state tells us if I/O limits are configured. */
267
universal_newlines=True)
152
ThrottleState *throttle_state;
268
exitcode = subp.wait()
153
ThrottleTimers throttle_timers;
269
if exitcode < 0:
154
unsigned pending_reqs[2];
270
- sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode,
271
- ' '.join(args)))
272
+ sys.stderr.write('qemu received signal %i: %s\n' %
273
+ (-exitcode, ' '.join(args)))
274
return subp.communicate()[0]
275
276
def supported_formats(read_only=False):
277
@@ -XXX,XX +XXX,XX @@ def skip_if_unsupported(required_formats=[], read_only=False):
278
if usf_list:
279
test_case.case_skip('{}: formats {} are not whitelisted'.format(
280
test_case, usf_list))
281
+ return None
282
else:
283
return func(test_case, *args, **kwargs)
284
return func_wrapper
285
@@ -XXX,XX +XXX,XX @@ def skip_if_user_is_root(func):
286
def func_wrapper(*args, **kwargs):
287
if os.getuid() == 0:
288
case_notrun('{}: cannot be run as root'.format(args[0]))
289
+ return None
290
else:
291
return func(*args, **kwargs)
292
return func_wrapper
293
--
155
--
294
2.26.2
156
2.9.3
295
157
296
158
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We originally refused to allow resize of images with internal
3
This uses the lock-free mutex described in the paper '"Blocking without
4
snapshots because the v2 image format did not require the tracking of
4
Locking", or LFTHREADS: A lock-free thread library' by Gidenstam and
5
snapshot size, making it impossible to safely revert to a snapshot
5
Papatriantafilou. The same technique is used in OSv, and in fact
6
with a different size than the current view of the image. But the
6
the code is essentially a conversion to C of OSv's code.
7
snapshot size tracking was rectified in v3, and our recent fixes to
7
8
qemu-img amend (see 0a85af35) guarantee that we always have a valid
8
[Added missing coroutine_fn in tests/test-aio-multithread.c.
9
snapshot size. Thus, we no longer need to artificially limit image
9
--Stefan]
10
resizes, but it does become one more thing that would prevent a
10
11
downgrade back to v2. And now that we support different-sized
11
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
12
snapshots, it's also easy to fix reverting to a snapshot to apply the
12
Reviewed-by: Fam Zheng <famz@redhat.com>
13
new size.
13
Message-id: 20170213181244.16297-2-pbonzini@redhat.com
14
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Upgrade iotest 61 to cover this (we previously had NO coverage of
16
refusal to resize while snapshots exist). Note that the amend process
17
can fail but still have effects: in particular, since we break things
18
into upgrade, resize, downgrade, a failure during resize does not roll
19
back changes made during upgrade, nor does failure in downgrade roll
20
back a resize. But this situation is pre-existing even without this
21
patch; and without journaling, the best we could do is minimize the
22
chance of partial failure by collecting all changes prior to doing any
23
writes - which adds a lot of complexity but could still fail with EIO.
24
On the other hand, we are careful that even if we have partial
25
modification but then fail, the image is left viable (that is, we are
26
careful to sequence things so that after each successful cluster
27
write, there may be transient leaked clusters but no corrupt
28
metadata). And complicating the code to make it more transaction-like
29
is not worth the effort: a user can always request multiple 'qemu-img
30
amend' changing one thing each, if they need finer-grained control
31
over detecting the first failure than what they get by letting qemu
32
decide how to sequence multiple changes.
33
34
Signed-off-by: Eric Blake <eblake@redhat.com>
35
Reviewed-by: Max Reitz <mreitz@redhat.com>
36
Message-Id: <20200428192648.749066-3-eblake@redhat.com>
37
Signed-off-by: Max Reitz <mreitz@redhat.com>
38
---
15
---
39
block/qcow2-snapshot.c | 20 ++++++++++++++++----
16
include/qemu/coroutine.h | 17 ++++-
40
block/qcow2.c | 25 ++++++++++++++++++++++---
17
tests/test-aio-multithread.c | 86 ++++++++++++++++++++++++
41
tests/qemu-iotests/061 | 35 +++++++++++++++++++++++++++++++++++
18
util/qemu-coroutine-lock.c | 155 ++++++++++++++++++++++++++++++++++++++++---
42
tests/qemu-iotests/061.out | 28 ++++++++++++++++++++++++++++
19
util/trace-events | 1 +
43
4 files changed, 101 insertions(+), 7 deletions(-)
20
4 files changed, 246 insertions(+), 13 deletions(-)
44
21
45
diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c
22
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
46
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
47
--- a/block/qcow2-snapshot.c
24
--- a/include/qemu/coroutine.h
48
+++ b/block/qcow2-snapshot.c
25
+++ b/include/qemu/coroutine.h
26
@@ -XXX,XX +XXX,XX @@ bool qemu_co_queue_empty(CoQueue *queue);
27
/**
28
* Provides a mutex that can be used to synchronise coroutines
29
*/
30
+struct CoWaitRecord;
31
typedef struct CoMutex {
32
- bool locked;
33
+ /* Count of pending lockers; 0 for a free mutex, 1 for an
34
+ * uncontended mutex.
35
+ */
36
+ unsigned locked;
37
+
38
+ /* A queue of waiters. Elements are added atomically in front of
39
+ * from_push. to_pop is only populated, and popped from, by whoever
40
+ * is in charge of the next wakeup. This can be an unlocker or,
41
+ * through the handoff protocol, a locker that is about to go to sleep.
42
+ */
43
+ QSLIST_HEAD(, CoWaitRecord) from_push, to_pop;
44
+
45
+ unsigned handoff, sequence;
46
+
47
Coroutine *holder;
48
- CoQueue queue;
49
} CoMutex;
50
51
/**
52
diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/tests/test-aio-multithread.c
55
+++ b/tests/test-aio-multithread.c
56
@@ -XXX,XX +XXX,XX @@ static void test_multi_co_schedule_10(void)
57
test_multi_co_schedule(10);
58
}
59
60
+/* CoMutex thread-safety. */
61
+
62
+static uint32_t atomic_counter;
63
+static uint32_t running;
64
+static uint32_t counter;
65
+static CoMutex comutex;
66
+
67
+static void coroutine_fn test_multi_co_mutex_entry(void *opaque)
68
+{
69
+ while (!atomic_mb_read(&now_stopping)) {
70
+ qemu_co_mutex_lock(&comutex);
71
+ counter++;
72
+ qemu_co_mutex_unlock(&comutex);
73
+
74
+ /* Increase atomic_counter *after* releasing the mutex. Otherwise
75
+ * there is a chance (it happens about 1 in 3 runs) that the iothread
76
+ * exits before the coroutine is woken up, causing a spurious
77
+ * assertion failure.
78
+ */
79
+ atomic_inc(&atomic_counter);
80
+ }
81
+ atomic_dec(&running);
82
+}
83
+
84
+static void test_multi_co_mutex(int threads, int seconds)
85
+{
86
+ int i;
87
+
88
+ qemu_co_mutex_init(&comutex);
89
+ counter = 0;
90
+ atomic_counter = 0;
91
+ now_stopping = false;
92
+
93
+ create_aio_contexts();
94
+ assert(threads <= NUM_CONTEXTS);
95
+ running = threads;
96
+ for (i = 0; i < threads; i++) {
97
+ Coroutine *co1 = qemu_coroutine_create(test_multi_co_mutex_entry, NULL);
98
+ aio_co_schedule(ctx[i], co1);
99
+ }
100
+
101
+ g_usleep(seconds * 1000000);
102
+
103
+ atomic_mb_set(&now_stopping, true);
104
+ while (running > 0) {
105
+ g_usleep(100000);
106
+ }
107
+
108
+ join_aio_contexts();
109
+ g_test_message("%d iterations/second\n", counter / seconds);
110
+ g_assert_cmpint(counter, ==, atomic_counter);
111
+}
112
+
113
+/* Testing with NUM_CONTEXTS threads focuses on the queue. The mutex however
114
+ * is too contended (and the threads spend too much time in aio_poll)
115
+ * to actually stress the handoff protocol.
116
+ */
117
+static void test_multi_co_mutex_1(void)
118
+{
119
+ test_multi_co_mutex(NUM_CONTEXTS, 1);
120
+}
121
+
122
+static void test_multi_co_mutex_10(void)
123
+{
124
+ test_multi_co_mutex(NUM_CONTEXTS, 10);
125
+}
126
+
127
+/* Testing with fewer threads stresses the handoff protocol too. Still, the
128
+ * case where the locker _can_ pick up a handoff is very rare, happening
129
+ * about 10 times in 1 million, so increase the runtime a bit compared to
130
+ * other "quick" testcases that only run for 1 second.
131
+ */
132
+static void test_multi_co_mutex_2_3(void)
133
+{
134
+ test_multi_co_mutex(2, 3);
135
+}
136
+
137
+static void test_multi_co_mutex_2_30(void)
138
+{
139
+ test_multi_co_mutex(2, 30);
140
+}
141
+
142
/* End of tests. */
143
144
int main(int argc, char **argv)
145
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
146
g_test_add_func("/aio/multi/lifecycle", test_lifecycle);
147
if (g_test_quick()) {
148
g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_1);
149
+ g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_1);
150
+ g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_3);
151
} else {
152
g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_10);
153
+ g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_10);
154
+ g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_30);
155
}
156
return g_test_run();
157
}
158
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
159
index XXXXXXX..XXXXXXX 100644
160
--- a/util/qemu-coroutine-lock.c
161
+++ b/util/qemu-coroutine-lock.c
49
@@ -XXX,XX +XXX,XX @@
162
@@ -XXX,XX +XXX,XX @@
163
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
164
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
165
* THE SOFTWARE.
166
+ *
167
+ * The lock-free mutex implementation is based on OSv
168
+ * (core/lfmutex.cc, include/lockfree/mutex.hh).
169
+ * Copyright (C) 2013 Cloudius Systems, Ltd.
50
*/
170
*/
51
171
52
#include "qemu/osdep.h"
172
#include "qemu/osdep.h"
53
+#include "sysemu/block-backend.h"
173
@@ -XXX,XX +XXX,XX @@ bool qemu_co_queue_empty(CoQueue *queue)
54
#include "qapi/error.h"
174
return QSIMPLEQ_FIRST(&queue->entries) == NULL;
55
#include "qcow2.h"
175
}
56
#include "qemu/bswap.h"
176
57
@@ -XXX,XX +XXX,XX @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id)
177
+/* The wait records are handled with a multiple-producer, single-consumer
178
+ * lock-free queue. There cannot be two concurrent pop_waiter() calls
179
+ * because pop_waiter() can only be called while mutex->handoff is zero.
180
+ * This can happen in three cases:
181
+ * - in qemu_co_mutex_unlock, before the hand-off protocol has started.
182
+ * In this case, qemu_co_mutex_lock will see mutex->handoff == 0 and
183
+ * not take part in the handoff.
184
+ * - in qemu_co_mutex_lock, if it steals the hand-off responsibility from
185
+ * qemu_co_mutex_unlock. In this case, qemu_co_mutex_unlock will fail
186
+ * the cmpxchg (it will see either 0 or the next sequence value) and
187
+ * exit. The next hand-off cannot begin until qemu_co_mutex_lock has
188
+ * woken up someone.
189
+ * - in qemu_co_mutex_unlock, if it takes the hand-off token itself.
190
+ * In this case another iteration starts with mutex->handoff == 0;
191
+ * a concurrent qemu_co_mutex_lock will fail the cmpxchg, and
192
+ * qemu_co_mutex_unlock will go back to case (1).
193
+ *
194
+ * The following functions manage this queue.
195
+ */
196
+typedef struct CoWaitRecord {
197
+ Coroutine *co;
198
+ QSLIST_ENTRY(CoWaitRecord) next;
199
+} CoWaitRecord;
200
+
201
+static void push_waiter(CoMutex *mutex, CoWaitRecord *w)
202
+{
203
+ w->co = qemu_coroutine_self();
204
+ QSLIST_INSERT_HEAD_ATOMIC(&mutex->from_push, w, next);
205
+}
206
+
207
+static void move_waiters(CoMutex *mutex)
208
+{
209
+ QSLIST_HEAD(, CoWaitRecord) reversed;
210
+ QSLIST_MOVE_ATOMIC(&reversed, &mutex->from_push);
211
+ while (!QSLIST_EMPTY(&reversed)) {
212
+ CoWaitRecord *w = QSLIST_FIRST(&reversed);
213
+ QSLIST_REMOVE_HEAD(&reversed, next);
214
+ QSLIST_INSERT_HEAD(&mutex->to_pop, w, next);
215
+ }
216
+}
217
+
218
+static CoWaitRecord *pop_waiter(CoMutex *mutex)
219
+{
220
+ CoWaitRecord *w;
221
+
222
+ if (QSLIST_EMPTY(&mutex->to_pop)) {
223
+ move_waiters(mutex);
224
+ if (QSLIST_EMPTY(&mutex->to_pop)) {
225
+ return NULL;
226
+ }
227
+ }
228
+ w = QSLIST_FIRST(&mutex->to_pop);
229
+ QSLIST_REMOVE_HEAD(&mutex->to_pop, next);
230
+ return w;
231
+}
232
+
233
+static bool has_waiters(CoMutex *mutex)
234
+{
235
+ return QSLIST_EMPTY(&mutex->to_pop) || QSLIST_EMPTY(&mutex->from_push);
236
+}
237
+
238
void qemu_co_mutex_init(CoMutex *mutex)
239
{
240
memset(mutex, 0, sizeof(*mutex));
241
- qemu_co_queue_init(&mutex->queue);
242
}
243
244
-void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
245
+static void coroutine_fn qemu_co_mutex_lock_slowpath(CoMutex *mutex)
246
{
247
Coroutine *self = qemu_coroutine_self();
248
+ CoWaitRecord w;
249
+ unsigned old_handoff;
250
251
trace_qemu_co_mutex_lock_entry(mutex, self);
252
+ w.co = self;
253
+ push_waiter(mutex, &w);
254
255
- while (mutex->locked) {
256
- qemu_co_queue_wait(&mutex->queue);
257
+ /* This is the "Responsibility Hand-Off" protocol; a lock() picks from
258
+ * a concurrent unlock() the responsibility of waking somebody up.
259
+ */
260
+ old_handoff = atomic_mb_read(&mutex->handoff);
261
+ if (old_handoff &&
262
+ has_waiters(mutex) &&
263
+ atomic_cmpxchg(&mutex->handoff, old_handoff, 0) == old_handoff) {
264
+ /* There can be no concurrent pops, because there can be only
265
+ * one active handoff at a time.
266
+ */
267
+ CoWaitRecord *to_wake = pop_waiter(mutex);
268
+ Coroutine *co = to_wake->co;
269
+ if (co == self) {
270
+ /* We got the lock ourselves! */
271
+ assert(to_wake == &w);
272
+ return;
273
+ }
274
+
275
+ aio_co_wake(co);
58
}
276
}
59
277
60
if (sn->disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
278
- mutex->locked = true;
61
- error_report("qcow2: Loading snapshots with different disk "
279
- mutex->holder = self;
62
- "size is not implemented");
280
- self->locks_held++;
63
- ret = -ENOTSUP;
281
-
64
- goto fail;
282
+ qemu_coroutine_yield();
65
+ BlockBackend *blk = blk_new_with_bs(bs, BLK_PERM_RESIZE, BLK_PERM_ALL,
283
trace_qemu_co_mutex_lock_return(mutex, self);
66
+ &local_err);
284
}
67
+ if (!blk) {
285
68
+ error_report_err(local_err);
286
+void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
69
+ ret = -ENOTSUP;
287
+{
70
+ goto fail;
288
+ Coroutine *self = qemu_coroutine_self();
71
+ }
289
+
72
+
290
+ if (atomic_fetch_inc(&mutex->locked) == 0) {
73
+ ret = blk_truncate(blk, sn->disk_size, true, PREALLOC_MODE_OFF, 0,
291
+ /* Uncontended. */
74
+ &local_err);
292
+ trace_qemu_co_mutex_lock_uncontended(mutex, self);
75
+ blk_unref(blk);
293
+ } else {
76
+ if (ret < 0) {
294
+ qemu_co_mutex_lock_slowpath(mutex);
77
+ error_report_err(local_err);
295
+ }
78
+ goto fail;
296
+ mutex->holder = self;
79
+ }
297
+ self->locks_held++;
80
}
298
+}
81
299
+
82
/*
300
void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
83
diff --git a/block/qcow2.c b/block/qcow2.c
301
{
302
Coroutine *self = qemu_coroutine_self();
303
304
trace_qemu_co_mutex_unlock_entry(mutex, self);
305
306
- assert(mutex->locked == true);
307
+ assert(mutex->locked);
308
assert(mutex->holder == self);
309
assert(qemu_in_coroutine());
310
311
- mutex->locked = false;
312
mutex->holder = NULL;
313
self->locks_held--;
314
- qemu_co_queue_next(&mutex->queue);
315
+ if (atomic_fetch_dec(&mutex->locked) == 1) {
316
+ /* No waiting qemu_co_mutex_lock(). Pfew, that was easy! */
317
+ return;
318
+ }
319
+
320
+ for (;;) {
321
+ CoWaitRecord *to_wake = pop_waiter(mutex);
322
+ unsigned our_handoff;
323
+
324
+ if (to_wake) {
325
+ Coroutine *co = to_wake->co;
326
+ aio_co_wake(co);
327
+ break;
328
+ }
329
+
330
+ /* Some concurrent lock() is in progress (we know this because
331
+ * mutex->locked was >1) but it hasn't yet put itself on the wait
332
+ * queue. Pick a sequence number for the handoff protocol (not 0).
333
+ */
334
+ if (++mutex->sequence == 0) {
335
+ mutex->sequence = 1;
336
+ }
337
+
338
+ our_handoff = mutex->sequence;
339
+ atomic_mb_set(&mutex->handoff, our_handoff);
340
+ if (!has_waiters(mutex)) {
341
+ /* The concurrent lock has not added itself yet, so it
342
+ * will be able to pick our handoff.
343
+ */
344
+ break;
345
+ }
346
+
347
+ /* Try to do the handoff protocol ourselves; if somebody else has
348
+ * already taken it, however, we're done and they're responsible.
349
+ */
350
+ if (atomic_cmpxchg(&mutex->handoff, our_handoff, 0) != our_handoff) {
351
+ break;
352
+ }
353
+ }
354
355
trace_qemu_co_mutex_unlock_return(mutex, self);
356
}
357
diff --git a/util/trace-events b/util/trace-events
84
index XXXXXXX..XXXXXXX 100644
358
index XXXXXXX..XXXXXXX 100644
85
--- a/block/qcow2.c
359
--- a/util/trace-events
86
+++ b/block/qcow2.c
360
+++ b/util/trace-events
87
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset,
361
@@ -XXX,XX +XXX,XX @@ qemu_coroutine_terminate(void *co) "self %p"
88
362
89
qemu_co_mutex_lock(&s->lock);
363
# util/qemu-coroutine-lock.c
90
364
qemu_co_queue_run_restart(void *co) "co %p"
91
- /* cannot proceed if image has snapshots */
365
+qemu_co_mutex_lock_uncontended(void *mutex, void *self) "mutex %p self %p"
92
- if (s->nb_snapshots) {
366
qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p"
93
- error_setg(errp, "Can't resize an image which has snapshots");
367
qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p"
94
+ /*
368
qemu_co_mutex_unlock_entry(void *mutex, void *self) "mutex %p self %p"
95
+ * Even though we store snapshot size for all images, it was not
96
+ * required until v3, so it is not safe to proceed for v2.
97
+ */
98
+ if (s->nb_snapshots && s->qcow_version < 3) {
99
+ error_setg(errp, "Can't resize a v2 image which has snapshots");
100
ret = -ENOTSUP;
101
goto fail;
102
}
103
@@ -XXX,XX +XXX,XX @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
104
BDRVQcow2State *s = bs->opaque;
105
int current_version = s->qcow_version;
106
int ret;
107
+ int i;
108
109
/* This is qcow2_downgrade(), not qcow2_upgrade() */
110
assert(target_version < current_version);
111
@@ -XXX,XX +XXX,XX @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version,
112
return -ENOTSUP;
113
}
114
115
+ /*
116
+ * If any internal snapshot has a different size than the current
117
+ * image size, or VM state size that exceeds 32 bits, downgrading
118
+ * is unsafe. Even though we would still use v3-compliant output
119
+ * to preserve that data, other v2 programs might not realize
120
+ * those optional fields are important.
121
+ */
122
+ for (i = 0; i < s->nb_snapshots; i++) {
123
+ if (s->snapshots[i].vm_state_size > UINT32_MAX ||
124
+ s->snapshots[i].disk_size != bs->total_sectors * BDRV_SECTOR_SIZE) {
125
+ error_setg(errp, "Internal snapshots prevent downgrade of image");
126
+ return -ENOTSUP;
127
+ }
128
+ }
129
+
130
/* clear incompatible features */
131
if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) {
132
ret = qcow2_mark_clean(bs);
133
diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061
134
index XXXXXXX..XXXXXXX 100755
135
--- a/tests/qemu-iotests/061
136
+++ b/tests/qemu-iotests/061
137
@@ -XXX,XX +XXX,XX @@ $PYTHON qcow2.py "$TEST_IMG" dump-header
138
$QEMU_IO -c "read -P 0x2a 42M 64k" "$TEST_IMG" | _filter_qemu_io
139
_check_test_img
140
141
+echo
142
+echo "=== Testing resize with snapshots ==="
143
+echo
144
+_make_test_img -o "compat=0.10" 32M
145
+$QEMU_IO -c "write -P 0x2a 24M 64k" "$TEST_IMG" | _filter_qemu_io
146
+$QEMU_IMG snapshot -c foo "$TEST_IMG"
147
+$QEMU_IMG resize "$TEST_IMG" 64M &&
148
+ echo "unexpected pass"
149
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
150
+
151
+$QEMU_IMG amend -o "compat=1.1,size=128M" "$TEST_IMG" ||
152
+ echo "unexpected fail"
153
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
154
+
155
+$QEMU_IMG snapshot -c bar "$TEST_IMG"
156
+$QEMU_IMG resize --shrink "$TEST_IMG" 64M ||
157
+ echo "unexpected fail"
158
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
159
+
160
+$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" &&
161
+ echo "unexpected pass"
162
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
163
+
164
+$QEMU_IMG snapshot -a bar "$TEST_IMG" ||
165
+ echo "unexpected fail"
166
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
167
+
168
+$QEMU_IMG snapshot -d bar "$TEST_IMG"
169
+$QEMU_IMG amend -o "compat=0.10,size=32M" "$TEST_IMG" ||
170
+ echo "unexpected fail"
171
+$PYTHON qcow2.py "$TEST_IMG" dump-header | grep '^\(version\|size\|nb_snap\)'
172
+
173
+_check_test_img
174
+
175
+
176
echo
177
echo "=== Testing dirty lazy_refcounts=off ==="
178
echo
179
diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out
180
index XXXXXXX..XXXXXXX 100644
181
--- a/tests/qemu-iotests/061.out
182
+++ b/tests/qemu-iotests/061.out
183
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 44040192
184
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
185
No errors were found on the image.
186
187
+=== Testing resize with snapshots ===
188
+
189
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432
190
+wrote 65536/65536 bytes at offset 25165824
191
+64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
192
+qemu-img: Can't resize a v2 image which has snapshots
193
+version 2
194
+size 33554432
195
+nb_snapshots 1
196
+version 3
197
+size 134217728
198
+nb_snapshots 1
199
+Image resized.
200
+version 3
201
+size 67108864
202
+nb_snapshots 2
203
+qemu-img: Internal snapshots prevent downgrade of image
204
+version 3
205
+size 33554432
206
+nb_snapshots 2
207
+version 3
208
+size 134217728
209
+nb_snapshots 2
210
+version 2
211
+size 33554432
212
+nb_snapshots 1
213
+No errors were found on the image.
214
+
215
=== Testing dirty lazy_refcounts=off ===
216
217
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
218
--
369
--
219
2.26.2
370
2.9.3
220
371
221
372
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
The right way to solve this is to come up with a virtual environment
3
Running a very small critical section on pthread_mutex_t and CoMutex
4
infrastructure that sets all the paths correctly, and/or to create
4
shows that pthread_mutex_t is much faster because it doesn't actually
5
installable python modules that can be imported normally.
5
go to sleep. What happens is that the critical section is shorter
6
than the latency of entering the kernel and thus FUTEX_WAIT always
7
fails. With CoMutex there is no such latency but you still want to
8
avoid wait and wakeup. So introduce it artificially.
6
9
7
That's hard, so just silence this error for now.
10
This only works with one waiters; because CoMutex is fair, it will
11
always have more waits and wakeups than a pthread_mutex_t.
8
12
9
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
13
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: Fam Zheng <famz@redhat.com>
11
Signed-off-by: John Snow <jsnow@redhat.com>
15
Message-id: 20170213181244.16297-3-pbonzini@redhat.com
12
Message-Id: <20200331000014.11581-4-jsnow@redhat.com>
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
17
---
16
tests/qemu-iotests/iotests.py | 1 +
18
include/qemu/coroutine.h | 5 +++++
17
1 file changed, 1 insertion(+)
19
util/qemu-coroutine-lock.c | 51 ++++++++++++++++++++++++++++++++++++++++------
20
util/qemu-coroutine.c | 2 +-
21
3 files changed, 51 insertions(+), 7 deletions(-)
18
22
19
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
23
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
20
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
21
--- a/tests/qemu-iotests/iotests.py
25
--- a/include/qemu/coroutine.h
22
+++ b/tests/qemu-iotests/iotests.py
26
+++ b/include/qemu/coroutine.h
23
@@ -XXX,XX +XXX,XX @@ import io
27
@@ -XXX,XX +XXX,XX @@ typedef struct CoMutex {
24
from collections import OrderedDict
28
*/
25
import faulthandler
29
unsigned locked;
26
30
27
+# pylint: disable=import-error, wrong-import-position
31
+ /* Context that is holding the lock. Useful to avoid spinning
28
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python'))
32
+ * when two coroutines on the same AioContext try to get the lock. :)
29
from qemu import qtest
33
+ */
34
+ AioContext *ctx;
35
+
36
/* A queue of waiters. Elements are added atomically in front of
37
* from_push. to_pop is only populated, and popped from, by whoever
38
* is in charge of the next wakeup. This can be an unlocker or,
39
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/util/qemu-coroutine-lock.c
42
+++ b/util/qemu-coroutine-lock.c
43
@@ -XXX,XX +XXX,XX @@
44
#include "qemu-common.h"
45
#include "qemu/coroutine.h"
46
#include "qemu/coroutine_int.h"
47
+#include "qemu/processor.h"
48
#include "qemu/queue.h"
49
#include "block/aio.h"
50
#include "trace.h"
51
@@ -XXX,XX +XXX,XX @@ void qemu_co_mutex_init(CoMutex *mutex)
52
memset(mutex, 0, sizeof(*mutex));
53
}
54
55
-static void coroutine_fn qemu_co_mutex_lock_slowpath(CoMutex *mutex)
56
+static void coroutine_fn qemu_co_mutex_wake(CoMutex *mutex, Coroutine *co)
57
+{
58
+ /* Read co before co->ctx; pairs with smp_wmb() in
59
+ * qemu_coroutine_enter().
60
+ */
61
+ smp_read_barrier_depends();
62
+ mutex->ctx = co->ctx;
63
+ aio_co_wake(co);
64
+}
65
+
66
+static void coroutine_fn qemu_co_mutex_lock_slowpath(AioContext *ctx,
67
+ CoMutex *mutex)
68
{
69
Coroutine *self = qemu_coroutine_self();
70
CoWaitRecord w;
71
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(CoMutex *mutex)
72
if (co == self) {
73
/* We got the lock ourselves! */
74
assert(to_wake == &w);
75
+ mutex->ctx = ctx;
76
return;
77
}
78
79
- aio_co_wake(co);
80
+ qemu_co_mutex_wake(mutex, co);
81
}
82
83
qemu_coroutine_yield();
84
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn qemu_co_mutex_lock_slowpath(CoMutex *mutex)
85
86
void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex)
87
{
88
+ AioContext *ctx = qemu_get_current_aio_context();
89
Coroutine *self = qemu_coroutine_self();
90
+ int waiters, i;
91
92
- if (atomic_fetch_inc(&mutex->locked) == 0) {
93
+ /* Running a very small critical section on pthread_mutex_t and CoMutex
94
+ * shows that pthread_mutex_t is much faster because it doesn't actually
95
+ * go to sleep. What happens is that the critical section is shorter
96
+ * than the latency of entering the kernel and thus FUTEX_WAIT always
97
+ * fails. With CoMutex there is no such latency but you still want to
98
+ * avoid wait and wakeup. So introduce it artificially.
99
+ */
100
+ i = 0;
101
+retry_fast_path:
102
+ waiters = atomic_cmpxchg(&mutex->locked, 0, 1);
103
+ if (waiters != 0) {
104
+ while (waiters == 1 && ++i < 1000) {
105
+ if (atomic_read(&mutex->ctx) == ctx) {
106
+ break;
107
+ }
108
+ if (atomic_read(&mutex->locked) == 0) {
109
+ goto retry_fast_path;
110
+ }
111
+ cpu_relax();
112
+ }
113
+ waiters = atomic_fetch_inc(&mutex->locked);
114
+ }
115
+
116
+ if (waiters == 0) {
117
/* Uncontended. */
118
trace_qemu_co_mutex_lock_uncontended(mutex, self);
119
+ mutex->ctx = ctx;
120
} else {
121
- qemu_co_mutex_lock_slowpath(mutex);
122
+ qemu_co_mutex_lock_slowpath(ctx, mutex);
123
}
124
mutex->holder = self;
125
self->locks_held++;
126
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
127
assert(mutex->holder == self);
128
assert(qemu_in_coroutine());
129
130
+ mutex->ctx = NULL;
131
mutex->holder = NULL;
132
self->locks_held--;
133
if (atomic_fetch_dec(&mutex->locked) == 1) {
134
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex)
135
unsigned our_handoff;
136
137
if (to_wake) {
138
- Coroutine *co = to_wake->co;
139
- aio_co_wake(co);
140
+ qemu_co_mutex_wake(mutex, to_wake->co);
141
break;
142
}
143
144
diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c
145
index XXXXXXX..XXXXXXX 100644
146
--- a/util/qemu-coroutine.c
147
+++ b/util/qemu-coroutine.c
148
@@ -XXX,XX +XXX,XX @@ void qemu_coroutine_enter(Coroutine *co)
149
co->ctx = qemu_get_current_aio_context();
150
151
/* Store co->ctx before anything that stores co. Matches
152
- * barrier in aio_co_wake.
153
+ * barrier in aio_co_wake and qemu_co_mutex_wake.
154
*/
155
smp_wmb();
30
156
31
--
157
--
32
2.26.2
158
2.9.3
33
159
34
160
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We are going to use aio-task-pool API, so we'll need state pointer in
3
Add two implementations of the same benchmark as the previous patch,
4
BlockCopyTask anyway. Add it now and use where possible.
4
but using pthreads. One uses a normal QemuMutex, the other is Linux
5
5
only and implements a fair mutex based on MCS locks and futexes.
6
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
This shows that the slower performance of the 5-thread case is due to
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
the fairness of CoMutex, rather than to coroutines. If fairness does
8
Message-Id: <20200429130847.28124-4-vsementsov@virtuozzo.com>
8
not matter, as is the case with two threads, CoMutex can actually be
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
faster than pthreads.
10
11
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
12
Reviewed-by: Fam Zheng <famz@redhat.com>
13
Message-id: 20170213181244.16297-4-pbonzini@redhat.com
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
15
---
11
block/block-copy.c | 28 +++++++++++++++-------------
16
tests/test-aio-multithread.c | 164 +++++++++++++++++++++++++++++++++++++++++++
12
1 file changed, 15 insertions(+), 13 deletions(-)
17
1 file changed, 164 insertions(+)
13
18
14
diff --git a/block/block-copy.c b/block/block-copy.c
19
diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c
15
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
16
--- a/block/block-copy.c
21
--- a/tests/test-aio-multithread.c
17
+++ b/block/block-copy.c
22
+++ b/tests/test-aio-multithread.c
18
@@ -XXX,XX +XXX,XX @@
23
@@ -XXX,XX +XXX,XX @@ static void test_multi_co_mutex_2_30(void)
19
#define BLOCK_COPY_MAX_MEM (128 * MiB)
24
test_multi_co_mutex(2, 30);
20
21
typedef struct BlockCopyTask {
22
+ BlockCopyState *s;
23
int64_t offset;
24
int64_t bytes;
25
QLIST_ENTRY(BlockCopyTask) list;
26
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
27
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
28
s->in_flight_bytes += bytes;
29
30
- task->offset = offset;
31
- task->bytes = bytes;
32
+ *task = (BlockCopyTask) {
33
+ .s = s,
34
+ .offset = offset,
35
+ .bytes = bytes,
36
+ };
37
qemu_co_queue_init(&task->wait_queue);
38
QLIST_INSERT_HEAD(&s->tasks, task, list);
39
40
@@ -XXX,XX +XXX,XX @@ static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
41
* wake up all tasks waiting for us (may be some of them are not intersecting
42
* with shrunk task)
43
*/
44
-static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
45
- BlockCopyTask *task,
46
+static void coroutine_fn block_copy_task_shrink(BlockCopyTask *task,
47
int64_t new_bytes)
48
{
49
if (new_bytes == task->bytes) {
50
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn block_copy_task_shrink(BlockCopyState *s,
51
52
assert(new_bytes > 0 && new_bytes < task->bytes);
53
54
- s->in_flight_bytes -= task->bytes - new_bytes;
55
- bdrv_set_dirty_bitmap(s->copy_bitmap,
56
+ task->s->in_flight_bytes -= task->bytes - new_bytes;
57
+ bdrv_set_dirty_bitmap(task->s->copy_bitmap,
58
task->offset + new_bytes, task->bytes - new_bytes);
59
60
task->bytes = new_bytes;
61
qemu_co_queue_restart_all(&task->wait_queue);
62
}
25
}
63
26
64
-static void coroutine_fn block_copy_task_end(BlockCopyState *s,
27
+/* Same test with fair mutexes, for performance comparison. */
65
- BlockCopyTask *task, int ret)
28
+
66
+static void coroutine_fn block_copy_task_end(BlockCopyTask *task, int ret)
29
+#ifdef CONFIG_LINUX
67
{
30
+#include "qemu/futex.h"
68
- s->in_flight_bytes -= task->bytes;
31
+
69
+ task->s->in_flight_bytes -= task->bytes;
32
+/* The nodes for the mutex reside in this structure (on which we try to avoid
70
if (ret < 0) {
33
+ * false sharing). The head of the mutex is in the "mutex_head" variable.
71
- bdrv_set_dirty_bitmap(s->copy_bitmap, task->offset, task->bytes);
34
+ */
72
+ bdrv_set_dirty_bitmap(task->s->copy_bitmap, task->offset, task->bytes);
35
+static struct {
36
+ int next, locked;
37
+ int padding[14];
38
+} nodes[NUM_CONTEXTS] __attribute__((__aligned__(64)));
39
+
40
+static int mutex_head = -1;
41
+
42
+static void mcs_mutex_lock(void)
43
+{
44
+ int prev;
45
+
46
+ nodes[id].next = -1;
47
+ nodes[id].locked = 1;
48
+ prev = atomic_xchg(&mutex_head, id);
49
+ if (prev != -1) {
50
+ atomic_set(&nodes[prev].next, id);
51
+ qemu_futex_wait(&nodes[id].locked, 1);
52
+ }
53
+}
54
+
55
+static void mcs_mutex_unlock(void)
56
+{
57
+ int next;
58
+ if (nodes[id].next == -1) {
59
+ if (atomic_read(&mutex_head) == id &&
60
+ atomic_cmpxchg(&mutex_head, id, -1) == id) {
61
+ /* Last item in the list, exit. */
62
+ return;
63
+ }
64
+ while (atomic_read(&nodes[id].next) == -1) {
65
+ /* mcs_mutex_lock did the xchg, but has not updated
66
+ * nodes[prev].next yet.
67
+ */
68
+ }
69
+ }
70
+
71
+ /* Wake up the next in line. */
72
+ next = nodes[id].next;
73
+ nodes[next].locked = 0;
74
+ qemu_futex_wake(&nodes[next].locked, 1);
75
+}
76
+
77
+static void test_multi_fair_mutex_entry(void *opaque)
78
+{
79
+ while (!atomic_mb_read(&now_stopping)) {
80
+ mcs_mutex_lock();
81
+ counter++;
82
+ mcs_mutex_unlock();
83
+ atomic_inc(&atomic_counter);
84
+ }
85
+ atomic_dec(&running);
86
+}
87
+
88
+static void test_multi_fair_mutex(int threads, int seconds)
89
+{
90
+ int i;
91
+
92
+ assert(mutex_head == -1);
93
+ counter = 0;
94
+ atomic_counter = 0;
95
+ now_stopping = false;
96
+
97
+ create_aio_contexts();
98
+ assert(threads <= NUM_CONTEXTS);
99
+ running = threads;
100
+ for (i = 0; i < threads; i++) {
101
+ Coroutine *co1 = qemu_coroutine_create(test_multi_fair_mutex_entry, NULL);
102
+ aio_co_schedule(ctx[i], co1);
103
+ }
104
+
105
+ g_usleep(seconds * 1000000);
106
+
107
+ atomic_mb_set(&now_stopping, true);
108
+ while (running > 0) {
109
+ g_usleep(100000);
110
+ }
111
+
112
+ join_aio_contexts();
113
+ g_test_message("%d iterations/second\n", counter / seconds);
114
+ g_assert_cmpint(counter, ==, atomic_counter);
115
+}
116
+
117
+static void test_multi_fair_mutex_1(void)
118
+{
119
+ test_multi_fair_mutex(NUM_CONTEXTS, 1);
120
+}
121
+
122
+static void test_multi_fair_mutex_10(void)
123
+{
124
+ test_multi_fair_mutex(NUM_CONTEXTS, 10);
125
+}
126
+#endif
127
+
128
+/* Same test with pthread mutexes, for performance comparison and
129
+ * portability. */
130
+
131
+static QemuMutex mutex;
132
+
133
+static void test_multi_mutex_entry(void *opaque)
134
+{
135
+ while (!atomic_mb_read(&now_stopping)) {
136
+ qemu_mutex_lock(&mutex);
137
+ counter++;
138
+ qemu_mutex_unlock(&mutex);
139
+ atomic_inc(&atomic_counter);
140
+ }
141
+ atomic_dec(&running);
142
+}
143
+
144
+static void test_multi_mutex(int threads, int seconds)
145
+{
146
+ int i;
147
+
148
+ qemu_mutex_init(&mutex);
149
+ counter = 0;
150
+ atomic_counter = 0;
151
+ now_stopping = false;
152
+
153
+ create_aio_contexts();
154
+ assert(threads <= NUM_CONTEXTS);
155
+ running = threads;
156
+ for (i = 0; i < threads; i++) {
157
+ Coroutine *co1 = qemu_coroutine_create(test_multi_mutex_entry, NULL);
158
+ aio_co_schedule(ctx[i], co1);
159
+ }
160
+
161
+ g_usleep(seconds * 1000000);
162
+
163
+ atomic_mb_set(&now_stopping, true);
164
+ while (running > 0) {
165
+ g_usleep(100000);
166
+ }
167
+
168
+ join_aio_contexts();
169
+ g_test_message("%d iterations/second\n", counter / seconds);
170
+ g_assert_cmpint(counter, ==, atomic_counter);
171
+}
172
+
173
+static void test_multi_mutex_1(void)
174
+{
175
+ test_multi_mutex(NUM_CONTEXTS, 1);
176
+}
177
+
178
+static void test_multi_mutex_10(void)
179
+{
180
+ test_multi_mutex(NUM_CONTEXTS, 10);
181
+}
182
+
183
/* End of tests. */
184
185
int main(int argc, char **argv)
186
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
187
g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_1);
188
g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_1);
189
g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_3);
190
+#ifdef CONFIG_LINUX
191
+ g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_1);
192
+#endif
193
+ g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_1);
194
} else {
195
g_test_add_func("/aio/multi/schedule", test_multi_co_schedule_10);
196
g_test_add_func("/aio/multi/mutex/contended", test_multi_co_mutex_10);
197
g_test_add_func("/aio/multi/mutex/handoff", test_multi_co_mutex_2_30);
198
+#ifdef CONFIG_LINUX
199
+ g_test_add_func("/aio/multi/mutex/mcs", test_multi_fair_mutex_10);
200
+#endif
201
+ g_test_add_func("/aio/multi/mutex/pthread", test_multi_mutex_10);
73
}
202
}
74
QLIST_REMOVE(task, list);
203
return g_test_run();
75
qemu_co_queue_restart_all(&task->wait_queue);
204
}
76
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
77
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
78
assert(ret >= 0); /* never fail */
79
cur_bytes = MIN(cur_bytes, status_bytes);
80
- block_copy_task_shrink(s, task, cur_bytes);
81
+ block_copy_task_shrink(task, cur_bytes);
82
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
83
- block_copy_task_end(s, task, 0);
84
+ block_copy_task_end(task, 0);
85
progress_set_remaining(s->progress,
86
bdrv_get_dirty_count(s->copy_bitmap) +
87
s->in_flight_bytes);
88
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
89
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
90
error_is_read);
91
co_put_to_shres(s->mem, cur_bytes);
92
- block_copy_task_end(s, task, ret);
93
+ block_copy_task_end(task, ret);
94
if (ret < 0) {
95
return ret;
96
}
97
--
205
--
98
2.26.2
206
2.9.3
99
207
100
208
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
Minor cleanup for HMP functions; helps with line length and consolidates
3
This will avoid forward references in the next patch. It is also
4
HMP helpers through one implementation function.
4
more logical because CoQueue is not anymore the basic primitive.
5
5
6
Although we are adding a universal toggle to turn QMP logging on or off,
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
many existing callers to hmp functions don't expect that output to be
7
Reviewed-by: Fam Zheng <famz@redhat.com>
8
logged, which causes quite a few changes in the test output.
8
Message-id: 20170213181244.16297-5-pbonzini@redhat.com
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
---
11
include/qemu/coroutine.h | 89 ++++++++++++++++++++++++------------------------
12
1 file changed, 44 insertions(+), 45 deletions(-)
9
13
10
For now, offer a use_log parameter.
14
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
11
12
Typing notes:
13
14
QMPResponse is just an alias for Dict[str, Any]. It holds no special
15
meanings and it is not a formal subtype of Dict[str, Any]. It is best
16
thought of as a lexical synonym.
17
18
We may well wish to add stricter subtypes in the future for certain
19
shapes of data that are not formalized as Python objects, at which point
20
we can simply retire the alias and allow mypy to more strictly check
21
usages of the name.
22
23
Signed-off-by: John Snow <jsnow@redhat.com>
24
Message-Id: <20200331000014.11581-11-jsnow@redhat.com>
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
26
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
28
---
29
tests/qemu-iotests/iotests.py | 39 +++++++++++++++++++++--------------
30
1 file changed, 24 insertions(+), 15 deletions(-)
31
32
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
33
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
34
--- a/tests/qemu-iotests/iotests.py
16
--- a/include/qemu/coroutine.h
35
+++ b/tests/qemu-iotests/iotests.py
17
+++ b/include/qemu/coroutine.h
36
@@ -XXX,XX +XXX,XX @@ from qemu import qtest
18
@@ -XXX,XX +XXX,XX @@ bool qemu_in_coroutine(void);
37
19
*/
38
assert sys.version_info >= (3, 6)
20
bool qemu_coroutine_entered(Coroutine *co);
39
21
40
+# Type Aliases
22
-
41
+QMPResponse = Dict[str, Any]
23
-/**
24
- * CoQueues are a mechanism to queue coroutines in order to continue executing
25
- * them later. They provide the fundamental primitives on which coroutine locks
26
- * are built.
27
- */
28
-typedef struct CoQueue {
29
- QSIMPLEQ_HEAD(, Coroutine) entries;
30
-} CoQueue;
31
-
32
-/**
33
- * Initialise a CoQueue. This must be called before any other operation is used
34
- * on the CoQueue.
35
- */
36
-void qemu_co_queue_init(CoQueue *queue);
37
-
38
-/**
39
- * Adds the current coroutine to the CoQueue and transfers control to the
40
- * caller of the coroutine.
41
- */
42
-void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
43
-
44
-/**
45
- * Restarts the next coroutine in the CoQueue and removes it from the queue.
46
- *
47
- * Returns true if a coroutine was restarted, false if the queue is empty.
48
- */
49
-bool coroutine_fn qemu_co_queue_next(CoQueue *queue);
50
-
51
-/**
52
- * Restarts all coroutines in the CoQueue and leaves the queue empty.
53
- */
54
-void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue);
55
-
56
-/**
57
- * Enter the next coroutine in the queue
58
- */
59
-bool qemu_co_enter_next(CoQueue *queue);
60
-
61
-/**
62
- * Checks if the CoQueue is empty.
63
- */
64
-bool qemu_co_queue_empty(CoQueue *queue);
65
-
66
-
67
/**
68
* Provides a mutex that can be used to synchronise coroutines
69
*/
70
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_lock(CoMutex *mutex);
71
*/
72
void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
73
74
+
75
+/**
76
+ * CoQueues are a mechanism to queue coroutines in order to continue executing
77
+ * them later.
78
+ */
79
+typedef struct CoQueue {
80
+ QSIMPLEQ_HEAD(, Coroutine) entries;
81
+} CoQueue;
82
+
83
+/**
84
+ * Initialise a CoQueue. This must be called before any other operation is used
85
+ * on the CoQueue.
86
+ */
87
+void qemu_co_queue_init(CoQueue *queue);
88
+
89
+/**
90
+ * Adds the current coroutine to the CoQueue and transfers control to the
91
+ * caller of the coroutine.
92
+ */
93
+void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
94
+
95
+/**
96
+ * Restarts the next coroutine in the CoQueue and removes it from the queue.
97
+ *
98
+ * Returns true if a coroutine was restarted, false if the queue is empty.
99
+ */
100
+bool coroutine_fn qemu_co_queue_next(CoQueue *queue);
101
+
102
+/**
103
+ * Restarts all coroutines in the CoQueue and leaves the queue empty.
104
+ */
105
+void coroutine_fn qemu_co_queue_restart_all(CoQueue *queue);
106
+
107
+/**
108
+ * Enter the next coroutine in the queue
109
+ */
110
+bool qemu_co_enter_next(CoQueue *queue);
111
+
112
+/**
113
+ * Checks if the CoQueue is empty.
114
+ */
115
+bool qemu_co_queue_empty(CoQueue *queue);
42
+
116
+
43
+
117
+
44
faulthandler.enable()
118
typedef struct CoRwlock {
45
119
bool writer;
46
# This will not work if arguments contain spaces but is necessary if we
120
int reader;
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
48
self._args.append(addr)
49
return self
50
51
- def pause_drive(self, drive, event=None):
52
- '''Pause drive r/w operations'''
53
+ def hmp(self, command_line: str, use_log: bool = False) -> QMPResponse:
54
+ cmd = 'human-monitor-command'
55
+ kwargs = {'command-line': command_line}
56
+ if use_log:
57
+ return self.qmp_log(cmd, **kwargs)
58
+ else:
59
+ return self.qmp(cmd, **kwargs)
60
+
61
+ def pause_drive(self, drive: str, event: Optional[str] = None) -> None:
62
+ """Pause drive r/w operations"""
63
if not event:
64
self.pause_drive(drive, "read_aio")
65
self.pause_drive(drive, "write_aio")
66
return
67
- self.qmp('human-monitor-command',
68
- command_line='qemu-io %s "break %s bp_%s"'
69
- % (drive, event, drive))
70
-
71
- def resume_drive(self, drive):
72
- self.qmp('human-monitor-command',
73
- command_line='qemu-io %s "remove_break bp_%s"'
74
- % (drive, drive))
75
-
76
- def hmp_qemu_io(self, drive, cmd):
77
- '''Write to a given drive using an HMP command'''
78
- return self.qmp('human-monitor-command',
79
- command_line='qemu-io %s "%s"' % (drive, cmd))
80
+ self.hmp(f'qemu-io {drive} "break {event} bp_{drive}"')
81
+
82
+ def resume_drive(self, drive: str) -> None:
83
+ """Resume drive r/w operations"""
84
+ self.hmp(f'qemu-io {drive} "remove_break bp_{drive}"')
85
+
86
+ def hmp_qemu_io(self, drive: str, cmd: str,
87
+ use_log: bool = False) -> QMPResponse:
88
+ """Write to a given drive using an HMP command"""
89
+ return self.hmp(f'qemu-io {drive} "{cmd}"', use_log=use_log)
90
91
def flatten_qmp_object(self, obj, output=None, basestr=''):
92
if output is None:
93
--
121
--
94
2.26.2
122
2.9.3
95
123
96
124
diff view generated by jsdifflib
1
From: Eric Blake <eblake@redhat.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
It's been a while since we got rid of the sector-based bdrv_read and
3
All that CoQueue needs in order to become thread-safe is help
4
bdrv_write (commit 2e11d756); let's finish the job on a few remaining
4
from an external mutex. Add this to the API.
5
comments.
5
6
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
Signed-off-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Fam Zheng <famz@redhat.com>
8
Message-Id: <20200428213807.776655-1-eblake@redhat.com>
8
Message-id: 20170213181244.16297-6-pbonzini@redhat.com
9
Reviewed-by: Alberto Garcia <berto@igalia.com>
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
10
---
12
block/io.c | 3 ++-
11
include/qemu/coroutine.h | 8 +++++---
13
block/qcow2-refcount.c | 2 +-
12
block/backup.c | 2 +-
14
block/vvfat.c | 10 +++++-----
13
block/io.c | 4 ++--
15
tests/qemu-iotests/001 | 2 +-
14
block/nbd-client.c | 2 +-
16
tests/qemu-iotests/052 | 2 +-
15
block/qcow2-cluster.c | 4 +---
17
tests/qemu-iotests/134 | 2 +-
16
block/sheepdog.c | 2 +-
18
tests/qemu-iotests/188 | 2 +-
17
block/throttle-groups.c | 2 +-
19
7 files changed, 12 insertions(+), 11 deletions(-)
18
hw/9pfs/9p.c | 2 +-
20
19
util/qemu-coroutine-lock.c | 24 +++++++++++++++++++++---
20
9 files changed, 34 insertions(+), 16 deletions(-)
21
22
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/qemu/coroutine.h
25
+++ b/include/qemu/coroutine.h
26
@@ -XXX,XX +XXX,XX @@ void coroutine_fn qemu_co_mutex_unlock(CoMutex *mutex);
27
28
/**
29
* CoQueues are a mechanism to queue coroutines in order to continue executing
30
- * them later.
31
+ * them later. They are similar to condition variables, but they need help
32
+ * from an external mutex in order to maintain thread-safety.
33
*/
34
typedef struct CoQueue {
35
QSIMPLEQ_HEAD(, Coroutine) entries;
36
@@ -XXX,XX +XXX,XX @@ void qemu_co_queue_init(CoQueue *queue);
37
38
/**
39
* Adds the current coroutine to the CoQueue and transfers control to the
40
- * caller of the coroutine.
41
+ * caller of the coroutine. The mutex is unlocked during the wait and
42
+ * locked again afterwards.
43
*/
44
-void coroutine_fn qemu_co_queue_wait(CoQueue *queue);
45
+void coroutine_fn qemu_co_queue_wait(CoQueue *queue, CoMutex *mutex);
46
47
/**
48
* Restarts the next coroutine in the CoQueue and removes it from the queue.
49
diff --git a/block/backup.c b/block/backup.c
50
index XXXXXXX..XXXXXXX 100644
51
--- a/block/backup.c
52
+++ b/block/backup.c
53
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn wait_for_overlapping_requests(BackupBlockJob *job,
54
retry = false;
55
QLIST_FOREACH(req, &job->inflight_reqs, list) {
56
if (end > req->start && start < req->end) {
57
- qemu_co_queue_wait(&req->wait_queue);
58
+ qemu_co_queue_wait(&req->wait_queue, NULL);
59
retry = true;
60
break;
61
}
21
diff --git a/block/io.c b/block/io.c
62
diff --git a/block/io.c b/block/io.c
22
index XXXXXXX..XXXXXXX 100644
63
index XXXXXXX..XXXXXXX 100644
23
--- a/block/io.c
64
--- a/block/io.c
24
+++ b/block/io.c
65
+++ b/block/io.c
25
@@ -XXX,XX +XXX,XX @@ int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset,
66
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn wait_serialising_requests(BdrvTrackedRequest *self)
26
* flags are passed through to bdrv_pwrite_zeroes (e.g. BDRV_REQ_MAY_UNMAP,
67
* (instead of producing a deadlock in the former case). */
27
* BDRV_REQ_FUA).
68
if (!req->waiting_for) {
28
*
69
self->waiting_for = req;
29
- * Returns < 0 on error, 0 on success. For error codes see bdrv_write().
70
- qemu_co_queue_wait(&req->wait_queue);
30
+ * Returns < 0 on error, 0 on success. For error codes see bdrv_pwrite().
71
+ qemu_co_queue_wait(&req->wait_queue, NULL);
31
*/
72
self->waiting_for = NULL;
32
int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
73
retry = true;
74
waited = true;
75
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
76
77
/* Wait until any previous flushes are completed */
78
while (bs->active_flush_req) {
79
- qemu_co_queue_wait(&bs->flush_queue);
80
+ qemu_co_queue_wait(&bs->flush_queue, NULL);
81
}
82
83
bs->active_flush_req = true;
84
diff --git a/block/nbd-client.c b/block/nbd-client.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/nbd-client.c
87
+++ b/block/nbd-client.c
88
@@ -XXX,XX +XXX,XX @@ static void nbd_coroutine_start(NBDClientSession *s,
89
/* Poor man semaphore. The free_sema is locked when no other request
90
* can be accepted, and unlocked after receiving one reply. */
91
if (s->in_flight == MAX_NBD_REQUESTS) {
92
- qemu_co_queue_wait(&s->free_sema);
93
+ qemu_co_queue_wait(&s->free_sema, NULL);
94
assert(s->in_flight < MAX_NBD_REQUESTS);
95
}
96
s->in_flight++;
97
diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/qcow2-cluster.c
100
+++ b/block/qcow2-cluster.c
101
@@ -XXX,XX +XXX,XX @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset,
102
if (bytes == 0) {
103
/* Wait for the dependency to complete. We need to recheck
104
* the free/allocated clusters when we continue. */
105
- qemu_co_mutex_unlock(&s->lock);
106
- qemu_co_queue_wait(&old_alloc->dependent_requests);
107
- qemu_co_mutex_lock(&s->lock);
108
+ qemu_co_queue_wait(&old_alloc->dependent_requests, &s->lock);
109
return -EAGAIN;
110
}
111
}
112
diff --git a/block/sheepdog.c b/block/sheepdog.c
113
index XXXXXXX..XXXXXXX 100644
114
--- a/block/sheepdog.c
115
+++ b/block/sheepdog.c
116
@@ -XXX,XX +XXX,XX @@ static void wait_for_overlapping_aiocb(BDRVSheepdogState *s, SheepdogAIOCB *acb)
117
retry:
118
QLIST_FOREACH(cb, &s->inflight_aiocb_head, aiocb_siblings) {
119
if (AIOCBOverlapping(acb, cb)) {
120
- qemu_co_queue_wait(&s->overlapping_queue);
121
+ qemu_co_queue_wait(&s->overlapping_queue, NULL);
122
goto retry;
123
}
124
}
125
diff --git a/block/throttle-groups.c b/block/throttle-groups.c
126
index XXXXXXX..XXXXXXX 100644
127
--- a/block/throttle-groups.c
128
+++ b/block/throttle-groups.c
129
@@ -XXX,XX +XXX,XX @@ void coroutine_fn throttle_group_co_io_limits_intercept(BlockBackend *blk,
130
if (must_wait || blkp->pending_reqs[is_write]) {
131
blkp->pending_reqs[is_write]++;
132
qemu_mutex_unlock(&tg->lock);
133
- qemu_co_queue_wait(&blkp->throttled_reqs[is_write]);
134
+ qemu_co_queue_wait(&blkp->throttled_reqs[is_write], NULL);
135
qemu_mutex_lock(&tg->lock);
136
blkp->pending_reqs[is_write]--;
137
}
138
diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/hw/9pfs/9p.c
141
+++ b/hw/9pfs/9p.c
142
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn v9fs_flush(void *opaque)
143
/*
144
* Wait for pdu to complete.
145
*/
146
- qemu_co_queue_wait(&cancel_pdu->complete);
147
+ qemu_co_queue_wait(&cancel_pdu->complete, NULL);
148
cancel_pdu->cancelled = 0;
149
pdu_free(cancel_pdu);
150
}
151
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
152
index XXXXXXX..XXXXXXX 100644
153
--- a/util/qemu-coroutine-lock.c
154
+++ b/util/qemu-coroutine-lock.c
155
@@ -XXX,XX +XXX,XX @@ void qemu_co_queue_init(CoQueue *queue)
156
QSIMPLEQ_INIT(&queue->entries);
157
}
158
159
-void coroutine_fn qemu_co_queue_wait(CoQueue *queue)
160
+void coroutine_fn qemu_co_queue_wait(CoQueue *queue, CoMutex *mutex)
33
{
161
{
34
@@ -XXX,XX +XXX,XX @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags)
162
Coroutine *self = qemu_coroutine_self();
35
}
163
QSIMPLEQ_INSERT_TAIL(&queue->entries, self, co_queue_next);
164
+
165
+ if (mutex) {
166
+ qemu_co_mutex_unlock(mutex);
167
+ }
168
+
169
+ /* There is no race condition here. Other threads will call
170
+ * aio_co_schedule on our AioContext, which can reenter this
171
+ * coroutine but only after this yield and after the main loop
172
+ * has gone through the next iteration.
173
+ */
174
qemu_coroutine_yield();
175
assert(qemu_in_coroutine());
176
+
177
+ /* TODO: OSv implements wait morphing here, where the wakeup
178
+ * primitive automatically places the woken coroutine on the
179
+ * mutex's queue. This avoids the thundering herd effect.
180
+ */
181
+ if (mutex) {
182
+ qemu_co_mutex_lock(mutex);
183
+ }
36
}
184
}
37
185
38
+/* return < 0 if error. See bdrv_pwrite() for the return codes */
186
/**
39
int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov)
187
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_rdlock(CoRwlock *lock)
40
{
188
Coroutine *self = qemu_coroutine_self();
41
int ret;
189
42
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
190
while (lock->writer) {
43
index XXXXXXX..XXXXXXX 100644
191
- qemu_co_queue_wait(&lock->queue);
44
--- a/block/qcow2-refcount.c
192
+ qemu_co_queue_wait(&lock->queue, NULL);
45
+++ b/block/qcow2-refcount.c
193
}
46
@@ -XXX,XX +XXX,XX @@ fail:
194
lock->reader++;
47
* - 0 if writing to this offset will not affect the mentioned metadata
195
self->locks_held++;
48
* - a positive QCow2MetadataOverlap value indicating one overlapping section
196
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_wrlock(CoRwlock *lock)
49
* - a negative value (-errno) indicating an error while performing a check,
197
Coroutine *self = qemu_coroutine_self();
50
- * e.g. when bdrv_read failed on QCOW2_OL_INACTIVE_L2
198
51
+ * e.g. when bdrv_pread failed on QCOW2_OL_INACTIVE_L2
199
while (lock->writer || lock->reader) {
52
*/
200
- qemu_co_queue_wait(&lock->queue);
53
int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset,
201
+ qemu_co_queue_wait(&lock->queue, NULL);
54
int64_t size)
202
}
55
diff --git a/block/vvfat.c b/block/vvfat.c
203
lock->writer = true;
56
index XXXXXXX..XXXXXXX 100644
204
self->locks_held++;
57
--- a/block/vvfat.c
58
+++ b/block/vvfat.c
59
@@ -XXX,XX +XXX,XX @@ DLOG(checkpoint());
60
* - get modified FAT
61
* - compare the two FATs (TODO)
62
* - get buffer for marking used clusters
63
- * - recurse direntries from root (using bs->bdrv_read to make
64
+ * - recurse direntries from root (using bs->bdrv_pread to make
65
* sure to get the new data)
66
* - check that the FAT agrees with the size
67
* - count the number of clusters occupied by this directory and
68
@@ -XXX,XX +XXX,XX @@ static int handle_deletes(BDRVVVFATState* s)
69
/*
70
* synchronize mapping with new state:
71
*
72
- * - copy FAT (with bdrv_read)
73
+ * - copy FAT (with bdrv_pread)
74
* - mark all filenames corresponding to mappings as deleted
75
- * - recurse direntries from root (using bs->bdrv_read)
76
+ * - recurse direntries from root (using bs->bdrv_pread)
77
* - delete files corresponding to mappings marked as deleted
78
*/
79
static int do_commit(BDRVVVFATState* s)
80
@@ -XXX,XX +XXX,XX @@ static int do_commit(BDRVVVFATState* s)
81
return ret;
82
}
83
84
- /* copy FAT (with bdrv_read) */
85
+ /* copy FAT (with bdrv_pread) */
86
memcpy(s->fat.pointer, s->fat2, 0x200 * s->sectors_per_fat);
87
88
- /* recurse direntries from root (using bs->bdrv_read) */
89
+ /* recurse direntries from root (using bs->bdrv_pread) */
90
ret = commit_direntries(s, 0, -1);
91
if (ret) {
92
fprintf(stderr, "Fatal: error while committing (%d)\n", ret);
93
diff --git a/tests/qemu-iotests/001 b/tests/qemu-iotests/001
94
index XXXXXXX..XXXXXXX 100755
95
--- a/tests/qemu-iotests/001
96
+++ b/tests/qemu-iotests/001
97
@@ -XXX,XX +XXX,XX @@
98
#!/usr/bin/env bash
99
#
100
-# Test simple read/write using plain bdrv_read/bdrv_write
101
+# Test simple read/write using plain bdrv_pread/bdrv_pwrite
102
#
103
# Copyright (C) 2009 Red Hat, Inc.
104
#
105
diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052
106
index XXXXXXX..XXXXXXX 100755
107
--- a/tests/qemu-iotests/052
108
+++ b/tests/qemu-iotests/052
109
@@ -XXX,XX +XXX,XX @@
110
#!/usr/bin/env bash
111
#
112
-# Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT
113
+# Test bdrv_pread/bdrv_pwrite using BDRV_O_SNAPSHOT
114
#
115
# Copyright (C) 2013 Red Hat, Inc.
116
#
117
diff --git a/tests/qemu-iotests/134 b/tests/qemu-iotests/134
118
index XXXXXXX..XXXXXXX 100755
119
--- a/tests/qemu-iotests/134
120
+++ b/tests/qemu-iotests/134
121
@@ -XXX,XX +XXX,XX @@
122
#!/usr/bin/env bash
123
#
124
-# Test encrypted read/write using plain bdrv_read/bdrv_write
125
+# Test encrypted read/write using plain bdrv_pread/bdrv_pwrite
126
#
127
# Copyright (C) 2015 Red Hat, Inc.
128
#
129
diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188
130
index XXXXXXX..XXXXXXX 100755
131
--- a/tests/qemu-iotests/188
132
+++ b/tests/qemu-iotests/188
133
@@ -XXX,XX +XXX,XX @@
134
#!/usr/bin/env bash
135
#
136
-# Test encrypted read/write using plain bdrv_read/bdrv_write
137
+# Test encrypted read/write using plain bdrv_pread/bdrv_pwrite
138
#
139
# Copyright (C) 2017 Red Hat, Inc.
140
#
141
--
205
--
142
2.26.2
206
2.9.3
143
207
144
208
diff view generated by jsdifflib
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
We are going to use aio-task-pool API, so tasks will be handled in
3
This adds a CoMutex around the existing CoQueue. Because the write-side
4
parallel. We need therefore separate allocated task on each iteration.
4
can just take CoMutex, the old "writer" field is not necessary anymore.
5
Introduce this logic now.
5
Instead of removing it altogether, count the number of pending writers
6
during a read-side critical section and forbid further readers from
7
entering.
6
8
7
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
9
Message-Id: <20200429130847.28124-3-vsementsov@virtuozzo.com>
11
Message-id: 20170213181244.16297-7-pbonzini@redhat.com
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
13
---
12
block/block-copy.c | 18 +++++++++++-------
14
include/qemu/coroutine.h | 3 ++-
13
1 file changed, 11 insertions(+), 7 deletions(-)
15
util/qemu-coroutine-lock.c | 35 ++++++++++++++++++++++++-----------
16
2 files changed, 26 insertions(+), 12 deletions(-)
14
17
15
diff --git a/block/block-copy.c b/block/block-copy.c
18
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
16
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
17
--- a/block/block-copy.c
20
--- a/include/qemu/coroutine.h
18
+++ b/block/block-copy.c
21
+++ b/include/qemu/coroutine.h
19
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn block_copy_wait_one(BlockCopyState *s, int64_t offset,
22
@@ -XXX,XX +XXX,XX @@ bool qemu_co_queue_empty(CoQueue *queue);
23
24
25
typedef struct CoRwlock {
26
- bool writer;
27
+ int pending_writer;
28
int reader;
29
+ CoMutex mutex;
30
CoQueue queue;
31
} CoRwlock;
32
33
diff --git a/util/qemu-coroutine-lock.c b/util/qemu-coroutine-lock.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/util/qemu-coroutine-lock.c
36
+++ b/util/qemu-coroutine-lock.c
37
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_init(CoRwlock *lock)
38
{
39
memset(lock, 0, sizeof(*lock));
40
qemu_co_queue_init(&lock->queue);
41
+ qemu_co_mutex_init(&lock->mutex);
20
}
42
}
21
43
22
/* Called only on full-dirty region */
44
void qemu_co_rwlock_rdlock(CoRwlock *lock)
23
-static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
24
- int64_t offset, int64_t bytes)
25
+static BlockCopyTask *block_copy_task_create(BlockCopyState *s,
26
+ int64_t offset, int64_t bytes)
27
{
45
{
28
+ BlockCopyTask *task = g_new(BlockCopyTask, 1);
46
Coroutine *self = qemu_coroutine_self();
47
48
- while (lock->writer) {
49
- qemu_co_queue_wait(&lock->queue, NULL);
50
+ qemu_co_mutex_lock(&lock->mutex);
51
+ /* For fairness, wait if a writer is in line. */
52
+ while (lock->pending_writer) {
53
+ qemu_co_queue_wait(&lock->queue, &lock->mutex);
54
}
55
lock->reader++;
56
+ qemu_co_mutex_unlock(&lock->mutex);
29
+
57
+
30
assert(!find_conflicting_task(s, offset, bytes));
58
+ /* The rest of the read-side critical section is run without the mutex. */
31
59
self->locks_held++;
32
bdrv_reset_dirty_bitmap(s->copy_bitmap, offset, bytes);
60
}
33
@@ -XXX,XX +XXX,XX @@ static void block_copy_task_begin(BlockCopyState *s, BlockCopyTask *task,
61
34
task->bytes = bytes;
62
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_unlock(CoRwlock *lock)
35
qemu_co_queue_init(&task->wait_queue);
63
Coroutine *self = qemu_coroutine_self();
36
QLIST_INSERT_HEAD(&s->tasks, task, list);
64
65
assert(qemu_in_coroutine());
66
- if (lock->writer) {
67
- lock->writer = false;
68
+ if (!lock->reader) {
69
+ /* The critical section started in qemu_co_rwlock_wrlock. */
70
qemu_co_queue_restart_all(&lock->queue);
71
} else {
72
+ self->locks_held--;
37
+
73
+
38
+ return task;
74
+ qemu_co_mutex_lock(&lock->mutex);
75
lock->reader--;
76
assert(lock->reader >= 0);
77
/* Wakeup only one waiting writer */
78
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_unlock(CoRwlock *lock)
79
qemu_co_queue_next(&lock->queue);
80
}
81
}
82
- self->locks_held--;
83
+ qemu_co_mutex_unlock(&lock->mutex);
39
}
84
}
40
85
41
/*
86
void qemu_co_rwlock_wrlock(CoRwlock *lock)
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
87
{
43
assert(QEMU_IS_ALIGNED(bytes, s->cluster_size));
88
- Coroutine *self = qemu_coroutine_self();
44
89
-
45
while (bytes) {
90
- while (lock->writer || lock->reader) {
46
- BlockCopyTask task;
91
- qemu_co_queue_wait(&lock->queue, NULL);
47
+ g_autofree BlockCopyTask *task = NULL;
92
+ qemu_co_mutex_lock(&lock->mutex);
48
int64_t next_zero, cur_bytes, status_bytes;
93
+ lock->pending_writer++;
49
94
+ while (lock->reader) {
50
if (!bdrv_dirty_bitmap_get(s->copy_bitmap, offset)) {
95
+ qemu_co_queue_wait(&lock->queue, &lock->mutex);
51
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
96
}
52
assert(next_zero < offset + cur_bytes); /* no need to do MIN() */
97
- lock->writer = true;
53
cur_bytes = next_zero - offset;
98
- self->locks_held++;
54
}
99
+ lock->pending_writer--;
55
- block_copy_task_begin(s, &task, offset, cur_bytes);
100
+
56
+ task = block_copy_task_create(s, offset, cur_bytes);
101
+ /* The rest of the write-side critical section is run with
57
102
+ * the mutex taken, so that lock->reader remains zero.
58
ret = block_copy_block_status(s, offset, cur_bytes, &status_bytes);
103
+ * There is no need to update self->locks_held.
59
assert(ret >= 0); /* never fail */
104
+ */
60
cur_bytes = MIN(cur_bytes, status_bytes);
105
}
61
- block_copy_task_shrink(s, &task, cur_bytes);
62
+ block_copy_task_shrink(s, task, cur_bytes);
63
if (s->skip_unallocated && !(ret & BDRV_BLOCK_ALLOCATED)) {
64
- block_copy_task_end(s, &task, 0);
65
+ block_copy_task_end(s, task, 0);
66
progress_set_remaining(s->progress,
67
bdrv_get_dirty_count(s->copy_bitmap) +
68
s->in_flight_bytes);
69
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_copy_dirty_clusters(BlockCopyState *s,
70
ret = block_copy_do_copy(s, offset, cur_bytes, ret & BDRV_BLOCK_ZERO,
71
error_is_read);
72
co_put_to_shres(s->mem, cur_bytes);
73
- block_copy_task_end(s, &task, ret);
74
+ block_copy_task_end(s, task, ret);
75
if (ret < 0) {
76
return ret;
77
}
78
--
106
--
79
2.26.2
107
2.9.3
80
108
81
109
diff view generated by jsdifflib