1
The following changes since commit 4f50c1673a89b07f376ce5c42d22d79a79cd466d:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/ehabkost/tags/x86-next-pull-request' into staging (2018-05-22 09:43:58 +0100)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +0000)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to bdebdc712b06ba82e103d617c335830682cde242:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
qemu-iotests: Test job-* with block jobs (2018-05-23 14:30:52 +0200)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
16
- Generic background jobs
17
- qemu-iotests fixes for NFS and the 'migration' group
18
- sheepdog: Minor code simplification
19
15
20
----------------------------------------------------------------
16
----------------------------------------------------------------
21
Kevin Wolf (45):
17
Doug Gale (1):
22
qemu-iotests: Fix paths for NFS
18
nvme: Add tracing
23
qemu-iotests: Filter NFS paths
24
qemu-iotests: 086 doesn't work with NFS
25
qemu-iotests: Add more tests to "migration" group
26
qemu-iotests: Remove MIG_SOCKET from non-migration tests
27
blockjob: Update block-job-pause/resume documentation
28
blockjob: Improve BlockJobInfo.offset/len documentation
29
job: Create Job, JobDriver and job_create()
30
job: Rename BlockJobType into JobType
31
job: Add JobDriver.job_type
32
job: Add job_delete()
33
job: Maintain a list of all jobs
34
job: Move state transitions to Job
35
job: Add reference counting
36
job: Move cancelled to Job
37
job: Add Job.aio_context
38
job: Move defer_to_main_loop to Job
39
job: Move coroutine and related code to Job
40
job: Add job_sleep_ns()
41
job: Move pause/resume functions to Job
42
job: Replace BlockJob.completed with job_is_completed()
43
job: Move BlockJobCreateFlags to Job
44
blockjob: Split block_job_event_pending()
45
job: Add job_event_*()
46
job: Move single job finalisation to Job
47
job: Convert block_job_cancel_async() to Job
48
job: Add job_drain()
49
job: Move .complete callback to Job
50
job: Move job_finish_sync() to Job
51
job: Switch transactions to JobTxn
52
job: Move transactions to Job
53
job: Move completion and cancellation to Job
54
block: Cancel job in bdrv_close_all() callers
55
job: Add job_yield()
56
job: Add job_dismiss()
57
job: Add job_is_ready()
58
job: Add job_transition_to_ready()
59
job: Move progress fields to Job
60
job: Introduce qapi/job.json
61
job: Add JOB_STATUS_CHANGE QMP event
62
job: Add lifecycle QMP commands
63
job: Add query-jobs QMP command
64
blockjob: Remove BlockJob.driver
65
iotests: Move qmp_to_opts() to VM
66
qemu-iotests: Test job-* with block jobs
67
19
68
Peter Maydell (1):
20
Edgar Kaziakhmedov (1):
69
sheepdog: Remove unnecessary NULL check in sd_prealloc()
21
qcow2: get rid of qcow2_backing_read1 routine
70
22
71
qapi/block-core.json | 116 +---
23
Fam Zheng (2):
72
qapi/job.json | 253 +++++++++
24
block: Open backing image in force share mode for size probe
73
qapi/qapi-schema.json | 1 +
25
block: Remove unused bdrv_requests_pending
74
include/block/block_int.h | 2 +-
75
include/block/blockjob.h | 324 +----------
76
include/block/blockjob_int.h | 176 +-----
77
include/qemu/job.h | 562 ++++++++++++++++++++
78
block.c | 2 +-
79
block/backup.c | 59 +-
80
block/commit.c | 44 +-
81
block/mirror.c | 113 ++--
82
block/replication.c | 10 +-
83
block/sheepdog.c | 4 +-
84
block/stream.c | 39 +-
85
blockdev.c | 68 +--
86
blockjob.c | 1094 ++++++--------------------------------
87
job-qmp.c | 188 +++++++
88
job.c | 1000 ++++++++++++++++++++++++++++++++++
89
qemu-img.c | 22 +-
90
qemu-nbd.c | 8 +-
91
tests/test-bdrv-drain.c | 63 ++-
92
tests/test-blockjob-txn.c | 74 +--
93
tests/test-blockjob.c | 141 ++---
94
vl.c | 1 +
95
MAINTAINERS | 4 +
96
Makefile | 9 +
97
Makefile.objs | 7 +-
98
block/trace-events | 5 -
99
tests/qemu-iotests/030 | 17 +-
100
tests/qemu-iotests/040 | 2 +
101
tests/qemu-iotests/041 | 23 +-
102
tests/qemu-iotests/086 | 2 +-
103
tests/qemu-iotests/094.out | 7 +
104
tests/qemu-iotests/095 | 2 +-
105
tests/qemu-iotests/095.out | 6 +
106
tests/qemu-iotests/109 | 2 +-
107
tests/qemu-iotests/109.out | 178 ++++++-
108
tests/qemu-iotests/124 | 8 +
109
tests/qemu-iotests/126.out | 2 +-
110
tests/qemu-iotests/127.out | 7 +
111
tests/qemu-iotests/141 | 13 +-
112
tests/qemu-iotests/141.out | 29 +
113
tests/qemu-iotests/144 | 2 +-
114
tests/qemu-iotests/144.out | 7 +
115
tests/qemu-iotests/155 | 2 +-
116
tests/qemu-iotests/156 | 2 +-
117
tests/qemu-iotests/156.out | 7 +
118
tests/qemu-iotests/185 | 14 +-
119
tests/qemu-iotests/185.out | 10 +
120
tests/qemu-iotests/191 | 6 +-
121
tests/qemu-iotests/191.out | 132 +++++
122
tests/qemu-iotests/219 | 209 ++++++++
123
tests/qemu-iotests/219.out | 327 ++++++++++++
124
tests/qemu-iotests/common.filter | 6 +-
125
tests/qemu-iotests/common.rc | 12 +-
126
tests/qemu-iotests/group | 11 +-
127
tests/qemu-iotests/iotests.py | 50 +-
128
trace-events | 14 +
129
58 files changed, 3601 insertions(+), 1897 deletions(-)
130
create mode 100644 qapi/job.json
131
create mode 100644 include/qemu/job.h
132
create mode 100644 job-qmp.c
133
create mode 100644 job.c
134
create mode 100755 tests/qemu-iotests/219
135
create mode 100644 tests/qemu-iotests/219.out
136
26
27
John Snow (1):
28
iotests: fix 197 for vpc
29
30
Kevin Wolf (27):
31
block: Formats don't need CONSISTENT_READ with NO_IO
32
block: Make bdrv_drain_invoke() recursive
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
34
test-bdrv-drain: Test BlockDriver callbacks for drain
35
block: bdrv_drain_recurse(): Remove unused begin parameter
36
block: Don't wait for requests in bdrv_drain*_end()
37
block: Unify order in drain functions
38
block: Don't acquire AioContext in hmp_qemu_io()
39
block: Document that x-blockdev-change breaks quorum children list
40
block: Assert drain_all is only called from main AioContext
41
block: Make bdrv_drain() driver callbacks non-recursive
42
test-bdrv-drain: Test callback for bdrv_drain
43
test-bdrv-drain: Test bs->quiesce_counter
44
blockjob: Pause job on draining any job BDS
45
test-bdrv-drain: Test drain vs. block jobs
46
block: Don't block_job_pause_all() in bdrv_drain_all()
47
block: Nested drain_end must still call callbacks
48
test-bdrv-drain: Test nested drain sections
49
block: Don't notify parents in drain call chain
50
block: Add bdrv_subtree_drained_begin/end()
51
test-bdrv-drain: Tests for bdrv_subtree_drain
52
test-bdrv-drain: Test behaviour in coroutine context
53
test-bdrv-drain: Recursive draining with multiple parents
54
block: Allow graph changes in subtree drained section
55
test-bdrv-drain: Test graph changes in drained section
56
commit: Simplify reopen of base
57
block: Keep nodes drained between reopen_queue/multiple
58
59
Thomas Huth (3):
60
block: Remove the obsolete -drive boot=on|off parameter
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
63
64
qapi/block-core.json | 4 +
65
block/qcow2.h | 3 -
66
include/block/block.h | 15 +-
67
include/block/block_int.h | 6 +-
68
block.c | 75 ++++-
69
block/commit.c | 8 +-
70
block/io.c | 164 +++++++---
71
block/qcow2.c | 51 +--
72
block/replication.c | 6 +
73
blockdev.c | 11 -
74
blockjob.c | 22 +-
75
hmp.c | 6 -
76
hw/block/nvme.c | 349 +++++++++++++++++----
77
qemu-io-cmds.c | 3 +
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
79
vl.c | 86 +-----
80
hw/block/trace-events | 93 ++++++
81
qemu-doc.texi | 29 +-
82
qemu-options.hx | 19 +-
83
tests/Makefile.include | 2 +
84
tests/qemu-iotests/197 | 4 +
85
tests/qemu-iotests/common.filter | 3 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
87
create mode 100644 tests/test-bdrv-drain.c
88
diff view generated by jsdifflib
Deleted patch
1
Test cases were trying to use nfs:// URLs as local filenames, which made
2
every test fail for NFS. With TEST_IMG and TEST_IMG_FILE set like for
3
the other protocols, NFS tests can pass again.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
8
tests/qemu-iotests/common.rc | 4 ++--
9
1 file changed, 2 insertions(+), 2 deletions(-)
10
11
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
12
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/common.rc
14
+++ b/tests/qemu-iotests/common.rc
15
@@ -XXX,XX +XXX,XX @@ else
16
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
17
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
18
elif [ "$IMGPROTO" = "nfs" ]; then
19
- TEST_DIR="nfs://127.0.0.1/$TEST_DIR"
20
- TEST_IMG=$TEST_DIR/t.$IMGFMT
21
+ TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
22
+ TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
23
elif [ "$IMGPROTO" = "vxhs" ]; then
24
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
25
TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT"
26
--
27
2.13.6
28
29
diff view generated by jsdifflib
1
qmp_to_opts() used to be a method of QMPTestCase, but recently we
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
started to add more Python test cases that don't make use of
2
in use as a mirror target. It is not enough for image formats, though,
3
QMPTestCase. In order to make the method usable there, move it to VM.
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
4
5
As this permission is geared towards whether the guest-visible data is
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
4
10
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
12
---
8
tests/qemu-iotests/041 | 6 +++---
13
block.c | 6 +++++-
9
tests/qemu-iotests/155 | 2 +-
14
1 file changed, 5 insertions(+), 1 deletion(-)
10
tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++++++---------------------
11
3 files changed, 27 insertions(+), 26 deletions(-)
12
15
13
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
16
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/041
16
+++ b/tests/qemu-iotests/041
17
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
18
'read-only': 'on' }
19
20
self.vm = iotests.VM()
21
- self.vm.add_blockdev(self.qmp_to_opts(blk0))
22
- self.vm.add_blockdev(self.qmp_to_opts(blk1))
23
- self.vm.add_blockdev(self.qmp_to_opts(blk2))
24
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
25
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk1))
26
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk2))
27
self.vm.launch()
28
29
def tearDown(self):
30
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
31
index XXXXXXX..XXXXXXX 100755
32
--- a/tests/qemu-iotests/155
33
+++ b/tests/qemu-iotests/155
34
@@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase):
35
'driver': iotests.imgfmt,
36
'file': {'driver': 'file',
37
'filename': source_img}}
38
- self.vm.add_blockdev(self.qmp_to_opts(blockdev))
39
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev))
40
self.vm.add_device('virtio-blk,id=qdev0,drive=source')
41
self.vm.launch()
42
43
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
44
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
45
--- a/tests/qemu-iotests/iotests.py
18
--- a/block.c
46
+++ b/tests/qemu-iotests/iotests.py
19
+++ b/block.c
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
48
return self.qmp('human-monitor-command',
21
assert(role == &child_backing || role == &child_file);
49
command_line='qemu-io %s "%s"' % (drive, cmd))
22
50
23
if (!backing) {
51
+ def flatten_qmp_object(self, obj, output=None, basestr=''):
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
52
+ if output is None:
53
+ output = dict()
54
+ if isinstance(obj, list):
55
+ for i in range(len(obj)):
56
+ self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
57
+ elif isinstance(obj, dict):
58
+ for key in obj:
59
+ self.flatten_qmp_object(obj[key], output, basestr + key + '.')
60
+ else:
61
+ output[basestr[:-1]] = obj # Strip trailing '.'
62
+ return output
63
+
25
+
64
+ def qmp_to_opts(self, obj):
26
/* Apart from the modifications below, the same permissions are
65
+ obj = self.flatten_qmp_object(obj)
27
* forwarded and left alone as for filters */
66
+ output_list = list()
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
67
+ for key in obj:
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
68
+ output_list += [key + '=' + obj[key]]
30
69
+ return ','.join(output_list)
31
/* bs->file always needs to be consistent because of the metadata. We
70
+
32
* can never allow other users to resize or write to it. */
71
+
33
- perm |= BLK_PERM_CONSISTENT_READ;
72
34
+ if (!(flags & BDRV_O_NO_IO)) {
73
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
35
+ perm |= BLK_PERM_CONSISTENT_READ;
74
36
+ }
75
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
76
self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
38
} else {
77
return d
39
/* We want consistent read from backing files if the parent needs it.
78
79
- def flatten_qmp_object(self, obj, output=None, basestr=''):
80
- if output is None:
81
- output = dict()
82
- if isinstance(obj, list):
83
- for i in range(len(obj)):
84
- self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
85
- elif isinstance(obj, dict):
86
- for key in obj:
87
- self.flatten_qmp_object(obj[key], output, basestr + key + '.')
88
- else:
89
- output[basestr[:-1]] = obj # Strip trailing '.'
90
- return output
91
-
92
- def qmp_to_opts(self, obj):
93
- obj = self.flatten_qmp_object(obj)
94
- output_list = list()
95
- for key in obj:
96
- output_list += [key + '=' + obj[key]]
97
- return ','.join(output_list)
98
-
99
def assert_qmp_absent(self, d, path):
100
try:
101
result = self.dictpath(d, path)
102
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
103
'''Asserts that the given filename is a json: filename and that its
104
content is equal to the given reference object'''
105
self.assertEqual(json_filename[:5], 'json:')
106
- self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])),
107
- self.flatten_qmp_object(reference))
108
+ self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
109
+ self.vm.flatten_qmp_object(reference))
110
111
def cancel_and_wait(self, drive='drive0', force=False, resume=False):
112
'''Cancel a block job and wait for it to finish, returning the event'''
113
--
40
--
114
2.13.6
41
2.13.6
115
42
116
43
diff view generated by jsdifflib
1
NFS paths were only partially filtered in _filter_img_create, _img_info
1
From: John Snow <jsnow@redhat.com>
2
and _filter_img_info, resulting in "nfs://127.0.0.1TEST_DIR/t.IMGFMT".
3
This adds another replacement to the sed calls that matches the test
4
directory not as a host path, but as an NFS URL (the prefix as used for
5
$TEST_IMG).
6
2
3
VPC has some difficulty creating geometries of particular size.
4
However, we can indeed force it to use a literal one, so let's
5
do that for the sake of test 197, which is testing some specific
6
offsets.
7
8
Signed-off-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Fam Zheng <famz@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
9
---
13
---
10
tests/qemu-iotests/126.out | 2 +-
14
tests/qemu-iotests/197 | 4 ++++
11
tests/qemu-iotests/common.filter | 6 ++++--
15
tests/qemu-iotests/common.filter | 3 ++-
12
tests/qemu-iotests/common.rc | 8 +++++++-
16
2 files changed, 6 insertions(+), 1 deletion(-)
13
3 files changed, 12 insertions(+), 4 deletions(-)
14
17
15
diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
16
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100755
17
--- a/tests/qemu-iotests/126.out
20
--- a/tests/qemu-iotests/197
18
+++ b/tests/qemu-iotests/126.out
21
+++ b/tests/qemu-iotests/197
19
@@ -XXX,XX +XXX,XX @@ QA output created by 126
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
20
=== Testing plain files ===
23
echo
21
24
22
Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
25
# Prep the images
23
-Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
24
+Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
27
+if [ "$IMGFMT" = "vpc" ]; then
25
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
26
=== Testing relative backing filename resolution ===
29
+fi
27
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
28
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
29
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
30
--- a/tests/qemu-iotests/common.filter
35
--- a/tests/qemu-iotests/common.filter
31
+++ b/tests/qemu-iotests/common.filter
36
+++ b/tests/qemu-iotests/common.filter
32
@@ -XXX,XX +XXX,XX @@ _filter_actual_image_size()
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
33
# replace driver-specific options in the "Formatting..." line
38
-e "s# log_size=[0-9]\\+##g" \
34
_filter_img_create()
39
-e "s# refcount_bits=[0-9]\\+##g" \
35
{
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
36
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
41
- -e "s# iter-time=[0-9]\\+##g"
37
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
42
+ -e "s# iter-time=[0-9]\\+##g" \
38
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
43
+ -e "s# force_size=\\(on\\|off\\)##g"
39
-e "s#$TEST_DIR#TEST_DIR#g" \
44
}
40
-e "s#$IMGFMT#IMGFMT#g" \
45
41
-e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \
46
_filter_img_info()
42
@@ -XXX,XX +XXX,XX @@ _filter_img_info()
43
44
discard=0
45
regex_json_spec_start='^ *"format-specific": \{'
46
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
47
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
48
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
49
-e "s#$TEST_DIR#TEST_DIR#g" \
50
-e "s#$IMGFMT#IMGFMT#g" \
51
-e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \
52
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
53
index XXXXXXX..XXXXXXX 100644
54
--- a/tests/qemu-iotests/common.rc
55
+++ b/tests/qemu-iotests/common.rc
56
@@ -XXX,XX +XXX,XX @@ else
57
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
58
elif [ "$IMGPROTO" = "nfs" ]; then
59
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
60
+ REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR"
61
TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
62
elif [ "$IMGPROTO" = "vxhs" ]; then
63
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
64
@@ -XXX,XX +XXX,XX @@ if [ ! -d "$TEST_DIR" ]; then
65
exit 1
66
fi
67
68
+if [ -z "$REMOTE_TEST_DIR" ]; then
69
+ REMOTE_TEST_DIR="$TEST_DIR"
70
+fi
71
+
72
if [ ! -d "$SAMPLE_IMG_DIR" ]; then
73
echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory"
74
exit 1
75
@@ -XXX,XX +XXX,XX @@ _img_info()
76
discard=0
77
regex_json_spec_start='^ *"format-specific": \{'
78
$QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
79
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
80
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
81
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
82
-e "s#$TEST_DIR#TEST_DIR#g" \
83
-e "s#$IMGFMT#IMGFMT#g" \
84
-e "/^disk size:/ D" \
85
--
47
--
86
2.13.6
48
2.13.6
87
49
88
50
diff view generated by jsdifflib
1
block_job_drain() contains a blk_drain() call which cannot be moved to
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
Job, so add a new JobDriver callback JobDriver.drain which has a common
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
implementation for all BlockJobs. In addition to this we keep the
3
performs its own recursion now.
4
existing BlockJobDriver.drain callback that is called by the common
5
drain implementation for all block jobs.
6
4
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
19
---
10
include/block/blockjob_int.h | 12 ++++++++++++
20
block/io.c | 14 +++++++++++---
11
include/qemu/job.h | 13 +++++++++++++
21
1 file changed, 11 insertions(+), 3 deletions(-)
12
block/backup.c | 1 +
13
block/commit.c | 1 +
14
block/mirror.c | 2 ++
15
block/stream.c | 1 +
16
blockjob.c | 20 ++++++++++----------
17
job.c | 11 +++++++++++
18
tests/test-bdrv-drain.c | 1 +
19
tests/test-blockjob-txn.c | 1 +
20
tests/test-blockjob.c | 2 ++
21
11 files changed, 55 insertions(+), 10 deletions(-)
22
22
23
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
23
diff --git a/block/io.c b/block/io.c
24
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob_int.h
25
--- a/block/io.c
26
+++ b/include/block/blockjob_int.h
26
+++ b/block/io.c
27
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
28
* If the callback is not NULL, it will be invoked when the job has to be
28
bdrv_wakeup(bs);
29
* synchronously cancelled or completed; it should drain BlockDriverStates
29
}
30
* as required to ensure progress.
30
31
+ *
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
+ * Block jobs must use the default implementation for job_driver.drain,
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
+ * which will in turn call this callback after doing generic block job
33
{
34
+ * stuff.
34
+ BdrvChild *child, *tmp;
35
*/
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
36
void (*drain)(BlockJob *job);
36
37
};
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
38
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job);
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
39
void block_job_user_resume(Job *job);
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
40
40
bdrv_coroutine_enter(bs, data.co);
41
/**
41
BDRV_POLL_WHILE(bs, !data.done);
42
+ * block_job_drain:
43
+ * Callback to be used for JobDriver.drain in all block jobs. Drains the main
44
+ * block node associated with the block jobs and calls BlockJobDriver.drain for
45
+ * job-specific actions.
46
+ */
47
+void block_job_drain(Job *job);
48
+
42
+
49
+/**
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
50
* block_job_yield:
44
+ bdrv_drain_invoke(child->bs, begin);
51
* @job: The job that calls the function.
45
+ }
52
*
53
diff --git a/include/qemu/job.h b/include/qemu/job.h
54
index XXXXXXX..XXXXXXX 100644
55
--- a/include/qemu/job.h
56
+++ b/include/qemu/job.h
57
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
58
*/
59
void (*user_resume)(Job *job);
60
61
+ /*
62
+ * If the callback is not NULL, it will be invoked when the job has to be
63
+ * synchronously cancelled or completed; it should drain any activities
64
+ * as required to ensure progress.
65
+ */
66
+ void (*drain)(Job *job);
67
+
68
/**
69
* If the callback is not NULL, it will be invoked when all the jobs
70
* belonging to the same transaction complete; or upon this job's
71
@@ -XXX,XX +XXX,XX @@ bool job_user_paused(Job *job);
72
*/
73
void job_user_resume(Job *job, Error **errp);
74
75
+/*
76
+ * Drain any activities as required to ensure progress. This can be called in a
77
+ * loop to synchronously complete a job.
78
+ */
79
+void job_drain(Job *job);
80
+
81
/**
82
* Get the next element from the list of block jobs after @job, or the
83
* first one if @job is %NULL.
84
diff --git a/block/backup.c b/block/backup.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/backup.c
87
+++ b/block/backup.c
88
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
89
.job_type = JOB_TYPE_BACKUP,
90
.free = block_job_free,
91
.user_resume = block_job_user_resume,
92
+ .drain = block_job_drain,
93
.start = backup_run,
94
.commit = backup_commit,
95
.abort = backup_abort,
96
diff --git a/block/commit.c b/block/commit.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/block/commit.c
99
+++ b/block/commit.c
100
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
101
.job_type = JOB_TYPE_COMMIT,
102
.free = block_job_free,
103
.user_resume = block_job_user_resume,
104
+ .drain = block_job_drain,
105
.start = commit_run,
106
},
107
};
108
diff --git a/block/mirror.c b/block/mirror.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/mirror.c
111
+++ b/block/mirror.c
112
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
113
.job_type = JOB_TYPE_MIRROR,
114
.free = block_job_free,
115
.user_resume = block_job_user_resume,
116
+ .drain = block_job_drain,
117
.start = mirror_run,
118
.pause = mirror_pause,
119
},
120
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
121
.job_type = JOB_TYPE_COMMIT,
122
.free = block_job_free,
123
.user_resume = block_job_user_resume,
124
+ .drain = block_job_drain,
125
.start = mirror_run,
126
.pause = mirror_pause,
127
},
128
diff --git a/block/stream.c b/block/stream.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/block/stream.c
131
+++ b/block/stream.c
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
133
.free = block_job_free,
134
.start = stream_run,
135
.user_resume = block_job_user_resume,
136
+ .drain = block_job_drain,
137
},
138
};
139
140
diff --git a/blockjob.c b/blockjob.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/blockjob.c
143
+++ b/blockjob.c
144
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
145
job_resume(&job->job);
146
}
46
}
147
47
148
-static void block_job_drain(BlockJob *job)
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
149
+void block_job_drain(Job *job)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
150
{
50
BdrvChild *child, *tmp;
151
- /* If job is !job->job.busy this kicks it into the next pause point. */
51
bool waited;
152
- block_job_enter(job);
52
153
+ BlockJob *bjob = container_of(job, BlockJob, job);
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
154
54
- bdrv_drain_invoke(bs, begin);
155
- blk_drain(job->blk);
55
-
156
- if (job->driver->drain) {
56
/* Wait for drained requests to finish */
157
- job->driver->drain(job);
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
158
+ blk_drain(bjob->blk);
58
159
+ if (bjob->driver->drain) {
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
160
+ bjob->driver->drain(bjob);
60
bdrv_parent_drained_begin(bs);
161
}
61
}
62
63
+ bdrv_drain_invoke(bs, true);
64
bdrv_drain_recurse(bs, true);
162
}
65
}
163
66
164
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
165
job_pause(&job->job);
166
167
while (!job->job.paused && !job_is_completed(&job->job)) {
168
- block_job_drain(job);
169
+ job_drain(&job->job);
170
}
68
}
171
69
172
job->job.aio_context = NULL;
70
bdrv_parent_drained_end(bs);
173
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
71
+ bdrv_drain_invoke(bs, false);
174
job_unref(&job->job);
72
bdrv_drain_recurse(bs, false);
175
return -EBUSY;
73
aio_enable_external(bdrv_get_aio_context(bs));
74
}
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
76
aio_context_acquire(aio_context);
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
78
if (aio_context == bdrv_get_aio_context(bs)) {
79
+ /* FIXME Calling this multiple times is wrong */
80
+ bdrv_drain_invoke(bs, true);
81
waited |= bdrv_drain_recurse(bs, true);
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
176
}
91
}
177
- /* block_job_drain calls block_job_enter, and it should be enough to
178
- * induce progress until the job completes or moves to the main thread.
179
+ /* job_drain calls job_enter, and it should be enough to induce progress
180
+ * until the job completes or moves to the main thread.
181
*/
182
while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
183
- block_job_drain(job);
184
+ job_drain(&job->job);
185
}
186
while (!job_is_completed(&job->job)) {
187
aio_poll(qemu_get_aio_context(), true);
188
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
189
assert(is_block_job(&job->job));
190
assert(job->job.driver->free == &block_job_free);
191
assert(job->job.driver->user_resume == &block_job_user_resume);
192
+ assert(job->job.driver->drain == &block_job_drain);
193
194
job->driver = driver;
195
job->blk = blk;
196
diff --git a/job.c b/job.c
197
index XXXXXXX..XXXXXXX 100644
198
--- a/job.c
199
+++ b/job.c
200
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
201
job_pause_point(job);
202
}
203
204
+void job_drain(Job *job)
205
+{
206
+ /* If job is !busy this kicks it into the next pause point. */
207
+ job_enter(job);
208
+
209
+ if (job->driver->drain) {
210
+ job->driver->drain(job);
211
+ }
212
+}
213
+
214
+
215
/**
216
* All jobs must allow a pause point before entering their job proper. This
217
* ensures that jobs can be paused prior to being started, then resumed later.
218
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
219
index XXXXXXX..XXXXXXX 100644
220
--- a/tests/test-bdrv-drain.c
221
+++ b/tests/test-bdrv-drain.c
222
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
223
.instance_size = sizeof(TestBlockJob),
224
.free = block_job_free,
225
.user_resume = block_job_user_resume,
226
+ .drain = block_job_drain,
227
.start = test_job_start,
228
},
229
.complete = test_job_complete,
230
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
231
index XXXXXXX..XXXXXXX 100644
232
--- a/tests/test-blockjob-txn.c
233
+++ b/tests/test-blockjob-txn.c
234
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
235
.instance_size = sizeof(TestBlockJob),
236
.free = block_job_free,
237
.user_resume = block_job_user_resume,
238
+ .drain = block_job_drain,
239
.start = test_block_job_run,
240
},
241
};
242
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
243
index XXXXXXX..XXXXXXX 100644
244
--- a/tests/test-blockjob.c
245
+++ b/tests/test-blockjob.c
246
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
247
.instance_size = sizeof(BlockJob),
248
.free = block_job_free,
249
.user_resume = block_job_user_resume,
250
+ .drain = block_job_drain,
251
},
252
};
253
254
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
255
.instance_size = sizeof(CancelJob),
256
.free = block_job_free,
257
.user_resume = block_job_user_resume,
258
+ .drain = block_job_drain,
259
.start = cancel_job_start,
260
},
261
.complete = cancel_job_complete,
262
--
92
--
263
2.13.6
93
2.13.6
264
94
265
95
diff view generated by jsdifflib
1
This adds a QMP event that is emitted whenever a job transitions from
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
one status to another.
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
3
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
14
---
7
qapi/job.json | 14 ++++
15
block/io.c | 3 +--
8
job.c | 10 +++
16
1 file changed, 1 insertion(+), 2 deletions(-)
9
tests/qemu-iotests/030 | 17 +++-
10
tests/qemu-iotests/040 | 2 +
11
tests/qemu-iotests/041 | 17 +++-
12
tests/qemu-iotests/094.out | 7 ++
13
tests/qemu-iotests/095 | 2 +-
14
tests/qemu-iotests/095.out | 6 ++
15
tests/qemu-iotests/109 | 2 +-
16
tests/qemu-iotests/109.out | 178 ++++++++++++++++++++++++++++++++++++------
17
tests/qemu-iotests/124 | 8 ++
18
tests/qemu-iotests/127.out | 7 ++
19
tests/qemu-iotests/141 | 13 ++-
20
tests/qemu-iotests/141.out | 29 +++++++
21
tests/qemu-iotests/144 | 2 +-
22
tests/qemu-iotests/144.out | 7 ++
23
tests/qemu-iotests/156 | 2 +-
24
tests/qemu-iotests/156.out | 7 ++
25
tests/qemu-iotests/185 | 12 +--
26
tests/qemu-iotests/185.out | 10 +++
27
tests/qemu-iotests/191 | 4 +-
28
tests/qemu-iotests/191.out | 132 +++++++++++++++++++++++++++++++
29
tests/qemu-iotests/iotests.py | 5 ++
30
23 files changed, 449 insertions(+), 44 deletions(-)
31
17
32
diff --git a/qapi/job.json b/qapi/job.json
18
diff --git a/block/io.c b/block/io.c
33
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
34
--- a/qapi/job.json
20
--- a/block/io.c
35
+++ b/qapi/job.json
21
+++ b/block/io.c
36
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
37
{ 'enum': 'JobVerb',
23
aio_context_acquire(aio_context);
38
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
24
bdrv_parent_drained_begin(bs);
39
'finalize' ] }
25
aio_disable_external(aio_context);
40
+
26
+ bdrv_drain_invoke(bs, true);
41
+##
27
aio_context_release(aio_context);
42
+# @JOB_STATUS_CHANGE:
28
43
+#
29
if (!g_slist_find(aio_ctxs, aio_context)) {
44
+# Emitted when a job transitions to a different status.
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
45
+#
31
aio_context_acquire(aio_context);
46
+# @id: The job identifier
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
47
+# @status: The new job status
33
if (aio_context == bdrv_get_aio_context(bs)) {
48
+#
34
- /* FIXME Calling this multiple times is wrong */
49
+# Since: 2.13
35
- bdrv_drain_invoke(bs, true);
50
+##
36
waited |= bdrv_drain_recurse(bs, true);
51
+{ 'event': 'JOB_STATUS_CHANGE',
37
}
52
+ 'data': { 'id': 'str',
38
}
53
+ 'status': 'JobStatus' } }
54
diff --git a/job.c b/job.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/job.c
57
+++ b/job.c
58
@@ -XXX,XX +XXX,XX @@
59
#include "qemu/id.h"
60
#include "qemu/main-loop.h"
61
#include "trace-root.h"
62
+#include "qapi/qapi-events-job.h"
63
64
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
65
66
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
67
return rc;
68
}
69
70
+static bool job_is_internal(Job *job)
71
+{
72
+ return (job->id == NULL);
73
+}
74
+
75
static void job_state_transition(Job *job, JobStatus s1)
76
{
77
JobStatus s0 = job->status;
78
@@ -XXX,XX +XXX,XX @@ static void job_state_transition(Job *job, JobStatus s1)
79
JobStatus_str(s0), JobStatus_str(s1));
80
assert(JobSTT[s0][s1]);
81
job->status = s1;
82
+
83
+ if (!job_is_internal(job) && s1 != s0) {
84
+ qapi_event_send_job_status_change(job->id, job->status, &error_abort);
85
+ }
86
}
87
88
int job_apply_verb(Job *job, JobVerb verb, Error **errp)
89
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
90
index XXXXXXX..XXXXXXX 100755
91
--- a/tests/qemu-iotests/030
92
+++ b/tests/qemu-iotests/030
93
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
94
result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6')
95
self.assert_qmp(result, 'error/class', 'GenericError')
96
97
- event = self.vm.get_qmp_event(wait=True)
98
- self.assertEqual(event['event'], 'BLOCK_JOB_READY')
99
+ event = self.vm.event_wait(name='BLOCK_JOB_READY')
100
self.assert_qmp(event, 'data/device', 'commit-drive0')
101
self.assert_qmp(event, 'data/type', 'commit')
102
self.assert_qmp_absent(event, 'data/error')
103
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
104
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
105
self.assert_qmp(event, 'data/len', self.image_len)
106
completed = True
107
+ elif event['event'] == 'JOB_STATUS_CHANGE':
108
+ self.assert_qmp(event, 'data/id', 'drive0')
109
110
self.assert_no_active_block_jobs()
111
self.vm.shutdown()
112
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
113
self.assert_qmp(event, 'data/offset', self.image_len)
114
self.assert_qmp(event, 'data/len', self.image_len)
115
completed = True
116
+ elif event['event'] == 'JOB_STATUS_CHANGE':
117
+ self.assert_qmp(event, 'data/id', 'drive0')
118
119
self.assert_no_active_block_jobs()
120
self.vm.shutdown()
121
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
122
self.assert_qmp(event, 'data/offset', self.image_len)
123
self.assert_qmp(event, 'data/len', self.image_len)
124
completed = True
125
+ elif event['event'] == 'JOB_STATUS_CHANGE':
126
+ self.assert_qmp(event, 'data/id', 'drive0')
127
128
self.assert_no_active_block_jobs()
129
self.vm.shutdown()
130
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
131
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
132
self.assert_qmp(event, 'data/len', self.image_len)
133
completed = True
134
+ elif event['event'] == 'JOB_STATUS_CHANGE':
135
+ self.assert_qmp(event, 'data/id', 'drive0')
136
137
self.assert_no_active_block_jobs()
138
self.vm.shutdown()
139
@@ -XXX,XX +XXX,XX @@ class TestENOSPC(TestErrors):
140
self.assert_qmp(event, 'data/offset', self.image_len)
141
self.assert_qmp(event, 'data/len', self.image_len)
142
completed = True
143
+ elif event['event'] == 'JOB_STATUS_CHANGE':
144
+ self.assert_qmp(event, 'data/id', 'drive0')
145
146
self.assert_no_active_block_jobs()
147
self.vm.shutdown()
148
@@ -XXX,XX +XXX,XX @@ class TestStreamStop(iotests.QMPTestCase):
149
150
time.sleep(0.1)
151
events = self.vm.get_qmp_events(wait=False)
152
- self.assertEqual(events, [], 'unexpected QMP event: %s' % events)
153
+ for e in events:
154
+ self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE')
155
+ self.assert_qmp(e, 'data/id', 'drive0')
156
157
self.cancel_and_wait(resume=True)
158
159
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
160
index XXXXXXX..XXXXXXX 100755
161
--- a/tests/qemu-iotests/040
162
+++ b/tests/qemu-iotests/040
163
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
164
elif event['event'] == 'BLOCK_JOB_CANCELLED':
165
self.assert_qmp(event, 'data/device', 'drive0')
166
cancelled = True
167
+ elif event['event'] == 'JOB_STATUS_CHANGE':
168
+ self.assert_qmp(event, 'data/id', 'drive0')
169
else:
170
self.fail("Unexpected event %s" % (event['event']))
171
172
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
173
index XXXXXXX..XXXXXXX 100755
174
--- a/tests/qemu-iotests/041
175
+++ b/tests/qemu-iotests/041
176
@@ -XXX,XX +XXX,XX @@ new_state = "1"
177
self.assert_qmp(event, 'data/device', 'drive0')
178
self.assert_qmp(event, 'data/error', 'Input/output error')
179
completed = True
180
+ elif event['event'] == 'JOB_STATUS_CHANGE':
181
+ self.assert_qmp(event, 'data/id', 'drive0')
182
183
self.assert_no_active_block_jobs()
184
self.vm.shutdown()
185
@@ -XXX,XX +XXX,XX @@ new_state = "1"
186
self.assert_qmp(result, 'return', {})
187
188
event = self.vm.get_qmp_event(wait=True)
189
+ while event['event'] == 'JOB_STATUS_CHANGE':
190
+ self.assert_qmp(event, 'data/id', 'drive0')
191
+ event = self.vm.get_qmp_event(wait=True)
192
+
193
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
194
self.assert_qmp(event, 'data/device', 'drive0')
195
self.assert_qmp(event, 'data/operation', 'read')
196
@@ -XXX,XX +XXX,XX @@ new_state = "1"
197
self.assert_qmp(result, 'return', {})
198
199
event = self.vm.get_qmp_event(wait=True)
200
+ while event['event'] == 'JOB_STATUS_CHANGE':
201
+ self.assert_qmp(event, 'data/id', 'drive0')
202
+ event = self.vm.get_qmp_event(wait=True)
203
+
204
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
205
self.assert_qmp(event, 'data/device', 'drive0')
206
self.assert_qmp(event, 'data/operation', 'read')
207
@@ -XXX,XX +XXX,XX @@ new_state = "1"
208
on_target_error='ignore')
209
self.assert_qmp(result, 'return', {})
210
211
- event = self.vm.get_qmp_event(wait=True)
212
+ event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
213
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
214
self.assert_qmp(event, 'data/device', 'drive0')
215
self.assert_qmp(event, 'data/operation', 'write')
216
@@ -XXX,XX +XXX,XX @@ class TestGranularity(iotests.QMPTestCase):
217
sync='full', target=target_img,
218
mode='absolute-paths', granularity=8192)
219
self.assert_qmp(result, 'return', {})
220
+
221
event = self.vm.get_qmp_event(wait=60.0)
222
+ while event['event'] == 'JOB_STATUS_CHANGE':
223
+ self.assert_qmp(event, 'data/id', 'drive0')
224
+ event = self.vm.get_qmp_event(wait=60.0)
225
+
226
# Failures will manifest as COMPLETED/ERROR.
227
self.assert_qmp(event, 'event', 'BLOCK_JOB_READY')
228
self.complete_and_wait(drive='drive0', wait_ready=False)
229
diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out
230
index XXXXXXX..XXXXXXX 100644
231
--- a/tests/qemu-iotests/094.out
232
+++ b/tests/qemu-iotests/094.out
233
@@ -XXX,XX +XXX,XX @@ QA output created by 094
234
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
235
Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864
236
{"return": {}}
237
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
238
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
239
{"return": {}}
240
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
241
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
242
{"return": {}}
243
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
244
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
245
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
246
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
247
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
248
{"return": {}}
249
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
250
*** done
251
diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095
252
index XXXXXXX..XXXXXXX 100755
253
--- a/tests/qemu-iotests/095
254
+++ b/tests/qemu-iotests/095
255
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return"
256
257
_send_qemu_cmd $h "{ 'execute': 'block-commit',
258
'arguments': { 'device': 'test',
259
- 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED"
260
+ 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"'
261
262
_cleanup_qemu
263
264
diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out
265
index XXXXXXX..XXXXXXX 100644
266
--- a/tests/qemu-iotests/095.out
267
+++ b/tests/qemu-iotests/095.out
268
@@ -XXX,XX +XXX,XX @@ virtual size: 5.0M (5242880 bytes)
269
=== Running QEMU Live Commit Test ===
270
271
{"return": {}}
272
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}}
273
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}}
274
{"return": {}}
275
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}}
276
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}}
277
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}}
278
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}}
279
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}}
280
281
=== Base image info after commit and resize ===
282
image: TEST_DIR/t.IMGFMT.base
283
diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109
284
index XXXXXXX..XXXXXXX 100755
285
--- a/tests/qemu-iotests/109
286
+++ b/tests/qemu-iotests/109
287
@@ -XXX,XX +XXX,XX @@ function run_qemu()
288
289
_send_qemu_cmd $QEMU_HANDLE '' "$qmp_event"
290
if test "$qmp_event" = BLOCK_JOB_ERROR; then
291
- _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED"
292
+ _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"'
293
fi
294
_send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return"
295
_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return"
296
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
297
index XXXXXXX..XXXXXXX 100644
298
--- a/tests/qemu-iotests/109.out
299
+++ b/tests/qemu-iotests/109.out
300
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
301
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
302
{"return": {}}
303
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
304
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
305
-Specify the 'raw' format explicitly to remove the restrictions.
306
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
307
+ Specify the 'raw' format explicitly to remove the restrictions.
308
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
309
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
310
{"return": {}}
311
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
312
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
313
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
314
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
315
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
316
{"return": []}
317
{"return": {}}
318
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
319
read 65536/65536 bytes at offset 0
320
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
321
{"return": {}}
322
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
323
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
324
{"return": {}}
325
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
326
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
327
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
328
{"return": {}}
329
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
330
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
331
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
332
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
333
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
334
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
335
Warning: Image size mismatch!
336
Images are identical.
337
338
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
339
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
340
{"return": {}}
341
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
342
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
343
-Specify the 'raw' format explicitly to remove the restrictions.
344
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
345
+ Specify the 'raw' format explicitly to remove the restrictions.
346
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
347
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
348
{"return": {}}
349
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
350
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
351
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
352
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
353
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
354
{"return": []}
355
{"return": {}}
356
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
357
read 65536/65536 bytes at offset 0
358
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
359
{"return": {}}
360
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
361
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
362
{"return": {}}
363
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
364
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
365
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
366
{"return": {}}
367
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
368
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
369
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
370
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
371
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
372
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
373
Warning: Image size mismatch!
374
Images are identical.
375
376
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
377
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
378
{"return": {}}
379
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
380
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
381
-Specify the 'raw' format explicitly to remove the restrictions.
382
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
383
+ Specify the 'raw' format explicitly to remove the restrictions.
384
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
385
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
386
{"return": {}}
387
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
388
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
389
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
390
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
391
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
392
{"return": []}
393
{"return": {}}
394
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
395
read 65536/65536 bytes at offset 0
396
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
397
{"return": {}}
398
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
399
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
400
{"return": {}}
401
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
402
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
403
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
404
{"return": {}}
405
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
406
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
407
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
408
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
409
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
410
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
411
Warning: Image size mismatch!
412
Images are identical.
413
414
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
415
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
416
{"return": {}}
417
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
418
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
419
-Specify the 'raw' format explicitly to remove the restrictions.
420
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
421
+ Specify the 'raw' format explicitly to remove the restrictions.
422
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
423
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
424
{"return": {}}
425
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
426
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
427
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
428
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
429
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
430
{"return": []}
431
{"return": {}}
432
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
433
read 65536/65536 bytes at offset 0
434
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
435
{"return": {}}
436
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
437
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
438
{"return": {}}
439
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
440
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
441
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
442
{"return": {}}
443
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
444
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
445
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
446
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
447
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
448
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
449
Warning: Image size mismatch!
450
Images are identical.
451
452
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
453
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
454
{"return": {}}
455
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
456
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
457
-Specify the 'raw' format explicitly to remove the restrictions.
458
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
459
+ Specify the 'raw' format explicitly to remove the restrictions.
460
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
461
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
462
{"return": {}}
463
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
464
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
465
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
466
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
467
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
468
{"return": []}
469
{"return": {}}
470
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
471
read 65536/65536 bytes at offset 0
472
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
473
{"return": {}}
474
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
475
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
476
{"return": {}}
477
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
478
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
479
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
480
{"return": {}}
481
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
482
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
483
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
484
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
485
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
486
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
487
Warning: Image size mismatch!
488
Images are identical.
489
490
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
491
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
492
{"return": {}}
493
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
494
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
495
-Specify the 'raw' format explicitly to remove the restrictions.
496
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
497
+ Specify the 'raw' format explicitly to remove the restrictions.
498
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
499
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
500
{"return": {}}
501
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
502
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
503
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
504
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
505
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
506
{"return": []}
507
{"return": {}}
508
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
509
read 65536/65536 bytes at offset 0
510
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
511
{"return": {}}
512
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
513
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
514
{"return": {}}
515
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
516
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
517
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
518
{"return": {}}
519
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
520
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
521
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
522
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
523
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
524
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
525
Warning: Image size mismatch!
526
Images are identical.
527
528
@@ -XXX,XX +XXX,XX @@ Images are identical.
529
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
530
{"return": {}}
531
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
532
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
533
-Specify the 'raw' format explicitly to remove the restrictions.
534
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
535
+ Specify the 'raw' format explicitly to remove the restrictions.
536
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
537
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
538
{"return": {}}
539
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
540
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
541
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
542
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
543
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
544
{"return": []}
545
{"return": {}}
546
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
547
read 65536/65536 bytes at offset 0
548
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
549
{"return": {}}
550
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
551
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
552
{"return": {}}
553
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
554
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
555
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
556
{"return": {}}
557
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
558
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
559
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
560
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
561
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
562
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
563
Warning: Image size mismatch!
564
Images are identical.
565
566
@@ -XXX,XX +XXX,XX @@ Images are identical.
567
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
568
{"return": {}}
569
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
570
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
571
-Specify the 'raw' format explicitly to remove the restrictions.
572
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
573
+ Specify the 'raw' format explicitly to remove the restrictions.
574
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
575
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
576
{"return": {}}
577
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
578
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
579
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
580
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
581
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
582
{"return": []}
583
{"return": {}}
584
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
585
read 65536/65536 bytes at offset 0
586
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
587
{"return": {}}
588
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
589
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
590
{"return": {}}
591
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
592
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
593
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
594
{"return": {}}
595
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
596
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
597
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
598
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
599
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
600
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
601
Warning: Image size mismatch!
602
Images are identical.
603
604
@@ -XXX,XX +XXX,XX @@ Images are identical.
605
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
606
{"return": {}}
607
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
608
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
609
-Specify the 'raw' format explicitly to remove the restrictions.
610
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
611
+ Specify the 'raw' format explicitly to remove the restrictions.
612
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
613
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
614
{"return": {}}
615
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
616
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
617
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
618
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
619
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
620
{"return": []}
621
{"return": {}}
622
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
623
read 65536/65536 bytes at offset 0
624
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
625
{"return": {}}
626
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
627
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
628
{"return": {}}
629
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
630
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
631
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
632
{"return": {}}
633
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
634
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
635
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
636
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
637
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
638
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
639
Warning: Image size mismatch!
640
Images are identical.
641
642
@@ -XXX,XX +XXX,XX @@ Images are identical.
643
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
644
{"return": {}}
645
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
646
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
647
-Specify the 'raw' format explicitly to remove the restrictions.
648
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
649
+ Specify the 'raw' format explicitly to remove the restrictions.
650
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
651
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
652
{"return": {}}
653
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
654
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
655
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
656
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
657
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
658
{"return": []}
659
{"return": {}}
660
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
661
read 65536/65536 bytes at offset 0
662
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
663
{"return": {}}
664
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
665
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
666
{"return": {}}
667
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
668
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
669
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
670
{"return": {}}
671
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
672
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
673
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
674
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
675
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
676
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
677
Warning: Image size mismatch!
678
Images are identical.
679
680
@@ -XXX,XX +XXX,XX @@ Images are identical.
681
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
682
{"return": {}}
683
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
684
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
685
-Specify the 'raw' format explicitly to remove the restrictions.
686
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
687
+ Specify the 'raw' format explicitly to remove the restrictions.
688
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
689
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
690
{"return": {}}
691
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
692
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
693
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
694
{"return": {}}
695
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
696
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
697
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
698
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
699
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
700
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
701
Warning: Image size mismatch!
702
Images are identical.
703
{"return": {}}
704
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
705
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
706
{"return": {}}
707
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
708
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
709
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
710
{"return": {}}
711
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
712
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
713
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
714
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
715
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
716
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
717
Warning: Image size mismatch!
718
Images are identical.
719
*** done
720
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
721
index XXXXXXX..XXXXXXX 100755
722
--- a/tests/qemu-iotests/124
723
+++ b/tests/qemu-iotests/124
724
@@ -XXX,XX +XXX,XX @@ class TestIncrementalBackupBase(iotests.QMPTestCase):
725
return self.wait_qmp_backup(kwargs['device'], error)
726
727
728
+ def ignore_job_status_change_events(self):
729
+ while True:
730
+ e = self.vm.event_wait(name="JOB_STATUS_CHANGE")
731
+ if e['data']['status'] == 'null':
732
+ break
733
+
734
def wait_qmp_backup(self, device, error='Input/output error'):
735
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
736
match={'data': {'device': device}})
737
self.assertNotEqual(event, None)
738
+ self.ignore_job_status_change_events()
739
740
try:
741
failure = self.dictpath(event, 'data/error')
742
@@ -XXX,XX +XXX,XX @@ class TestIncrementalBackupBase(iotests.QMPTestCase):
743
event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
744
match={'data': {'device': device}})
745
self.assertNotEqual(event, None)
746
+ self.ignore_job_status_change_events()
747
748
749
def create_anchor_backup(self, drive=None):
750
diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out
751
index XXXXXXX..XXXXXXX 100644
752
--- a/tests/qemu-iotests/127.out
753
+++ b/tests/qemu-iotests/127.out
754
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST
755
wrote 42/42 bytes at offset 0
756
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
757
{"return": {}}
758
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}}
759
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}}
760
{"return": {}}
761
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}}
762
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
763
{"return": {}}
764
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}}
765
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}}
766
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
767
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}}
768
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}}
769
{"return": {}}
770
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
771
*** done
772
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
773
index XXXXXXX..XXXXXXX 100755
774
--- a/tests/qemu-iotests/141
775
+++ b/tests/qemu-iotests/141
776
@@ -XXX,XX +XXX,XX @@ test_blockjob \
777
'format': '$IMGFMT',
778
'sync': 'none'}}" \
779
'return' \
780
- 'BLOCK_JOB_CANCELLED'
781
+ '"status": "null"'
782
783
echo
784
echo '=== Testing drive-mirror ==='
785
@@ -XXX,XX +XXX,XX @@ test_blockjob \
786
'format': '$IMGFMT',
787
'sync': 'none'}}" \
788
'BLOCK_JOB_READY' \
789
- 'BLOCK_JOB_COMPLETED'
790
+ '"status": "null"'
791
792
echo
793
echo '=== Testing active block-commit ==='
794
@@ -XXX,XX +XXX,XX @@ test_blockjob \
795
"{'execute': 'block-commit',
796
'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \
797
'BLOCK_JOB_READY' \
798
- 'BLOCK_JOB_COMPLETED'
799
+ '"status": "null"'
800
801
echo
802
echo '=== Testing non-active block-commit ==='
803
@@ -XXX,XX +XXX,XX @@ test_blockjob \
804
'top': '$TEST_DIR/m.$IMGFMT',
805
'speed': 1}}" \
806
'return' \
807
- 'BLOCK_JOB_CANCELLED'
808
+ '"status": "null"'
809
810
echo
811
echo '=== Testing block-stream ==='
812
@@ -XXX,XX +XXX,XX @@ echo
813
$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io
814
815
# With some data to stream (and @speed set to 1), block-stream will not complete
816
-# until we send the block-job-cancel command. Therefore, no event other than
817
-# BLOCK_JOB_CANCELLED will be emitted.
818
+# until we send the block-job-cancel command.
819
820
test_blockjob \
821
"{'execute': 'block-stream',
822
@@ -XXX,XX +XXX,XX @@ test_blockjob \
823
'device': 'drv0',
824
'speed': 1}}" \
825
'return' \
826
- 'BLOCK_JOB_CANCELLED'
827
+ '"status": "null"'
828
829
_cleanup_qemu
830
831
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
832
index XXXXXXX..XXXXXXX 100644
833
--- a/tests/qemu-iotests/141.out
834
+++ b/tests/qemu-iotests/141.out
835
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.
836
837
{"return": {}}
838
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
839
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
840
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
841
{"return": {}}
842
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
843
{"return": {}}
844
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
845
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}}
846
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
847
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
848
{"return": {}}
849
850
=== Testing drive-mirror ===
851
852
{"return": {}}
853
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
854
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
855
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
856
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
857
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
858
{"return": {}}
859
{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
860
{"return": {}}
861
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
862
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
863
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
864
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
865
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
866
{"return": {}}
867
868
=== Testing active block-commit ===
869
870
{"return": {}}
871
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
872
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
873
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
874
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
875
{"return": {}}
876
{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
877
{"return": {}}
878
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
879
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
880
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
881
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
882
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
883
{"return": {}}
884
885
=== Testing non-active block-commit ===
886
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
887
wrote 1048576/1048576 bytes at offset 0
888
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
889
{"return": {}}
890
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
891
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
892
{"return": {}}
893
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
894
{"return": {}}
895
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
896
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}}
897
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
898
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
899
{"return": {}}
900
901
=== Testing block-stream ===
902
@@ -XXX,XX +XXX,XX @@ wrote 1048576/1048576 bytes at offset 0
903
wrote 1048576/1048576 bytes at offset 0
904
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
905
{"return": {}}
906
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
907
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
908
{"return": {}}
909
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
910
{"return": {}}
911
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
912
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}}
913
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
914
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
915
{"return": {}}
916
*** done
917
diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144
918
index XXXXXXX..XXXXXXX 100755
919
--- a/tests/qemu-iotests/144
920
+++ b/tests/qemu-iotests/144
921
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete',
922
'arguments': {
923
'device': 'virtio0'
924
}
925
- }" "COMPLETED"
926
+ }" '"status": "null"'
927
928
echo
929
echo === Performing Live Snapshot 2 ===
930
diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out
931
index XXXXXXX..XXXXXXX 100644
932
--- a/tests/qemu-iotests/144.out
933
+++ b/tests/qemu-iotests/144.out
934
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/
935
936
=== Performing block-commit on active layer ===
937
938
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}}
939
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}}
940
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}}
941
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
942
{"return": {}}
943
{"return": {}}
944
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}}
945
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}}
946
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
947
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}}
948
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}}
949
950
=== Performing Live Snapshot 2 ===
951
952
diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156
953
index XXXXXXX..XXXXXXX 100755
954
--- a/tests/qemu-iotests/156
955
+++ b/tests/qemu-iotests/156
956
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $QEMU_HANDLE \
957
958
_send_qemu_cmd $QEMU_HANDLE \
959
'' \
960
- 'BLOCK_JOB_COMPLETED'
961
+ '"status": "null"'
962
963
# Remove the source images
964
rm -f "$TEST_IMG{,.backing,.overlay}"
965
diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out
966
index XXXXXXX..XXXXXXX 100644
967
--- a/tests/qemu-iotests/156.out
968
+++ b/tests/qemu-iotests/156.out
969
@@ -XXX,XX +XXX,XX @@ wrote 131072/131072 bytes at offset 131072
970
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
971
{"return": ""}
972
Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target
973
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}}
974
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}}
975
{"return": {}}
976
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}}
977
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}}
978
wrote 65536/65536 bytes at offset 196608
979
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
980
{"return": ""}
981
{"return": {}}
982
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}}
983
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}}
984
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}}
985
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}}
986
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}}
987
988
read 65536/65536 bytes at offset 0
989
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
990
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
991
index XXXXXXX..XXXXXXX 100755
992
--- a/tests/qemu-iotests/185
993
+++ b/tests/qemu-iotests/185
994
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
995
# If we don't sleep here 'quit' command races with disk I/O
996
sleep 0.5
997
998
+# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on
999
+# the timing, jobs may or may not transition through a paused state.
1000
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1001
-wait=1 _cleanup_qemu
1002
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1003
1004
echo
1005
echo === Start active commit job and exit qemu ===
1006
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1007
sleep 0.5
1008
1009
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1010
-wait=1 _cleanup_qemu
1011
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1012
1013
echo
1014
echo === Start mirror job and exit qemu ===
1015
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1016
sleep 0.5
1017
1018
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1019
-wait=1 _cleanup_qemu
1020
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1021
1022
echo
1023
echo === Start backup job and exit qemu ===
1024
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1025
sleep 0.5
1026
1027
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1028
-wait=1 _cleanup_qemu
1029
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1030
1031
echo
1032
echo === Start streaming job and exit qemu ===
1033
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1034
sleep 0.5
1035
1036
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1037
-wait=1 _cleanup_qemu
1038
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1039
1040
_check_test_img
1041
1042
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
1043
index XXXXXXX..XXXXXXX 100644
1044
--- a/tests/qemu-iotests/185.out
1045
+++ b/tests/qemu-iotests/185.out
1046
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1047
1048
=== Start commit job and exit qemu ===
1049
1050
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1051
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1052
{"return": {}}
1053
{"return": {}}
1054
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1055
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1056
=== Start active commit job and exit qemu ===
1057
1058
{"return": {}}
1059
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1060
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1061
{"return": {}}
1062
{"return": {}}
1063
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1064
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1065
1066
{"return": {}}
1067
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
1068
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1069
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1070
{"return": {}}
1071
{"return": {}}
1072
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1073
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
1074
1075
{"return": {}}
1076
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
1077
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1078
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1079
{"return": {}}
1080
{"return": {}}
1081
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1082
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
1083
=== Start streaming job and exit qemu ===
1084
1085
{"return": {}}
1086
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1087
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1088
{"return": {}}
1089
{"return": {}}
1090
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1091
diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191
1092
index XXXXXXX..XXXXXXX 100755
1093
--- a/tests/qemu-iotests/191
1094
+++ b/tests/qemu-iotests/191
1095
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1096
'device': 'top',
1097
'base':'$TEST_IMG.base',
1098
'top': '$TEST_IMG.mid' } }" \
1099
- "BLOCK_JOB_COMPLETED"
1100
+ '"status": "null"'
1101
_send_qemu_cmd $h "" "^}"
1102
1103
echo
1104
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1105
'device': 'top',
1106
'base':'$TEST_IMG.base',
1107
'top': '$TEST_IMG.mid' } }" \
1108
- "BLOCK_JOB_COMPLETED"
1109
+ '"status": "null"'
1110
_send_qemu_cmd $h "" "^}"
1111
1112
echo
1113
diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out
1114
index XXXXXXX..XXXXXXX 100644
1115
--- a/tests/qemu-iotests/191.out
1116
+++ b/tests/qemu-iotests/191.out
1117
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1118
=== Perform commit job ===
1119
1120
{
1121
+ "timestamp": {
1122
+ "seconds": TIMESTAMP,
1123
+ "microseconds": TIMESTAMP
1124
+ },
1125
+ "event": "JOB_STATUS_CHANGE",
1126
+ "data": {
1127
+ "status": "created",
1128
+ "id": "commit0"
1129
+ }
1130
+}
1131
+{
1132
+ "timestamp": {
1133
+ "seconds": TIMESTAMP,
1134
+ "microseconds": TIMESTAMP
1135
+ },
1136
+ "event": "JOB_STATUS_CHANGE",
1137
+ "data": {
1138
+ "status": "running",
1139
+ "id": "commit0"
1140
+ }
1141
+}
1142
+{
1143
"return": {
1144
}
1145
}
1146
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1147
"seconds": TIMESTAMP,
1148
"microseconds": TIMESTAMP
1149
},
1150
+ "event": "JOB_STATUS_CHANGE",
1151
+ "data": {
1152
+ "status": "waiting",
1153
+ "id": "commit0"
1154
+ }
1155
+}
1156
+{
1157
+ "timestamp": {
1158
+ "seconds": TIMESTAMP,
1159
+ "microseconds": TIMESTAMP
1160
+ },
1161
+ "event": "JOB_STATUS_CHANGE",
1162
+ "data": {
1163
+ "status": "pending",
1164
+ "id": "commit0"
1165
+ }
1166
+}
1167
+{
1168
+ "timestamp": {
1169
+ "seconds": TIMESTAMP,
1170
+ "microseconds": TIMESTAMP
1171
+ },
1172
"event": "BLOCK_JOB_COMPLETED",
1173
"data": {
1174
"device": "commit0",
1175
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1176
"type": "commit"
1177
}
1178
}
1179
+{
1180
+ "timestamp": {
1181
+ "seconds": TIMESTAMP,
1182
+ "microseconds": TIMESTAMP
1183
+ },
1184
+ "event": "JOB_STATUS_CHANGE",
1185
+ "data": {
1186
+ "status": "concluded",
1187
+ "id": "commit0"
1188
+ }
1189
+}
1190
+{
1191
+ "timestamp": {
1192
+ "seconds": TIMESTAMP,
1193
+ "microseconds": TIMESTAMP
1194
+ },
1195
+ "event": "JOB_STATUS_CHANGE",
1196
+ "data": {
1197
+ "status": "null",
1198
+ "id": "commit0"
1199
+ }
1200
+}
1201
1202
=== Check that both top and top2 point to base now ===
1203
1204
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1205
=== Perform commit job ===
1206
1207
{
1208
+ "timestamp": {
1209
+ "seconds": TIMESTAMP,
1210
+ "microseconds": TIMESTAMP
1211
+ },
1212
+ "event": "JOB_STATUS_CHANGE",
1213
+ "data": {
1214
+ "status": "created",
1215
+ "id": "commit0"
1216
+ }
1217
+}
1218
+{
1219
+ "timestamp": {
1220
+ "seconds": TIMESTAMP,
1221
+ "microseconds": TIMESTAMP
1222
+ },
1223
+ "event": "JOB_STATUS_CHANGE",
1224
+ "data": {
1225
+ "status": "running",
1226
+ "id": "commit0"
1227
+ }
1228
+}
1229
+{
1230
"return": {
1231
}
1232
}
1233
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1234
"seconds": TIMESTAMP,
1235
"microseconds": TIMESTAMP
1236
},
1237
+ "event": "JOB_STATUS_CHANGE",
1238
+ "data": {
1239
+ "status": "waiting",
1240
+ "id": "commit0"
1241
+ }
1242
+}
1243
+{
1244
+ "timestamp": {
1245
+ "seconds": TIMESTAMP,
1246
+ "microseconds": TIMESTAMP
1247
+ },
1248
+ "event": "JOB_STATUS_CHANGE",
1249
+ "data": {
1250
+ "status": "pending",
1251
+ "id": "commit0"
1252
+ }
1253
+}
1254
+{
1255
+ "timestamp": {
1256
+ "seconds": TIMESTAMP,
1257
+ "microseconds": TIMESTAMP
1258
+ },
1259
"event": "BLOCK_JOB_COMPLETED",
1260
"data": {
1261
"device": "commit0",
1262
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1263
"type": "commit"
1264
}
1265
}
1266
+{
1267
+ "timestamp": {
1268
+ "seconds": TIMESTAMP,
1269
+ "microseconds": TIMESTAMP
1270
+ },
1271
+ "event": "JOB_STATUS_CHANGE",
1272
+ "data": {
1273
+ "status": "concluded",
1274
+ "id": "commit0"
1275
+ }
1276
+}
1277
+{
1278
+ "timestamp": {
1279
+ "seconds": TIMESTAMP,
1280
+ "microseconds": TIMESTAMP
1281
+ },
1282
+ "event": "JOB_STATUS_CHANGE",
1283
+ "data": {
1284
+ "status": "null",
1285
+ "id": "commit0"
1286
+ }
1287
+}
1288
1289
=== Check that both top and top2 point to base now ===
1290
1291
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
1292
index XXXXXXX..XXXXXXX 100644
1293
--- a/tests/qemu-iotests/iotests.py
1294
+++ b/tests/qemu-iotests/iotests.py
1295
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1296
self.assert_qmp(event, 'data/device', drive)
1297
result = event
1298
cancelled = True
1299
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1300
+ self.assert_qmp(event, 'data/id', drive)
1301
+
1302
1303
self.assert_no_active_block_jobs()
1304
return result
1305
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1306
self.assert_qmp(event, 'data/offset', event['data']['len'])
1307
self.assert_no_active_block_jobs()
1308
return event
1309
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1310
+ self.assert_qmp(event, 'data/id', drive)
1311
1312
def wait_ready(self, drive='drive0'):
1313
'''Wait until a block job BLOCK_JOB_READY event'''
1314
--
39
--
1315
2.13.6
40
2.13.6
1316
41
1317
42
diff view generated by jsdifflib
1
This adds QMP commands that control the transition between states of the
1
This adds a test case that the BlockDriver callbacks for drain are
2
job lifecycle.
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
exactly once.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
5
---
8
---
6
qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++++++++
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
7
job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
tests/Makefile.include | 2 +
8
MAINTAINERS | 1 +
11
2 files changed, 139 insertions(+)
9
Makefile.objs | 1 +
12
create mode 100644 tests/test-bdrv-drain.c
10
trace-events | 9 ++++
11
5 files changed, 244 insertions(+)
12
create mode 100644 job-qmp.c
13
13
14
diff --git a/qapi/job.json b/qapi/job.json
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/job.json
17
+++ b/qapi/job.json
18
@@ -XXX,XX +XXX,XX @@
19
{ 'event': 'JOB_STATUS_CHANGE',
20
'data': { 'id': 'str',
21
'status': 'JobStatus' } }
22
+
23
+##
24
+# @job-pause:
25
+#
26
+# Pause an active job.
27
+#
28
+# This command returns immediately after marking the active job for pausing.
29
+# Pausing an already paused job is an error.
30
+#
31
+# The job will pause as soon as possible, which means transitioning into the
32
+# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The
33
+# corresponding JOB_STATUS_CHANGE event will be emitted.
34
+#
35
+# Cancelling a paused job automatically resumes it.
36
+#
37
+# @id: The job identifier.
38
+#
39
+# Since: 2.13
40
+##
41
+{ 'command': 'job-pause', 'data': { 'id': 'str' } }
42
+
43
+##
44
+# @job-resume:
45
+#
46
+# Resume a paused job.
47
+#
48
+# This command returns immediately after resuming a paused job. Resuming an
49
+# already running job is an error.
50
+#
51
+# @id : The job identifier.
52
+#
53
+# Since: 2.13
54
+##
55
+{ 'command': 'job-resume', 'data': { 'id': 'str' } }
56
+
57
+##
58
+# @job-cancel:
59
+#
60
+# Instruct an active background job to cancel at the next opportunity.
61
+# This command returns immediately after marking the active job for
62
+# cancellation.
63
+#
64
+# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE
65
+# event. Usually, the status will change to ABORTING, but it is possible that
66
+# a job successfully completes (e.g. because it was almost done and there was
67
+# no opportunity to cancel earlier than completing the job) and transitions to
68
+# PENDING instead.
69
+#
70
+# @id: The job identifier.
71
+#
72
+# Since: 2.13
73
+##
74
+{ 'command': 'job-cancel', 'data': { 'id': 'str' } }
75
+
76
+
77
+##
78
+# @job-complete:
79
+#
80
+# Manually trigger completion of an active job in the READY state.
81
+#
82
+# @id: The job identifier.
83
+#
84
+# Since: 2.13
85
+##
86
+{ 'command': 'job-complete', 'data': { 'id': 'str' } }
87
+
88
+##
89
+# @job-dismiss:
90
+#
91
+# Deletes a job that is in the CONCLUDED state. This command only needs to be
92
+# run explicitly for jobs that don't have automatic dismiss enabled.
93
+#
94
+# This command will refuse to operate on any job that has not yet reached its
95
+# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY
96
+# event, job-cancel or job-complete will still need to be used as appropriate.
97
+#
98
+# @id: The job identifier.
99
+#
100
+# Since: 2.13
101
+##
102
+{ 'command': 'job-dismiss', 'data': { 'id': 'str' } }
103
+
104
+##
105
+# @job-finalize:
106
+#
107
+# Instructs all jobs in a transaction (or a single job if it is not part of any
108
+# transaction) to finalize any graph changes and do any necessary cleanup. This
109
+# command requires that all involved jobs are in the PENDING state.
110
+#
111
+# For jobs in a transaction, instructing one job to finalize will force
112
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
113
+# a single member job to finalize.
114
+#
115
+# @id: The identifier of any job in the transaction, or of a job that is not
116
+# part of any transaction.
117
+#
118
+# Since: 2.13
119
+##
120
+{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
121
diff --git a/job-qmp.c b/job-qmp.c
122
new file mode 100644
15
new file mode 100644
123
index XXXXXXX..XXXXXXX
16
index XXXXXXX..XXXXXXX
124
--- /dev/null
17
--- /dev/null
125
+++ b/job-qmp.c
18
+++ b/tests/test-bdrv-drain.c
126
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
127
+/*
20
+/*
128
+ * QMP interface for background jobs
21
+ * Block node draining tests
129
+ *
22
+ *
130
+ * Copyright (c) 2011 IBM Corp.
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
131
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
132
+ *
24
+ *
133
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
134
+ * of this software and associated documentation files (the "Software"), to deal
26
+ * of this software and associated documentation files (the "Software"), to deal
135
+ * in the Software without restriction, including without limitation the rights
27
+ * in the Software without restriction, including without limitation the rights
136
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
...
...
148
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
149
+ * THE SOFTWARE.
41
+ * THE SOFTWARE.
150
+ */
42
+ */
151
+
43
+
152
+#include "qemu/osdep.h"
44
+#include "qemu/osdep.h"
153
+#include "qemu-common.h"
45
+#include "block/block.h"
154
+#include "qemu/job.h"
46
+#include "sysemu/block-backend.h"
155
+#include "qapi/qapi-commands-job.h"
156
+#include "qapi/error.h"
47
+#include "qapi/error.h"
157
+#include "trace-root.h"
158
+
48
+
159
+/* Get a job using its ID and acquire its AioContext */
49
+typedef struct BDRVTestState {
160
+static Job *find_job(const char *id, AioContext **aio_context, Error **errp)
50
+ int drain_count;
51
+} BDRVTestState;
52
+
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
161
+{
54
+{
162
+ Job *job;
55
+ BDRVTestState *s = bs->opaque;
163
+
56
+ s->drain_count++;
164
+ *aio_context = NULL;
165
+
166
+ job = job_get(id);
167
+ if (!job) {
168
+ error_setg(errp, "Job not found");
169
+ return NULL;
170
+ }
171
+
172
+ *aio_context = job->aio_context;
173
+ aio_context_acquire(*aio_context);
174
+
175
+ return job;
176
+}
57
+}
177
+
58
+
178
+void qmp_job_cancel(const char *id, Error **errp)
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
179
+{
60
+{
180
+ AioContext *aio_context;
61
+ BDRVTestState *s = bs->opaque;
181
+ Job *job = find_job(id, &aio_context, errp);
62
+ s->drain_count--;
182
+
183
+ if (!job) {
184
+ return;
185
+ }
186
+
187
+ trace_qmp_job_cancel(job);
188
+ job_user_cancel(job, true, errp);
189
+ aio_context_release(aio_context);
190
+}
63
+}
191
+
64
+
192
+void qmp_job_pause(const char *id, Error **errp)
65
+static void bdrv_test_close(BlockDriverState *bs)
193
+{
66
+{
194
+ AioContext *aio_context;
67
+ BDRVTestState *s = bs->opaque;
195
+ Job *job = find_job(id, &aio_context, errp);
68
+ g_assert_cmpint(s->drain_count, >, 0);
196
+
197
+ if (!job) {
198
+ return;
199
+ }
200
+
201
+ trace_qmp_job_pause(job);
202
+ job_user_pause(job, errp);
203
+ aio_context_release(aio_context);
204
+}
69
+}
205
+
70
+
206
+void qmp_job_resume(const char *id, Error **errp)
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
72
+ uint64_t offset, uint64_t bytes,
73
+ QEMUIOVector *qiov, int flags)
207
+{
74
+{
208
+ AioContext *aio_context;
75
+ /* We want this request to stay until the polling loop in drain waits for
209
+ Job *job = find_job(id, &aio_context, errp);
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
77
+ * first and polls its result, too, but it shouldn't accidentally complete
78
+ * this request yet. */
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
210
+
80
+
211
+ if (!job) {
81
+ return 0;
212
+ return;
213
+ }
214
+
215
+ trace_qmp_job_resume(job);
216
+ job_user_resume(job, errp);
217
+ aio_context_release(aio_context);
218
+}
82
+}
219
+
83
+
220
+void qmp_job_complete(const char *id, Error **errp)
84
+static BlockDriver bdrv_test = {
85
+ .format_name = "test",
86
+ .instance_size = sizeof(BDRVTestState),
87
+
88
+ .bdrv_close = bdrv_test_close,
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
94
+
95
+static void aio_ret_cb(void *opaque, int ret)
221
+{
96
+{
222
+ AioContext *aio_context;
97
+ int *aio_ret = opaque;
223
+ Job *job = find_job(id, &aio_context, errp);
98
+ *aio_ret = ret;
224
+
225
+ if (!job) {
226
+ return;
227
+ }
228
+
229
+ trace_qmp_job_complete(job);
230
+ job_complete(job, errp);
231
+ aio_context_release(aio_context);
232
+}
99
+}
233
+
100
+
234
+void qmp_job_finalize(const char *id, Error **errp)
101
+static void test_drv_cb_drain_all(void)
235
+{
102
+{
236
+ AioContext *aio_context;
103
+ BlockBackend *blk;
237
+ Job *job = find_job(id, &aio_context, errp);
104
+ BlockDriverState *bs;
105
+ BDRVTestState *s;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
238
+
108
+
239
+ if (!job) {
109
+ QEMUIOVector qiov;
240
+ return;
110
+ struct iovec iov = {
241
+ }
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
242
+
115
+
243
+ trace_qmp_job_finalize(job);
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
244
+ job_finalize(job, errp);
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
245
+ aio_context_release(aio_context);
118
+ &error_abort);
119
+ s = bs->opaque;
120
+ blk_insert_bs(blk, bs, &error_abort);
121
+
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
128
+
129
+ /* Now do the same while a request is pending */
130
+ aio_ret = -EINPROGRESS;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
134
+
135
+ g_assert_cmpint(s->drain_count, ==, 0);
136
+ bdrv_drain_all_begin();
137
+ g_assert_cmpint(aio_ret, ==, 0);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
141
+
142
+ bdrv_unref(bs);
143
+ blk_unref(blk);
246
+}
144
+}
247
+
145
+
248
+void qmp_job_dismiss(const char *id, Error **errp)
146
+int main(int argc, char **argv)
249
+{
147
+{
250
+ AioContext *aio_context;
148
+ bdrv_init();
251
+ Job *job = find_job(id, &aio_context, errp);
149
+ qemu_init_main_loop(&error_abort);
252
+
150
+
253
+ if (!job) {
151
+ g_test_init(&argc, &argv, NULL);
254
+ return;
255
+ }
256
+
152
+
257
+ trace_qmp_job_dismiss(job);
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
258
+ job_dismiss(&job, errp);
154
+
259
+ aio_context_release(aio_context);
155
+ return g_test_run();
260
+}
156
+}
261
diff --git a/MAINTAINERS b/MAINTAINERS
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
262
index XXXXXXX..XXXXXXX 100644
158
index XXXXXXX..XXXXXXX 100644
263
--- a/MAINTAINERS
159
--- a/tests/Makefile.include
264
+++ b/MAINTAINERS
160
+++ b/tests/Makefile.include
265
@@ -XXX,XX +XXX,XX @@ S: Supported
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
266
F: blockjob.c
162
gcov-files-test-hbitmap-y = util/hbitmap.c
267
F: include/block/blockjob.h
163
check-unit-y += tests/test-hbitmap$(EXESUF)
268
F: job.c
164
gcov-files-test-hbitmap-y = blockjob.c
269
+F: job-qmp.c
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
270
F: include/block/job.h
166
check-unit-y += tests/test-blockjob$(EXESUF)
271
F: block/backup.c
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
272
F: block/commit.c
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
273
diff --git a/Makefile.objs b/Makefile.objs
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
274
index XXXXXXX..XXXXXXX 100644
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
275
--- a/Makefile.objs
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
276
+++ b/Makefile.objs
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
277
@@ -XXX,XX +XXX,XX @@ io-obj-y = io/
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
278
ifeq ($(CONFIG_SOFTMMU),y)
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
279
common-obj-y = blockdev.o blockdev-nbd.o block/
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
280
common-obj-y += bootdevice.o iothread.o
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
281
+common-obj-y += job-qmp.o
282
common-obj-y += net/
283
common-obj-y += qdev-monitor.o device-hotplug.o
284
common-obj-$(CONFIG_WIN32) += os-win32.o
285
diff --git a/trace-events b/trace-events
286
index XXXXXXX..XXXXXXX 100644
287
--- a/trace-events
288
+++ b/trace-events
289
@@ -XXX,XX +XXX,XX @@ job_state_transition(void *job, int ret, const char *legal, const char *s0, con
290
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
291
job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
292
293
+# job-qmp.c
294
+qmp_job_cancel(void *job) "job %p"
295
+qmp_job_pause(void *job) "job %p"
296
+qmp_job_resume(void *job) "job %p"
297
+qmp_job_complete(void *job) "job %p"
298
+qmp_job_finalize(void *job) "job %p"
299
+qmp_job_dismiss(void *job) "job %p"
300
+
301
+
302
### Guest events, keep at bottom
303
304
305
--
177
--
306
2.13.6
178
2.13.6
307
179
308
180
diff view generated by jsdifflib
1
While we already moved the state related to job pausing to Job, the
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
functions to do were still BlockJob only. This commit moves them over to
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
3
Job.
4
3
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
6
---
9
include/block/blockjob.h | 32 -----------------
7
block/io.c | 12 ++++++------
10
include/block/blockjob_int.h | 7 ++++
8
1 file changed, 6 insertions(+), 6 deletions(-)
11
include/qemu/job.h | 37 ++++++++++++++++++++
12
block/backup.c | 1 +
13
block/commit.c | 1 +
14
block/mirror.c | 2 ++
15
block/stream.c | 1 +
16
blockdev.c | 6 ++--
17
blockjob.c | 81 +++++++++-----------------------------------
18
job.c | 59 ++++++++++++++++++++++++++++++++
19
tests/test-bdrv-drain.c | 1 +
20
tests/test-blockjob-txn.c | 1 +
21
tests/test-blockjob.c | 6 ++--
22
13 files changed, 133 insertions(+), 102 deletions(-)
23
9
24
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
10
diff --git a/block/io.c b/block/io.c
25
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/blockjob.h
12
--- a/block/io.c
27
+++ b/include/block/blockjob.h
13
+++ b/block/io.c
28
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
29
bool force;
30
31
/**
32
- * Set to true if the job is paused by user. Can be unpaused with the
33
- * block-job-resume QMP command.
34
- */
35
- bool user_paused;
36
-
37
- /**
38
* Set to true when the job is ready to be completed.
39
*/
40
bool ready;
41
@@ -XXX,XX +XXX,XX @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
42
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
43
44
/**
45
- * block_job_user_pause:
46
- * @job: The job to be paused.
47
- *
48
- * Asynchronously pause the specified job.
49
- * Do not allow a resume until a matching call to block_job_user_resume.
50
- */
51
-void block_job_user_pause(BlockJob *job, Error **errp);
52
-
53
-/**
54
- * block_job_paused:
55
- * @job: The job to query.
56
- *
57
- * Returns true if the job is user-paused.
58
- */
59
-bool block_job_user_paused(BlockJob *job);
60
-
61
-/**
62
- * block_job_user_resume:
63
- * @job: The job to be resumed.
64
- *
65
- * Resume the specified job.
66
- * Must be paired with a preceding block_job_user_pause.
67
- */
68
-void block_job_user_resume(BlockJob *job, Error **errp);
69
-
70
-/**
71
* block_job_user_cancel:
72
* @job: The job to be cancelled.
73
* @force: Quit a job without waiting for data to be in sync.
74
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
75
index XXXXXXX..XXXXXXX 100644
76
--- a/include/block/blockjob_int.h
77
+++ b/include/block/blockjob_int.h
78
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
79
void block_job_free(Job *job);
80
81
/**
82
+ * block_job_user_resume:
83
+ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the
84
+ * iostatus when the user resumes @job.
85
+ */
86
+void block_job_user_resume(Job *job);
87
+
88
+/**
89
* block_job_yield:
90
* @job: The job that calls the function.
91
*
92
diff --git a/include/qemu/job.h b/include/qemu/job.h
93
index XXXXXXX..XXXXXXX 100644
94
--- a/include/qemu/job.h
95
+++ b/include/qemu/job.h
96
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
97
bool paused;
98
99
/**
100
+ * Set to true if the job is paused by user. Can be unpaused with the
101
+ * block-job-resume QMP command.
102
+ */
103
+ bool user_paused;
104
+
105
+ /**
106
* Set to true if the job should cancel itself. The flag must
107
* always be tested just before toggling the busy flag from false
108
* to true. After a job has been cancelled, it should only yield
109
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
110
*/
111
void coroutine_fn (*resume)(Job *job);
112
113
+ /**
114
+ * Called when the job is resumed by the user (i.e. user_paused becomes
115
+ * false). .user_resume is called before .resume.
116
+ */
117
+ void (*user_resume)(Job *job);
118
+
119
/** Called when the job is freed */
120
void (*free)(Job *job);
121
};
122
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
123
bool job_is_cancelled(Job *job);
124
125
/**
126
+ * Request @job to pause at the next pause point. Must be paired with
127
+ * job_resume(). If the job is supposed to be resumed by user action, call
128
+ * job_user_pause() instead.
129
+ */
130
+void job_pause(Job *job);
131
+
132
+/** Resumes a @job paused with job_pause. */
133
+void job_resume(Job *job);
134
+
135
+/**
136
+ * Asynchronously pause the specified @job.
137
+ * Do not allow a resume until a matching call to job_user_resume.
138
+ */
139
+void job_user_pause(Job *job, Error **errp);
140
+
141
+/** Returns true if the job is user-paused. */
142
+bool job_user_paused(Job *job);
143
+
144
+/**
145
+ * Resume the specified @job.
146
+ * Must be paired with a preceding job_user_pause.
147
+ */
148
+void job_user_resume(Job *job, Error **errp);
149
+
150
+/**
151
* Get the next element from the list of block jobs after @job, or the
152
* first one if @job is %NULL.
153
*
154
diff --git a/block/backup.c b/block/backup.c
155
index XXXXXXX..XXXXXXX 100644
156
--- a/block/backup.c
157
+++ b/block/backup.c
158
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
159
.instance_size = sizeof(BackupBlockJob),
160
.job_type = JOB_TYPE_BACKUP,
161
.free = block_job_free,
162
+ .user_resume = block_job_user_resume,
163
.start = backup_run,
164
},
165
.commit = backup_commit,
166
diff --git a/block/commit.c b/block/commit.c
167
index XXXXXXX..XXXXXXX 100644
168
--- a/block/commit.c
169
+++ b/block/commit.c
170
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
171
.instance_size = sizeof(CommitBlockJob),
172
.job_type = JOB_TYPE_COMMIT,
173
.free = block_job_free,
174
+ .user_resume = block_job_user_resume,
175
.start = commit_run,
176
},
177
};
178
diff --git a/block/mirror.c b/block/mirror.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/mirror.c
181
+++ b/block/mirror.c
182
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
183
.instance_size = sizeof(MirrorBlockJob),
184
.job_type = JOB_TYPE_MIRROR,
185
.free = block_job_free,
186
+ .user_resume = block_job_user_resume,
187
.start = mirror_run,
188
.pause = mirror_pause,
189
},
190
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
191
.instance_size = sizeof(MirrorBlockJob),
192
.job_type = JOB_TYPE_COMMIT,
193
.free = block_job_free,
194
+ .user_resume = block_job_user_resume,
195
.start = mirror_run,
196
.pause = mirror_pause,
197
},
198
diff --git a/block/stream.c b/block/stream.c
199
index XXXXXXX..XXXXXXX 100644
200
--- a/block/stream.c
201
+++ b/block/stream.c
202
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
203
.job_type = JOB_TYPE_STREAM,
204
.free = block_job_free,
205
.start = stream_run,
206
+ .user_resume = block_job_user_resume,
207
},
208
};
209
210
diff --git a/blockdev.c b/blockdev.c
211
index XXXXXXX..XXXXXXX 100644
212
--- a/blockdev.c
213
+++ b/blockdev.c
214
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
215
force = false;
216
}
217
218
- if (block_job_user_paused(job) && !force) {
219
+ if (job_user_paused(&job->job) && !force) {
220
error_setg(errp, "The block job for device '%s' is currently paused",
221
device);
222
goto out;
223
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_pause(const char *device, Error **errp)
224
}
225
226
trace_qmp_block_job_pause(job);
227
- block_job_user_pause(job, errp);
228
+ job_user_pause(&job->job, errp);
229
aio_context_release(aio_context);
230
}
231
232
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_resume(const char *device, Error **errp)
233
}
234
235
trace_qmp_block_job_resume(job);
236
- block_job_user_resume(job, errp);
237
+ job_user_resume(&job->job, errp);
238
aio_context_release(aio_context);
239
}
240
241
diff --git a/blockjob.c b/blockjob.c
242
index XXXXXXX..XXXXXXX 100644
243
--- a/blockjob.c
244
+++ b/blockjob.c
245
@@ -XXX,XX +XXX,XX @@ static void block_job_txn_del_job(BlockJob *job)
246
}
15
}
247
}
16
}
248
17
249
-/* Assumes the job_mutex is held */
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
250
-static bool job_timer_not_pending(Job *job)
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
251
-{
20
{
252
- return !timer_pending(&job->sleep_timer);
21
BdrvChild *child, *tmp;
253
-}
22
bool waited;
254
-
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
255
-static void block_job_pause(BlockJob *job)
24
*/
256
-{
25
bdrv_ref(bs);
257
- job->job.pause_count++;
26
}
258
-}
27
- waited |= bdrv_drain_recurse(bs, begin);
259
-
28
+ waited |= bdrv_drain_recurse(bs);
260
-static void block_job_resume(BlockJob *job)
29
if (in_main_loop) {
261
-{
30
bdrv_unref(bs);
262
- assert(job->job.pause_count > 0);
31
}
263
- job->job.pause_count--;
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
264
- if (job->job.pause_count) {
265
- return;
266
- }
267
-
268
- /* kick only if no timer is pending */
269
- job_enter_cond(&job->job, job_timer_not_pending);
270
-}
271
-
272
static void block_job_attached_aio_context(AioContext *new_context,
273
void *opaque);
274
static void block_job_detach_aio_context(void *opaque);
275
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
276
job->driver->attached_aio_context(job, new_context);
277
}
33
}
278
34
279
- block_job_resume(job);
35
bdrv_drain_invoke(bs, true);
280
+ job_resume(&job->job);
36
- bdrv_drain_recurse(bs, true);
37
+ bdrv_drain_recurse(bs);
281
}
38
}
282
39
283
static void block_job_drain(BlockJob *job)
40
void bdrv_drained_end(BlockDriverState *bs)
284
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
285
/* In case the job terminates during aio_poll()... */
42
286
job_ref(&job->job);
43
bdrv_parent_drained_end(bs);
287
44
bdrv_drain_invoke(bs, false);
288
- block_job_pause(job);
45
- bdrv_drain_recurse(bs, false);
289
+ job_pause(&job->job);
46
+ bdrv_drain_recurse(bs);
290
47
aio_enable_external(bdrv_get_aio_context(bs));
291
while (!job->job.paused && !job->completed) {
292
block_job_drain(job);
293
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
294
static void child_job_drained_begin(BdrvChild *c)
295
{
296
BlockJob *job = c->opaque;
297
- block_job_pause(job);
298
+ job_pause(&job->job);
299
}
48
}
300
49
301
static void child_job_drained_end(BdrvChild *c)
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
302
{
51
aio_context_acquire(aio_context);
303
BlockJob *job = c->opaque;
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
304
- block_job_resume(job);
53
if (aio_context == bdrv_get_aio_context(bs)) {
305
+ job_resume(&job->job);
54
- waited |= bdrv_drain_recurse(bs, true);
306
}
55
+ waited |= bdrv_drain_recurse(bs);
307
56
}
308
static const BdrvChildRole child_job = {
57
}
309
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
58
aio_context_release(aio_context);
310
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
311
block_job_iostatus_reset(job);
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
312
}
66
}
313
- if (job->user_paused) {
314
+ if (job->job.user_paused) {
315
/* Do not call block_job_enter here, the caller will handle it. */
316
- job->user_paused = false;
317
+ job->job.user_paused = false;
318
job->job.pause_count--;
319
}
320
job->job.cancelled = true;
321
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
322
*jobptr = NULL;
323
}
324
325
-void block_job_user_pause(BlockJob *job, Error **errp)
326
-{
327
- if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) {
328
- return;
329
- }
330
- if (job->user_paused) {
331
- error_setg(errp, "Job is already paused");
332
- return;
333
- }
334
- job->user_paused = true;
335
- block_job_pause(job);
336
-}
337
-
338
-bool block_job_user_paused(BlockJob *job)
339
-{
340
- return job->user_paused;
341
-}
342
-
343
-void block_job_user_resume(BlockJob *job, Error **errp)
344
-{
345
- assert(job);
346
- if (!job->user_paused || job->job.pause_count <= 0) {
347
- error_setg(errp, "Can't resume a job that was not paused");
348
- return;
349
- }
350
- if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) {
351
- return;
352
- }
353
- block_job_iostatus_reset(job);
354
- job->user_paused = false;
355
- block_job_resume(job);
356
-}
357
-
358
void block_job_cancel(BlockJob *job, bool force)
359
{
360
if (job->job.status == JOB_STATUS_CONCLUDED) {
361
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
362
363
assert(is_block_job(&job->job));
364
assert(job->job.driver->free == &block_job_free);
365
+ assert(job->job.driver->user_resume == &block_job_user_resume);
366
367
job->driver = driver;
368
job->blk = blk;
369
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
370
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
371
return;
372
}
373
- assert(job->user_paused && job->job.pause_count > 0);
374
+ assert(job->job.user_paused && job->job.pause_count > 0);
375
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
376
}
377
378
+void block_job_user_resume(Job *job)
379
+{
380
+ BlockJob *bjob = container_of(job, BlockJob, job);
381
+ block_job_iostatus_reset(bjob);
382
+}
383
+
384
void block_job_event_ready(BlockJob *job)
385
{
386
job_state_transition(&job->job, JOB_STATUS_READY);
387
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
388
action, &error_abort);
389
}
390
if (action == BLOCK_ERROR_ACTION_STOP) {
391
- block_job_pause(job);
392
+ job_pause(&job->job);
393
/* make the pause user visible, which will be resumed from QMP. */
394
- job->user_paused = true;
395
+ job->job.user_paused = true;
396
block_job_iostatus_set_err(job, error);
397
}
398
return action;
399
diff --git a/job.c b/job.c
400
index XXXXXXX..XXXXXXX 100644
401
--- a/job.c
402
+++ b/job.c
403
@@ -XXX,XX +XXX,XX @@ void job_start(Job *job)
404
aio_co_enter(job->aio_context, job->co);
405
}
406
407
+/* Assumes the block_job_mutex is held */
408
+static bool job_timer_not_pending(Job *job)
409
+{
410
+ return !timer_pending(&job->sleep_timer);
411
+}
412
+
413
+void job_pause(Job *job)
414
+{
415
+ job->pause_count++;
416
+}
417
+
418
+void job_resume(Job *job)
419
+{
420
+ assert(job->pause_count > 0);
421
+ job->pause_count--;
422
+ if (job->pause_count) {
423
+ return;
424
+ }
425
+
426
+ /* kick only if no timer is pending */
427
+ job_enter_cond(job, job_timer_not_pending);
428
+}
429
+
430
+void job_user_pause(Job *job, Error **errp)
431
+{
432
+ if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) {
433
+ return;
434
+ }
435
+ if (job->user_paused) {
436
+ error_setg(errp, "Job is already paused");
437
+ return;
438
+ }
439
+ job->user_paused = true;
440
+ job_pause(job);
441
+}
442
+
443
+bool job_user_paused(Job *job)
444
+{
445
+ return job->user_paused;
446
+}
447
+
448
+void job_user_resume(Job *job, Error **errp)
449
+{
450
+ assert(job);
451
+ if (!job->user_paused || job->pause_count <= 0) {
452
+ error_setg(errp, "Can't resume a job that was not paused");
453
+ return;
454
+ }
455
+ if (job_apply_verb(job, JOB_VERB_RESUME, errp)) {
456
+ return;
457
+ }
458
+ if (job->driver->user_resume) {
459
+ job->driver->user_resume(job);
460
+ }
461
+ job->user_paused = false;
462
+ job_resume(job);
463
+}
464
+
465
+
466
typedef struct {
467
Job *job;
468
JobDeferToMainLoopFn *fn;
469
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
470
index XXXXXXX..XXXXXXX 100644
471
--- a/tests/test-bdrv-drain.c
472
+++ b/tests/test-bdrv-drain.c
473
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
474
.job_driver = {
475
.instance_size = sizeof(TestBlockJob),
476
.free = block_job_free,
477
+ .user_resume = block_job_user_resume,
478
.start = test_job_start,
479
},
480
.complete = test_job_complete,
481
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
482
index XXXXXXX..XXXXXXX 100644
483
--- a/tests/test-blockjob-txn.c
484
+++ b/tests/test-blockjob-txn.c
485
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
486
.job_driver = {
487
.instance_size = sizeof(TestBlockJob),
488
.free = block_job_free,
489
+ .user_resume = block_job_user_resume,
490
.start = test_block_job_run,
491
},
492
};
493
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
494
index XXXXXXX..XXXXXXX 100644
495
--- a/tests/test-blockjob.c
496
+++ b/tests/test-blockjob.c
497
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
498
.job_driver = {
499
.instance_size = sizeof(BlockJob),
500
.free = block_job_free,
501
+ .user_resume = block_job_user_resume,
502
},
503
};
504
505
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
506
.job_driver = {
507
.instance_size = sizeof(CancelJob),
508
.free = block_job_free,
509
+ .user_resume = block_job_user_resume,
510
.start = cancel_job_start,
511
},
512
.complete = cancel_job_complete,
513
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
514
job_start(&job->job);
515
assert(job->job.status == JOB_STATUS_RUNNING);
516
517
- block_job_user_pause(job, &error_abort);
518
+ job_user_pause(&job->job, &error_abort);
519
block_job_enter(job);
520
assert(job->job.status == JOB_STATUS_PAUSED);
521
522
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
523
block_job_enter(job);
524
assert(job->job.status == JOB_STATUS_READY);
525
526
- block_job_user_pause(job, &error_abort);
527
+ job_user_pause(&job->job, &error_abort);
528
block_job_enter(job);
529
assert(job->job.status == JOB_STATUS_STANDBY);
530
67
531
--
68
--
532
2.13.6
69
2.13.6
533
70
534
71
diff view generated by jsdifflib
1
Instead of having a 'bool ready' in BlockJob, add a function that
1
The device is drained, so there is no point in waiting for requests at
2
derives its value from the job status.
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
there.
3
4
4
At the same time, this fixes the behaviour to match what the QAPI
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
5
documentation promises for query-block-job: 'true if the job may be
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
6
completed'. When the ready flag was introduced in commit ef6dbf1e46e,
7
done by a separate bdrv_drain_invoke() call.
7
the flag never had to be reset to match the description because after
8
being ready, the jobs would immediately complete and disappear.
9
10
Job transactions and manual job finalisation were introduced only later.
11
With these changes, jobs may stay around even after having completed
12
(and they are not ready to be completed a second time), however their
13
patches forgot to reset the ready flag.
14
8
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
---
12
---
18
include/block/blockjob.h | 5 -----
13
block/io.c | 2 --
19
include/qemu/job.h | 3 +++
14
1 file changed, 2 deletions(-)
20
blockjob.c | 3 +--
21
job.c | 22 ++++++++++++++++++++++
22
qemu-img.c | 2 +-
23
tests/test-blockjob.c | 2 +-
24
6 files changed, 28 insertions(+), 9 deletions(-)
25
15
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
16
diff --git a/block/io.c b/block/io.c
27
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
18
--- a/block/io.c
29
+++ b/include/block/blockjob.h
19
+++ b/block/io.c
30
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
31
/** The block device on which the job is operating. */
21
32
BlockBackend *blk;
22
bdrv_parent_drained_end(bs);
33
23
bdrv_drain_invoke(bs, false);
34
- /**
24
- bdrv_drain_recurse(bs);
35
- * Set to true when the job is ready to be completed.
25
aio_enable_external(bdrv_get_aio_context(bs));
36
- */
37
- bool ready;
38
-
39
/** Status that is published by the query-block-jobs QMP API */
40
BlockDeviceIoStatus iostatus;
41
42
diff --git a/include/qemu/job.h b/include/qemu/job.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/qemu/job.h
45
+++ b/include/qemu/job.h
46
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job);
47
/** Returns whether the job is in a completed state. */
48
bool job_is_completed(Job *job);
49
50
+/** Returns whether the job is ready to be completed. */
51
+bool job_is_ready(Job *job);
52
+
53
/**
54
* Request @job to pause at the next pause point. Must be paired with
55
* job_resume(). If the job is supposed to be resumed by user action, call
56
diff --git a/blockjob.c b/blockjob.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/blockjob.c
59
+++ b/blockjob.c
60
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
61
info->offset = job->offset;
62
info->speed = job->speed;
63
info->io_status = job->iostatus;
64
- info->ready = job->ready;
65
+ info->ready = job_is_ready(&job->job),
66
info->status = job->job.status;
67
info->auto_finalize = job->job.auto_finalize;
68
info->auto_dismiss = job->job.auto_dismiss;
69
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job)
70
void block_job_event_ready(BlockJob *job)
71
{
72
job_state_transition(&job->job, JOB_STATUS_READY);
73
- job->ready = true;
74
75
if (block_job_is_internal(job)) {
76
return;
77
diff --git a/job.c b/job.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/job.c
80
+++ b/job.c
81
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
82
return job->cancelled;
83
}
26
}
84
27
85
+bool job_is_ready(Job *job)
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
86
+{
29
aio_enable_external(aio_context);
87
+ switch (job->status) {
30
bdrv_parent_drained_end(bs);
88
+ case JOB_STATUS_UNDEFINED:
31
bdrv_drain_invoke(bs, false);
89
+ case JOB_STATUS_CREATED:
32
- bdrv_drain_recurse(bs);
90
+ case JOB_STATUS_RUNNING:
33
aio_context_release(aio_context);
91
+ case JOB_STATUS_PAUSED:
34
}
92
+ case JOB_STATUS_WAITING:
93
+ case JOB_STATUS_PENDING:
94
+ case JOB_STATUS_ABORTING:
95
+ case JOB_STATUS_CONCLUDED:
96
+ case JOB_STATUS_NULL:
97
+ return false;
98
+ case JOB_STATUS_READY:
99
+ case JOB_STATUS_STANDBY:
100
+ return true;
101
+ default:
102
+ g_assert_not_reached();
103
+ }
104
+ return false;
105
+}
106
+
107
bool job_is_completed(Job *job)
108
{
109
switch (job->status) {
110
diff --git a/qemu-img.c b/qemu-img.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/qemu-img.c
113
+++ b/qemu-img.c
114
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
115
aio_poll(aio_context, true);
116
qemu_progress_print(job->len ?
117
((float)job->offset / job->len * 100.f) : 0.0f, 0);
118
- } while (!job->ready && !job_is_completed(&job->job));
119
+ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
120
121
if (!job_is_completed(&job->job)) {
122
ret = job_complete_sync(&job->job, errp);
123
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
124
index XXXXXXX..XXXXXXX 100644
125
--- a/tests/test-blockjob.c
126
+++ b/tests/test-blockjob.c
127
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
128
goto defer;
129
}
130
131
- if (!s->common.ready && s->should_converge) {
132
+ if (!job_is_ready(&s->common.job) && s->should_converge) {
133
block_job_event_ready(&s->common);
134
}
135
35
136
--
36
--
137
2.13.6
37
2.13.6
138
38
139
39
diff view generated by jsdifflib
1
This moves the job_type field from BlockJobDriver to JobDriver.
1
Drain requests are propagated to child nodes, parent nodes and directly
2
to the AioContext. The order in which this happened was different
3
between all combinations of drain/drain_all and begin/end.
4
5
The correct order is to keep children only drained when their parents
6
are also drained. This means that at the start of a drained section, the
7
AioContext needs to be drained first, the parents second and only then
8
the children. The correct order for the end of a drained section is the
9
opposite.
10
11
This patch changes the three other functions to follow the example of
12
bdrv_drained_begin(), which is the only one that got it right.
2
13
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: John Snow <jsnow@redhat.com>
6
---
16
---
7
include/block/blockjob_int.h | 3 ---
17
block/io.c | 12 ++++++++----
8
include/qemu/job.h | 11 +++++++++++
18
1 file changed, 8 insertions(+), 4 deletions(-)
9
block/backup.c | 2 +-
10
block/commit.c | 2 +-
11
block/mirror.c | 4 ++--
12
block/stream.c | 2 +-
13
blockjob.c | 16 +++++++---------
14
job.c | 10 ++++++++++
15
8 files changed, 33 insertions(+), 17 deletions(-)
16
19
17
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
20
diff --git a/block/io.c b/block/io.c
18
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob_int.h
22
--- a/block/io.c
20
+++ b/include/block/blockjob_int.h
23
+++ b/block/io.c
21
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
22
/** Generic JobDriver callbacks and settings */
23
JobDriver job_driver;
24
25
- /** String describing the operation, part of query-block-jobs QMP API */
26
- JobType job_type;
27
-
28
/** Mandatory: Entrypoint for the Coroutine. */
29
CoroutineEntry *start;
30
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/qemu/job.h
34
+++ b/include/qemu/job.h
35
@@ -XXX,XX +XXX,XX @@
36
#ifndef JOB_H
37
#define JOB_H
38
39
+#include "qapi/qapi-types-block-core.h"
40
+
41
typedef struct JobDriver JobDriver;
42
43
/**
44
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
45
struct JobDriver {
46
/** Derived Job struct size */
47
size_t instance_size;
48
+
49
+ /** Enum describing the operation */
50
+ JobType job_type;
51
};
52
53
54
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
55
*/
56
void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
57
58
+/** Returns the JobType of a given Job. */
59
+JobType job_type(const Job *job);
60
+
61
+/** Returns the enum string for the JobType of a given Job. */
62
+const char *job_type_str(const Job *job);
63
+
64
#endif
65
diff --git a/block/backup.c b/block/backup.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/block/backup.c
68
+++ b/block/backup.c
69
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
70
static const BlockJobDriver backup_job_driver = {
71
.job_driver = {
72
.instance_size = sizeof(BackupBlockJob),
73
+ .job_type = JOB_TYPE_BACKUP,
74
},
75
- .job_type = JOB_TYPE_BACKUP,
76
.start = backup_run,
77
.commit = backup_commit,
78
.abort = backup_abort,
79
diff --git a/block/commit.c b/block/commit.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/commit.c
82
+++ b/block/commit.c
83
@@ -XXX,XX +XXX,XX @@ out:
84
static const BlockJobDriver commit_job_driver = {
85
.job_driver = {
86
.instance_size = sizeof(CommitBlockJob),
87
+ .job_type = JOB_TYPE_COMMIT,
88
},
89
- .job_type = JOB_TYPE_COMMIT,
90
.start = commit_run,
91
};
92
93
diff --git a/block/mirror.c b/block/mirror.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/mirror.c
96
+++ b/block/mirror.c
97
@@ -XXX,XX +XXX,XX @@ static void mirror_drain(BlockJob *job)
98
static const BlockJobDriver mirror_job_driver = {
99
.job_driver = {
100
.instance_size = sizeof(MirrorBlockJob),
101
+ .job_type = JOB_TYPE_MIRROR,
102
},
103
- .job_type = JOB_TYPE_MIRROR,
104
.start = mirror_run,
105
.complete = mirror_complete,
106
.pause = mirror_pause,
107
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
108
static const BlockJobDriver commit_active_job_driver = {
109
.job_driver = {
110
.instance_size = sizeof(MirrorBlockJob),
111
+ .job_type = JOB_TYPE_COMMIT,
112
},
113
- .job_type = JOB_TYPE_COMMIT,
114
.start = mirror_run,
115
.complete = mirror_complete,
116
.pause = mirror_pause,
117
diff --git a/block/stream.c b/block/stream.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/block/stream.c
120
+++ b/block/stream.c
121
@@ -XXX,XX +XXX,XX @@ out:
122
static const BlockJobDriver stream_job_driver = {
123
.job_driver = {
124
.instance_size = sizeof(StreamBlockJob),
125
+ .job_type = JOB_TYPE_STREAM,
126
},
127
- .job_type = JOB_TYPE_STREAM,
128
.start = stream_run,
129
};
130
131
diff --git a/blockjob.c b/blockjob.c
132
index XXXXXXX..XXXXXXX 100644
133
--- a/blockjob.c
134
+++ b/blockjob.c
135
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
136
static char *child_job_get_parent_desc(BdrvChild *c)
137
{
138
BlockJob *job = c->opaque;
139
- return g_strdup_printf("%s job '%s'",
140
- JobType_str(job->driver->job_type),
141
- job->job.id);
142
+ return g_strdup_printf("%s job '%s'", job_type_str(&job->job), job->job.id);
143
}
144
145
static void child_job_drained_begin(BdrvChild *c)
146
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
147
return NULL;
148
}
149
info = g_new0(BlockJobInfo, 1);
150
- info->type = g_strdup(JobType_str(job->driver->job_type));
151
+ info->type = g_strdup(job_type_str(&job->job));
152
info->device = g_strdup(job->job.id);
153
info->len = job->len;
154
info->busy = atomic_read(&job->busy);
155
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
156
return;
25
return;
157
}
26
}
158
27
159
- qapi_event_send_block_job_cancelled(job->driver->job_type,
28
+ /* Stop things in parent-to-child order */
160
+ qapi_event_send_block_job_cancelled(job_type(&job->job),
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
161
job->job.id,
30
aio_disable_external(bdrv_get_aio_context(bs));
162
job->len,
31
bdrv_parent_drained_begin(bs);
163
job->offset,
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
164
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
165
return;
33
return;
166
}
34
}
167
35
168
- qapi_event_send_block_job_completed(job->driver->job_type,
36
- bdrv_parent_drained_end(bs);
169
+ qapi_event_send_block_job_completed(job_type(&job->job),
37
+ /* Re-enable things in child-to-parent order */
170
job->job.id,
38
bdrv_drain_invoke(bs, false);
171
job->len,
39
+ bdrv_parent_drained_end(bs);
172
job->offset,
40
aio_enable_external(bdrv_get_aio_context(bs));
173
@@ -XXX,XX +XXX,XX @@ static int block_job_event_pending(BlockJob *job)
41
}
174
{
42
175
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
176
if (!job->auto_finalize && !block_job_is_internal(job)) {
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
177
- qapi_event_send_block_job_pending(job->driver->job_type,
45
AioContext *aio_context = bdrv_get_aio_context(bs);
178
+ qapi_event_send_block_job_pending(job_type(&job->job),
46
179
job->job.id,
47
+ /* Stop things in parent-to-child order */
180
&error_abort);
48
aio_context_acquire(aio_context);
49
- bdrv_parent_drained_begin(bs);
50
aio_disable_external(aio_context);
51
+ bdrv_parent_drained_begin(bs);
52
bdrv_drain_invoke(bs, true);
53
aio_context_release(aio_context);
54
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
AioContext *aio_context = bdrv_get_aio_context(bs);
58
59
+ /* Re-enable things in child-to-parent order */
60
aio_context_acquire(aio_context);
61
- aio_enable_external(aio_context);
62
- bdrv_parent_drained_end(bs);
63
bdrv_drain_invoke(bs, false);
64
+ bdrv_parent_drained_end(bs);
65
+ aio_enable_external(aio_context);
66
aio_context_release(aio_context);
181
}
67
}
182
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
68
183
block_job_sleep_timer_cb, job);
184
185
error_setg(&job->blocker, "block device is in use by block job: %s",
186
- JobType_str(driver->job_type));
187
+ job_type_str(&job->job));
188
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
189
bs->job = job;
190
191
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job)
192
return;
193
}
194
195
- qapi_event_send_block_job_ready(job->driver->job_type,
196
+ qapi_event_send_block_job_ready(job_type(&job->job),
197
job->job.id,
198
job->len,
199
job->offset,
200
diff --git a/job.c b/job.c
201
index XXXXXXX..XXXXXXX 100644
202
--- a/job.c
203
+++ b/job.c
204
@@ -XXX,XX +XXX,XX @@
205
#include "qemu/job.h"
206
#include "qemu/id.h"
207
208
+JobType job_type(const Job *job)
209
+{
210
+ return job->driver->job_type;
211
+}
212
+
213
+const char *job_type_str(const Job *job)
214
+{
215
+ return JobType_str(job_type(job));
216
+}
217
+
218
void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
219
{
220
Job *job;
221
--
69
--
222
2.13.6
70
2.13.6
223
71
224
72
diff view generated by jsdifflib
1
Go through the Job layer in order to send QMP events. For the moment,
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
these functions only call a notifier in the BlockJob layer that sends
2
qemuio_command(). This means that the lock is taken twice now in the
3
the existing commands.
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
4
5
5
This uses notifiers rather than JobDriver callbacks because internal
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
6
users of jobs won't receive QMP events, but might still be interested
7
in getting notified for the events.
8
7
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
10
---
12
include/block/blockjob.h | 9 +++++++++
11
hmp.c | 6 ------
13
include/qemu/job.h | 18 ++++++++++++++++++
12
1 file changed, 6 deletions(-)
14
blockjob.c | 41 +++++++++++++++++++++++++++--------------
15
job.c | 19 +++++++++++++++++++
16
4 files changed, 73 insertions(+), 14 deletions(-)
17
13
18
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
14
diff --git a/hmp.c b/hmp.c
19
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/blockjob.h
16
--- a/hmp.c
21
+++ b/include/block/blockjob.h
17
+++ b/hmp.c
22
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
23
/** Block other operations when block job is running */
19
{
24
Error *blocker;
20
BlockBackend *blk;
25
21
BlockBackend *local_blk = NULL;
26
+ /** Called when a cancelled job is finalised. */
22
- AioContext *aio_context;
27
+ Notifier finalize_cancelled_notifier;
23
const char* device = qdict_get_str(qdict, "device");
28
+
24
const char* command = qdict_get_str(qdict, "command");
29
+ /** Called when a successfully completed job is finalised. */
25
Error *err = NULL;
30
+ Notifier finalize_completed_notifier;
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
31
+
32
+ /** Called when the job transitions to PENDING */
33
+ Notifier pending_notifier;
34
+
35
/** BlockDriverStates that are involved in this block job */
36
GSList *nodes;
37
38
diff --git a/include/qemu/job.h b/include/qemu/job.h
39
index XXXXXXX..XXXXXXX 100644
40
--- a/include/qemu/job.h
41
+++ b/include/qemu/job.h
42
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
43
/** True if this job should automatically dismiss itself */
44
bool auto_dismiss;
45
46
+ /** Notifiers called when a cancelled job is finalised */
47
+ NotifierList on_finalize_cancelled;
48
+
49
+ /** Notifiers called when a successfully completed job is finalised */
50
+ NotifierList on_finalize_completed;
51
+
52
+ /** Notifiers called when the job transitions to PENDING */
53
+ NotifierList on_pending;
54
+
55
/** Element of the list of jobs */
56
QLIST_ENTRY(Job) job_list;
57
} Job;
58
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
59
*/
60
void job_unref(Job *job);
61
62
+/** To be called when a cancelled job is finalised. */
63
+void job_event_cancelled(Job *job);
64
+
65
+/** To be called when a successfully completed job is finalised. */
66
+void job_event_completed(Job *job);
67
+
68
+/** To be called when the job transitions to PENDING */
69
+void job_event_pending(Job *job);
70
+
71
/**
72
* Conditionally enter the job coroutine if the job is ready to run, not
73
* already busy and fn() returns true. fn() is called while under the job_lock
74
diff --git a/blockjob.c b/blockjob.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/blockjob.c
77
+++ b/blockjob.c
78
@@ -XXX,XX +XXX,XX @@
79
#include "qemu/coroutine.h"
80
#include "qemu/timer.h"
81
82
-static void block_job_event_cancelled(BlockJob *job);
83
-static void block_job_event_completed(BlockJob *job, const char *msg);
84
-static void block_job_event_pending(BlockJob *job);
85
-
86
/* Transactional group of block jobs */
87
struct BlockJobTxn {
88
89
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
90
/* Emit events only if we actually started */
91
if (job_started(&job->job)) {
92
if (job_is_cancelled(&job->job)) {
93
- block_job_event_cancelled(job);
94
+ job_event_cancelled(&job->job);
95
} else {
96
- const char *msg = NULL;
97
- if (job->ret < 0) {
98
- msg = strerror(-job->ret);
99
- }
100
- block_job_event_completed(job, msg);
101
+ job_event_completed(&job->job);
102
}
27
}
103
}
28
}
104
29
105
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
30
- aio_context = blk_get_aio_context(blk);
106
{
31
- aio_context_acquire(aio_context);
107
job_state_transition(&job->job, JOB_STATUS_PENDING);
32
-
108
if (!job->job.auto_finalize) {
33
/*
109
- block_job_event_pending(job);
34
* Notably absent: Proper permission management. This is sad, but it seems
110
+ job_event_pending(&job->job);
35
* almost impossible to achieve without changing the semantics and thereby
111
}
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
112
return 0;
37
*/
113
}
38
qemuio_command(blk, command);
114
@@ -XXX,XX +XXX,XX @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
39
115
}
40
- aio_context_release(aio_context);
116
}
41
-
117
42
fail:
118
-static void block_job_event_cancelled(BlockJob *job)
43
blk_unref(local_blk);
119
+static void block_job_event_cancelled(Notifier *n, void *opaque)
44
hmp_handle_error(mon, &err);
120
{
121
+ BlockJob *job = opaque;
122
+
123
if (block_job_is_internal(job)) {
124
return;
125
}
126
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
127
&error_abort);
128
}
129
130
-static void block_job_event_completed(BlockJob *job, const char *msg)
131
+static void block_job_event_completed(Notifier *n, void *opaque)
132
{
133
+ BlockJob *job = opaque;
134
+ const char *msg = NULL;
135
+
136
if (block_job_is_internal(job)) {
137
return;
138
}
139
140
+ if (job->ret < 0) {
141
+ msg = strerror(-job->ret);
142
+ }
143
+
144
qapi_event_send_block_job_completed(job_type(&job->job),
145
job->job.id,
146
job->len,
147
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
148
&error_abort);
149
}
150
151
-static void block_job_event_pending(BlockJob *job)
152
+static void block_job_event_pending(Notifier *n, void *opaque)
153
{
154
+ BlockJob *job = opaque;
155
+
156
if (block_job_is_internal(job)) {
157
return;
158
}
159
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
160
job->cb = cb;
161
job->opaque = opaque;
162
163
+ job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
164
+ job->finalize_completed_notifier.notify = block_job_event_completed;
165
+ job->pending_notifier.notify = block_job_event_pending;
166
+
167
+ notifier_list_add(&job->job.on_finalize_cancelled,
168
+ &job->finalize_cancelled_notifier);
169
+ notifier_list_add(&job->job.on_finalize_completed,
170
+ &job->finalize_completed_notifier);
171
+ notifier_list_add(&job->job.on_pending, &job->pending_notifier);
172
+
173
error_setg(&job->blocker, "block device is in use by block job: %s",
174
job_type_str(&job->job));
175
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
176
diff --git a/job.c b/job.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/job.c
179
+++ b/job.c
180
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
181
job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
182
job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
183
184
+ notifier_list_init(&job->on_finalize_cancelled);
185
+ notifier_list_init(&job->on_finalize_completed);
186
+ notifier_list_init(&job->on_pending);
187
+
188
job_state_transition(job, JOB_STATUS_CREATED);
189
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
190
QEMU_CLOCK_REALTIME, SCALE_NS,
191
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
192
}
193
}
194
195
+void job_event_cancelled(Job *job)
196
+{
197
+ notifier_list_notify(&job->on_finalize_cancelled, job);
198
+}
199
+
200
+void job_event_completed(Job *job)
201
+{
202
+ notifier_list_notify(&job->on_finalize_completed, job);
203
+}
204
+
205
+void job_event_pending(Job *job)
206
+{
207
+ notifier_list_notify(&job->on_pending, job);
208
+}
209
+
210
void job_enter_cond(Job *job, bool(*fn)(Job *job))
211
{
212
if (!job_started(job)) {
213
--
45
--
214
2.13.6
46
2.13.6
215
47
216
48
diff view generated by jsdifflib
1
This moves block_job_yield() to the Job layer.
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
Since bdrv_co_preadv does all neccessary checks including
4
reading after the end of the backing file, avoid duplication
5
of verification before bdrv_co_preadv call.
6
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
11
---
6
include/block/blockjob_int.h | 8 --------
12
block/qcow2.h | 3 ---
7
include/qemu/job.h | 9 +++++++--
13
block/qcow2.c | 51 ++++++++-------------------------------------------
8
block/backup.c | 2 +-
14
2 files changed, 8 insertions(+), 46 deletions(-)
9
block/mirror.c | 2 +-
10
blockjob.c | 16 ----------------
11
job.c | 20 ++++++++++++++++++--
12
tests/test-blockjob-txn.c | 2 +-
13
7 files changed, 28 insertions(+), 31 deletions(-)
14
15
15
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
16
diff --git a/block/qcow2.h b/block/qcow2.h
16
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/blockjob_int.h
18
--- a/block/qcow2.h
18
+++ b/include/block/blockjob_int.h
19
+++ b/block/qcow2.h
19
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job);
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
20
void block_job_drain(Job *job);
21
}
21
22
22
/**
23
/* qcow2.c functions */
23
- * block_job_yield:
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
24
- * @job: The job that calls the function.
25
- int64_t sector_num, int nb_sectors);
25
- *
26
- * Yield the block job coroutine.
27
- */
28
-void block_job_yield(BlockJob *job);
29
-
26
-
30
-/**
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
31
* block_job_ratelimit_get_delay:
28
int refcount_order, bool generous_increase,
32
*
29
uint64_t *refblock_count);
33
* Calculate and return delay for the next request in ns. See the documentation
30
diff --git a/block/qcow2.c b/block/qcow2.c
34
diff --git a/include/qemu/job.h b/include/qemu/job.h
35
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
36
--- a/include/qemu/job.h
32
--- a/block/qcow2.c
37
+++ b/include/qemu/job.h
33
+++ b/block/qcow2.c
38
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job);
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
39
35
return status;
40
/**
41
* @job: The job that calls the function.
42
+ *
43
+ * Yield the job coroutine.
44
+ */
45
+void job_yield(Job *job);
46
+
47
+/**
48
+ * @job: The job that calls the function.
49
* @ns: How many nanoseconds to stop for.
50
*
51
* Put the job to sleep (assuming that it wasn't canceled) for @ns
52
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
53
54
/* TODO To be removed from the public interface */
55
void job_state_transition(Job *job, JobStatus s1);
56
-void coroutine_fn job_do_yield(Job *job, uint64_t ns);
57
-bool job_should_pause(Job *job);
58
void job_do_dismiss(Job *job);
59
60
#endif
61
diff --git a/block/backup.c b/block/backup.c
62
index XXXXXXX..XXXXXXX 100644
63
--- a/block/backup.c
64
+++ b/block/backup.c
65
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
66
while (!job_is_cancelled(&job->common.job)) {
67
/* Yield until the job is cancelled. We just let our before_write
68
* notify callback service CoW requests. */
69
- block_job_yield(&job->common);
70
+ job_yield(&job->common.job);
71
}
72
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
73
ret = backup_run_incremental(job);
74
diff --git a/block/mirror.c b/block/mirror.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/block/mirror.c
77
+++ b/block/mirror.c
78
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
79
block_job_event_ready(&s->common);
80
s->synced = true;
81
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
82
- block_job_yield(&s->common);
83
+ job_yield(&s->common.job);
84
}
85
s->common.job.cancelled = false;
86
goto immediate_exit;
87
diff --git a/blockjob.c b/blockjob.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/blockjob.c
90
+++ b/blockjob.c
91
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
92
return job;
93
}
36
}
94
37
95
-void block_job_yield(BlockJob *job)
38
-/* handle reading after the end of the backing file */
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
40
- int64_t offset, int bytes)
96
-{
41
-{
97
- assert(job->job.busy);
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
43
- int n1;
98
-
44
-
99
- /* Check cancellation *before* setting busy = false, too! */
45
- if ((offset + bytes) <= bs_size) {
100
- if (job_is_cancelled(&job->job)) {
46
- return bytes;
101
- return;
102
- }
47
- }
103
-
48
-
104
- if (!job_should_pause(&job->job)) {
49
- if (offset >= bs_size) {
105
- job_do_yield(&job->job, -1);
50
- n1 = 0;
51
- } else {
52
- n1 = bs_size - offset;
106
- }
53
- }
107
-
54
-
108
- job_pause_point(&job->job);
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
56
-
57
- return n1;
109
-}
58
-}
110
-
59
-
111
void block_job_iostatus_reset(BlockJob *job)
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
61
uint64_t bytes, QEMUIOVector *qiov,
62
int flags)
112
{
63
{
113
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
64
BDRVQcow2State *s = bs->opaque;
114
diff --git a/job.c b/job.c
65
- int offset_in_cluster, n1;
115
index XXXXXXX..XXXXXXX 100644
66
+ int offset_in_cluster;
116
--- a/job.c
67
int ret;
117
+++ b/job.c
68
unsigned int cur_bytes; /* number of bytes in current iteration */
118
@@ -XXX,XX +XXX,XX @@ static bool job_started(Job *job)
69
uint64_t cluster_offset = 0;
119
return job->co;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
120
}
71
case QCOW2_CLUSTER_UNALLOCATED:
121
72
122
-bool job_should_pause(Job *job)
73
if (bs->backing) {
123
+static bool job_should_pause(Job *job)
74
- /* read from the base image */
124
{
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
125
return job->pause_count > 0;
76
- offset, cur_bytes);
126
}
77
- if (n1 > 0) {
127
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
78
- QEMUIOVector local_qiov;
128
*
79
-
129
* If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
130
* called explicitly. */
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
131
-void coroutine_fn job_do_yield(Job *job, uint64_t ns)
82
-
132
+static void coroutine_fn job_do_yield(Job *job, uint64_t ns)
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
133
{
84
- qemu_co_mutex_unlock(&s->lock);
134
job_lock();
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
135
if (ns != -1) {
86
- &local_qiov, 0);
136
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job)
87
- qemu_co_mutex_lock(&s->lock);
137
}
88
-
138
}
89
- qemu_iovec_destroy(&local_qiov);
139
90
-
140
+void job_yield(Job *job)
91
- if (ret < 0) {
141
+{
92
- goto fail;
142
+ assert(job->busy);
93
- }
143
+
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
144
+ /* Check cancellation *before* setting busy = false, too! */
95
+ qemu_co_mutex_unlock(&s->lock);
145
+ if (job_is_cancelled(job)) {
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
146
+ return;
97
+ &hd_qiov, 0);
147
+ }
98
+ qemu_co_mutex_lock(&s->lock);
148
+
99
+ if (ret < 0) {
149
+ if (!job_should_pause(job)) {
100
+ goto fail;
150
+ job_do_yield(job, -1);
101
}
151
+ }
102
} else {
152
+
103
/* Note: in this case, no need to wait */
153
+ job_pause_point(job);
154
+}
155
+
156
void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
157
{
158
assert(job->busy);
159
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/tests/test-blockjob-txn.c
162
+++ b/tests/test-blockjob-txn.c
163
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
164
if (s->use_timer) {
165
job_sleep_ns(&job->job, 0);
166
} else {
167
- block_job_yield(job);
168
+ job_yield(&job->job);
169
}
170
171
if (job_is_cancelled(&job->job)) {
172
--
104
--
173
2.13.6
105
2.13.6
174
106
175
107
diff view generated by jsdifflib
1
Commit 0ec4dfb8d changed block-job_pause/resume so that they return an
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
error if they don't do anything because the job is already
2
driver state that cannot be recreated with create options because it
3
paused/running. It forgot to update the documentation, so do that now.
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
4
8
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
11
---
9
qapi/block-core.json | 5 ++---
12
qapi/block-core.json | 4 ++++
10
1 file changed, 2 insertions(+), 3 deletions(-)
13
1 file changed, 4 insertions(+)
11
14
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
15
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
16
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
20
# does not support all kinds of operations, all kinds of children, nor
21
# all block drivers.
17
#
22
#
18
# This command returns immediately after marking the active background block
23
+# FIXME Removing children from a quorum node means introducing gaps in the
19
# operation for pausing. It is an error to call this command if no
24
+# child indices. This cannot be represented in the 'children' list of
20
-# operation is in progress. Pausing an already paused job has no cumulative
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
21
-# effect; a single block-job-resume command will resume the job.
26
+#
22
+# operation is in progress or if the job is already paused.
27
# Warning: The data in a new quorum child MUST be consistent with that of
23
#
28
# the rest of the array.
24
# The operation will pause as soon as possible. No event is emitted when
25
# the operation is actually paused. Cancelling a paused job automatically
26
@@ -XXX,XX +XXX,XX @@
27
#
28
# This command returns immediately after resuming a paused background block
29
# operation. It is an error to call this command if no operation is in
30
-# progress. Resuming an already running job is not an error.
31
+# progress or if the job is not paused.
32
#
33
# This command also clears the error status of the job.
34
#
29
#
35
--
30
--
36
2.13.6
31
2.13.6
37
32
38
33
diff view generated by jsdifflib
1
This adds a test case that tests the new job-* QMP commands with
1
From: Doug Gale <doug16k@gmail.com>
2
mirror and backup block jobs.
3
2
3
Add trace output for commands, errors, and undefined behavior.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
7
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
12
---
6
tests/qemu-iotests/219 | 209 +++++++++++++++++++++++++++++
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
7
tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++++++++++
14
hw/block/trace-events | 93 ++++++++++++++
8
tests/qemu-iotests/group | 1 +
15
2 files changed, 390 insertions(+), 52 deletions(-)
9
3 files changed, 537 insertions(+)
10
create mode 100755 tests/qemu-iotests/219
11
create mode 100644 tests/qemu-iotests/219.out
12
16
13
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
14
new file mode 100755
18
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX
19
--- a/hw/block/nvme.c
16
--- /dev/null
20
+++ b/hw/block/nvme.c
17
+++ b/tests/qemu-iotests/219
18
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@
19
+#!/usr/bin/env python
22
#include "qapi/visitor.h"
20
+#
23
#include "sysemu/block-backend.h"
21
+# Copyright (C) 2018 Red Hat, Inc.
24
22
+#
25
+#include "qemu/log.h"
23
+# This program is free software; you can redistribute it and/or modify
26
+#include "trace.h"
24
+# it under the terms of the GNU General Public License as published by
27
#include "nvme.h"
25
+# the Free Software Foundation; either version 2 of the License, or
28
26
+# (at your option) any later version.
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
27
+#
30
+ do { \
28
+# This program is distributed in the hope that it will be useful,
31
+ (trace_##trace)(__VA_ARGS__); \
29
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
30
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
31
+# GNU General Public License for more details.
34
+ } while (0)
32
+#
35
+
33
+# You should have received a copy of the GNU General Public License
36
static void nvme_process_sq(void *opaque);
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
37
35
+#
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
36
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
37
+#
40
{
38
+# Check using the job-* QMP commands with block jobs
41
if (cq->irq_enabled) {
39
+
42
if (msix_enabled(&(n->parent_obj))) {
40
+import iotests
43
+ trace_nvme_irq_msix(cq->vector);
41
+
44
msix_notify(&(n->parent_obj), cq->vector);
42
+iotests.verify_image_format(supported_fmts=['qcow2'])
45
} else {
43
+
46
+ trace_nvme_irq_pin();
44
+def pause_wait(vm, job_id):
47
pci_irq_pulse(&n->parent_obj);
45
+ with iotests.Timeout(3, "Timeout waiting for job to pause"):
48
}
46
+ while True:
49
+ } else {
47
+ result = vm.qmp('query-jobs')
50
+ trace_nvme_irq_masked();
48
+ for job in result['return']:
51
}
49
+ if job['id'] == job_id and job['status'] in ['paused', 'standby']:
52
}
50
+ return job
53
51
+
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
52
+# Test that block-job-pause/resume and job-pause/resume can be mixed
55
trans_len = MIN(len, trans_len);
53
+def test_pause_resume(vm):
56
int num_prps = (len >> n->page_bits) + 1;
54
+ for pause_cmd, pause_arg in [('block-job-pause', 'device'),
57
55
+ ('job-pause', 'id')]:
58
- if (!prp1) {
56
+ for resume_cmd, resume_arg in [('block-job-resume', 'device'),
59
+ if (unlikely(!prp1)) {
57
+ ('job-resume', 'id')]:
60
+ trace_nvme_err_invalid_prp();
58
+ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
61
return NVME_INVALID_FIELD | NVME_DNR;
59
+
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
60
+ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
61
+ pause_wait(vm, 'job0')
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
62
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
65
}
63
+ iotests.log(vm.qmp('query-jobs'))
66
len -= trans_len;
64
+
67
if (len) {
65
+ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
68
- if (!prp2) {
66
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
69
+ if (unlikely(!prp2)) {
67
+ iotests.log(vm.qmp('query-jobs'))
70
+ trace_nvme_err_invalid_prp2_missing();
68
+
71
goto unmap;
69
+def test_job_lifecycle(vm, job, job_args, has_ready=False):
72
}
70
+ iotests.log('')
73
if (len > n->page_size) {
71
+ iotests.log('')
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
72
+ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
73
+ (job,
76
74
+ job_args.get('auto-finalize', True),
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
75
+ job_args.get('auto-dismiss', True)))
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
76
+ iotests.log(vm.qmp(job, job_id='job0', **job_args))
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
77
+
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
78
+ # Depending on the storage, the first request may or may not have completed
81
goto unmap;
79
+ # yet, so filter out the progress. Later query-job calls don't need the
82
}
80
+ # filtering because the progress is made deterministic by the block job
83
81
+ # speed
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
82
+ result = vm.qmp('query-jobs')
85
prp_ent = le64_to_cpu(prp_list[i]);
83
+ for j in result['return']:
86
}
84
+ del j['current-progress']
87
85
+ iotests.log(result)
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
86
+
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
87
+ # undefined -> created -> running
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
88
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
91
goto unmap;
89
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
92
}
90
+
93
91
+ # RUNNING state:
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
92
+ # pause/resume should work, complete/finalize/dismiss should error out
95
i++;
93
+ iotests.log('')
96
}
94
+ iotests.log('Pause/resume in RUNNING')
97
} else {
95
+ test_pause_resume(vm)
98
- if (prp2 & (n->page_size - 1)) {
96
+
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
97
+ iotests.log(vm.qmp('job-complete', id='job0'))
100
+ trace_nvme_err_invalid_prp2_align(prp2);
98
+ iotests.log(vm.qmp('job-finalize', id='job0'))
101
goto unmap;
99
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
102
}
100
+
103
if (qsg->nsg) {
101
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
102
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
105
QEMUIOVector iov;
103
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
106
uint16_t status = NVME_SUCCESS;
104
+
107
105
+ # Let the job complete (or transition to READY if it supports that)
108
+ trace_nvme_dma_read(prp1, prp2);
106
+ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
109
+
107
+ if has_ready:
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
108
+ iotests.log('')
111
return NVME_INVALID_FIELD | NVME_DNR;
109
+ iotests.log('Waiting for READY state...')
112
}
110
+ vm.event_wait('BLOCK_JOB_READY')
113
if (qsg.nsg > 0) {
111
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
114
- if (dma_buf_read(ptr, len, &qsg)) {
112
+ iotests.log(vm.qmp('query-jobs'))
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
113
+
116
+ trace_nvme_err_invalid_dma();
114
+ # READY state:
117
status = NVME_INVALID_FIELD | NVME_DNR;
115
+ # pause/resume/complete should work, finalize/dismiss should error out
118
}
116
+ iotests.log('')
119
qemu_sglist_destroy(&qsg);
117
+ iotests.log('Pause/resume in READY')
120
} else {
118
+ test_pause_resume(vm)
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
119
+
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
120
+ iotests.log(vm.qmp('job-finalize', id='job0'))
123
+ trace_nvme_err_invalid_dma();
121
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
124
status = NVME_INVALID_FIELD | NVME_DNR;
122
+
125
}
123
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
126
qemu_iovec_destroy(&iov);
124
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
125
+
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
126
+ # Transition to WAITING
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
127
+ iotests.log(vm.qmp('job-complete', id='job0'))
130
128
+
131
- if (slba + nlb > ns->id_ns.nsze) {
129
+ # Move to WAITING and PENDING state
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
130
+ iotests.log('')
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
131
+ iotests.log('Waiting for PENDING state...')
134
return NVME_LBA_RANGE | NVME_DNR;
132
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
135
}
133
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
136
134
+
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
135
+ if not job_args.get('auto-finalize', True):
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
136
+ # PENDING state:
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
137
+ # finalize should work, pause/complete/dismiss should error out
140
138
+ iotests.log(vm.qmp('query-jobs'))
141
- if ((slba + nlb) > ns->id_ns.nsze) {
139
+
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
140
+ iotests.log(vm.qmp('job-pause', id='job0'))
143
+
141
+ iotests.log(vm.qmp('job-complete', id='job0'))
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
142
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
143
+
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
144
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
147
return NVME_LBA_RANGE | NVME_DNR;
145
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
148
}
146
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
149
147
+
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
148
+ # Transition to CONCLUDED
151
NvmeNamespace *ns;
149
+ iotests.log(vm.qmp('job-finalize', id='job0'))
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
150
+
153
151
+
154
- if (nsid == 0 || nsid > n->num_namespaces) {
152
+ # Move to CONCLUDED state
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
153
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
154
+
157
return NVME_INVALID_NSID | NVME_DNR;
155
+ if not job_args.get('auto-dismiss', True):
158
}
156
+ # CONCLUDED state:
159
157
+ # dismiss should work, pause/complete/finalize should error out
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
158
+ iotests.log(vm.qmp('query-jobs'))
161
case NVME_CMD_READ:
159
+
162
return nvme_rw(n, ns, cmd, req);
160
+ iotests.log(vm.qmp('job-pause', id='job0'))
163
default:
161
+ iotests.log(vm.qmp('job-complete', id='job0'))
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
162
+ iotests.log(vm.qmp('job-finalize', id='job0'))
165
return NVME_INVALID_OPCODE | NVME_DNR;
163
+
166
}
164
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
167
}
165
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
166
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
169
NvmeCQueue *cq;
167
+
170
uint16_t qid = le16_to_cpu(c->qid);
168
+ # Transition to NULL
171
169
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
172
- if (!qid || nvme_check_sqid(n, qid)) {
170
+
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
171
+ # Move to NULL state
174
+ trace_nvme_err_invalid_del_sq(qid);
172
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
175
return NVME_INVALID_QID | NVME_DNR;
173
+ iotests.log(vm.qmp('query-jobs'))
176
}
174
+
177
175
+
178
+ trace_nvme_del_sq(qid);
176
+with iotests.FilePath('disk.img') as disk_path, \
179
+
177
+ iotests.FilePath('copy.img') as copy_path, \
180
sq = n->sq[qid];
178
+ iotests.VM() as vm:
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
179
+
182
req = QTAILQ_FIRST(&sq->out_req_list);
180
+ img_size = '4M'
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
181
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
182
+ iotests.qemu_io('-c', 'write 0 %s' % (img_size),
185
uint64_t prp1 = le64_to_cpu(c->prp1);
183
+ '-f', iotests.imgfmt, disk_path)
186
184
+
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
185
+ iotests.log('Launching VM...')
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
186
+ vm.add_blockdev(vm.qmp_to_opts({
189
+
187
+ 'driver': iotests.imgfmt,
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
188
+ 'node-name': 'drive0-node',
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
189
+ 'file': {
192
return NVME_INVALID_CQID | NVME_DNR;
190
+ 'driver': 'file',
193
}
191
+ 'filename': disk_path,
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
192
+ },
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
193
+ }))
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
194
+ vm.launch()
197
return NVME_INVALID_QID | NVME_DNR;
195
+
198
}
196
+ # In order to keep things deterministic (especially progress in query-job,
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
197
+ # but related to this also automatic state transitions like job
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
198
+ # completion), but still get pause points often enough to avoid making this
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
199
+ # test very slow, it's important to have the right ratio between speed and
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
200
+ # buf_size.
203
}
201
+ #
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
202
+ # For backup, buf_size is hard-coded to the source image cluster size (64k),
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
203
+ # so we'll pick the same for mirror. The slice time, i.e. the granularity
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
204
+ # of the rate limiting is 100ms. With a speed of 256k per second, we can
207
return NVME_INVALID_FIELD | NVME_DNR;
205
+ # get four pause points per second. This gives us 250ms per iteration,
208
}
206
+ # which should be enough to stay deterministic.
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
207
+
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
208
+ test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
209
+ 'device': 'drive0-node',
212
return NVME_INVALID_FIELD | NVME_DNR;
210
+ 'target': copy_path,
213
}
211
+ 'sync': 'full',
214
sq = g_malloc0(sizeof(*sq));
212
+ 'speed': 262144,
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
213
+ 'buf_size': 65536,
216
NvmeCQueue *cq;
214
+ })
217
uint16_t qid = le16_to_cpu(c->qid);
215
+
218
216
+ for auto_finalize in [True, False]:
219
- if (!qid || nvme_check_cqid(n, qid)) {
217
+ for auto_dismiss in [True, False]:
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
218
+ test_job_lifecycle(vm, 'drive-backup', job_args={
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
219
+ 'device': 'drive0-node',
222
return NVME_INVALID_CQID | NVME_DNR;
220
+ 'target': copy_path,
223
}
221
+ 'sync': 'full',
224
222
+ 'speed': 262144,
225
cq = n->cq[qid];
223
+ 'auto-finalize': auto_finalize,
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
224
+ 'auto-dismiss': auto_dismiss,
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
225
+ })
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
226
+
229
return NVME_INVALID_QUEUE_DEL;
227
+ vm.shutdown()
230
}
228
diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out
231
+ trace_nvme_del_cq(qid);
229
new file mode 100644
232
nvme_free_cq(cq, n);
230
index XXXXXXX..XXXXXXX
233
return NVME_SUCCESS;
231
--- /dev/null
234
}
232
+++ b/tests/qemu-iotests/219.out
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
233
@@ -XXX,XX +XXX,XX @@
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
234
+Launching VM...
237
uint64_t prp1 = le64_to_cpu(c->prp1);
235
+
238
236
+
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
237
+Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
238
+{u'return': {}}
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
239
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
242
+
240
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
241
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
242
+
245
return NVME_INVALID_CQID | NVME_DNR;
243
+Pause/resume in RUNNING
246
}
244
+=== Testing block-job-pause/block-job-resume ===
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
245
+{u'return': {}}
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
246
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
247
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
248
+{u'return': {}}
251
}
249
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
252
- if (!prp1) {
250
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
253
+ if (unlikely(!prp1)) {
251
+=== Testing block-job-pause/job-resume ===
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
252
+{u'return': {}}
255
return NVME_INVALID_FIELD | NVME_DNR;
253
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
256
}
254
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
257
- if (vector > n->num_queues) {
255
+{u'return': {}}
258
+ if (unlikely(vector > n->num_queues)) {
256
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
257
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
258
+=== Testing job-pause/block-job-resume ===
261
}
259
+{u'return': {}}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
260
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
261
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
262
+{u'return': {}}
265
return NVME_INVALID_FIELD | NVME_DNR;
263
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
266
}
264
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
267
265
+=== Testing job-pause/job-resume ===
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
266
+{u'return': {}}
269
uint64_t prp1 = le64_to_cpu(c->prp1);
267
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
270
uint64_t prp2 = le64_to_cpu(c->prp2);
268
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
271
269
+{u'return': {}}
272
+ trace_nvme_identify_ctrl();
270
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
273
+
271
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
272
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
275
prp1, prp2);
273
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
276
}
274
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
275
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
278
uint64_t prp1 = le64_to_cpu(c->prp1);
276
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
279
uint64_t prp2 = le64_to_cpu(c->prp2);
277
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
280
278
+{u'return': {}}
281
- if (nsid == 0 || nsid > n->num_namespaces) {
279
+
282
+ trace_nvme_identify_ns(nsid);
280
+Waiting for READY state...
283
+
281
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
282
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
283
+
286
return NVME_INVALID_NSID | NVME_DNR;
284
+Pause/resume in READY
287
}
285
+=== Testing block-job-pause/block-job-resume ===
288
286
+{u'return': {}}
289
ns = &n->namespaces[nsid - 1];
287
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
290
+
288
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
289
+{u'return': {}}
292
prp1, prp2);
290
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
293
}
291
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
292
+=== Testing block-job-pause/job-resume ===
295
uint16_t ret;
293
+{u'return': {}}
296
int i, j = 0;
294
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
297
295
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
298
+ trace_nvme_identify_nslist(min_nsid);
296
+{u'return': {}}
299
+
297
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
300
list = g_malloc0(data_len);
298
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
301
for (i = 0; i < n->num_namespaces; i++) {
299
+=== Testing job-pause/block-job-resume ===
302
if (i < min_nsid) {
300
+{u'return': {}}
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
301
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
304
case 0x02:
302
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
305
return nvme_identify_nslist(n, c);
303
+{u'return': {}}
306
default:
304
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
305
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
308
return NVME_INVALID_FIELD | NVME_DNR;
306
+=== Testing job-pause/job-resume ===
309
}
307
+{u'return': {}}
310
}
308
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
309
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
312
switch (dw10) {
310
+{u'return': {}}
313
case NVME_VOLATILE_WRITE_CACHE:
311
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
314
result = blk_enable_write_cache(n->conf.blk);
312
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
313
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
316
break;
314
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
317
case NVME_NUMBER_OF_QUEUES:
315
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
316
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
319
+ trace_nvme_getfeat_numq(result);
317
+{u'return': {}}
320
break;
318
+
321
default:
319
+Waiting for PENDING state...
322
+ trace_nvme_err_invalid_getfeat(dw10);
320
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
323
return NVME_INVALID_FIELD | NVME_DNR;
321
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
324
}
322
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
325
323
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
324
+{u'return': []}
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
325
+
328
break;
326
+
329
case NVME_NUMBER_OF_QUEUES:
327
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True)
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
328
+{u'return': {}}
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
329
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
332
+ n->num_queues - 1, n->num_queues - 1);
330
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
333
req->cqe.result =
331
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
332
+
335
break;
333
+Pause/resume in RUNNING
336
default:
334
+=== Testing block-job-pause/block-job-resume ===
337
+ trace_nvme_err_invalid_setfeat(dw10);
335
+{u'return': {}}
338
return NVME_INVALID_FIELD | NVME_DNR;
336
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
339
}
337
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
340
return NVME_SUCCESS;
338
+{u'return': {}}
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
339
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
342
case NVME_ADM_CMD_GET_FEATURES:
340
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
343
return nvme_get_feature(n, cmd, req);
341
+=== Testing block-job-pause/job-resume ===
344
default:
342
+{u'return': {}}
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
343
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
346
return NVME_INVALID_OPCODE | NVME_DNR;
344
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
347
}
345
+{u'return': {}}
348
}
346
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
347
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
348
+=== Testing job-pause/block-job-resume ===
351
uint32_t page_size = 1 << page_bits;
349
+{u'return': {}}
352
350
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
351
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
352
+{u'return': {}}
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
353
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
354
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
355
+=== Testing job-pause/job-resume ===
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
356
+{u'return': {}}
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
357
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
358
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
359
+{u'return': {}}
362
+ if (unlikely(n->cq[0])) {
360
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
363
+ trace_nvme_err_startfail_cq();
361
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
364
+ return -1;
362
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
365
+ }
363
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
366
+ if (unlikely(n->sq[0])) {
364
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
367
+ trace_nvme_err_startfail_sq();
365
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
368
+ return -1;
366
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
369
+ }
367
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
370
+ if (unlikely(!n->bar.asq)) {
368
+{u'return': {}}
371
+ trace_nvme_err_startfail_nbarasq();
369
+
372
+ return -1;
370
+Waiting for PENDING state...
373
+ }
371
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
374
+ if (unlikely(!n->bar.acq)) {
372
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
375
+ trace_nvme_err_startfail_nbaracq();
373
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
376
+ return -1;
374
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
377
+ }
375
+{u'return': []}
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
376
+
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
377
+
380
+ return -1;
378
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False)
381
+ }
379
+{u'return': {}}
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
380
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
381
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
384
+ return -1;
382
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
385
+ }
383
+
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
384
+Pause/resume in RUNNING
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
385
+=== Testing block-job-pause/block-job-resume ===
388
+ trace_nvme_err_startfail_page_too_small(
386
+{u'return': {}}
389
+ NVME_CC_MPS(n->bar.cc),
387
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
390
+ NVME_CAP_MPSMIN(n->bar.cap));
388
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
391
+ return -1;
389
+{u'return': {}}
392
+ }
390
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
391
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
392
+=== Testing block-job-pause/job-resume ===
395
+ trace_nvme_err_startfail_page_too_large(
393
+{u'return': {}}
396
+ NVME_CC_MPS(n->bar.cc),
394
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
397
+ NVME_CAP_MPSMAX(n->bar.cap));
395
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
398
+ return -1;
396
+{u'return': {}}
399
+ }
397
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
398
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
399
+=== Testing job-pause/block-job-resume ===
402
+ trace_nvme_err_startfail_cqent_too_small(
400
+{u'return': {}}
403
+ NVME_CC_IOCQES(n->bar.cc),
401
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
402
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
405
+ return -1;
403
+{u'return': {}}
406
+ }
404
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
405
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
406
+=== Testing job-pause/job-resume ===
409
+ trace_nvme_err_startfail_cqent_too_large(
407
+{u'return': {}}
410
+ NVME_CC_IOCQES(n->bar.cc),
408
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
409
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
412
+ return -1;
410
+{u'return': {}}
413
+ }
411
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
412
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
413
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
416
+ trace_nvme_err_startfail_sqent_too_small(
414
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
417
+ NVME_CC_IOSQES(n->bar.cc),
415
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
416
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
419
+ return -1;
417
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
420
+ }
418
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
419
+{u'return': {}}
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
420
+
423
+ trace_nvme_err_startfail_sqent_too_large(
421
+Waiting for PENDING state...
424
+ NVME_CC_IOSQES(n->bar.cc),
422
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
423
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
426
+ return -1;
424
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
427
+ }
425
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
426
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
429
+ trace_nvme_err_startfail_asqent_sz_zero();
427
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
430
+ return -1;
428
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
431
+ }
429
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
430
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
433
+ trace_nvme_err_startfail_acqent_sz_zero();
431
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
434
return -1;
432
+{u'return': {}}
435
}
433
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
436
434
+{u'return': []}
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
435
+
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
436
+
439
unsigned size)
437
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True)
440
{
438
+{u'return': {}}
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
439
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
440
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
443
+ "MMIO write not 32-bit aligned,"
441
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
444
+ " offset=0x%"PRIx64"", offset);
442
+
445
+ /* should be ignored, fall through for now */
443
+Pause/resume in RUNNING
446
+ }
444
+=== Testing block-job-pause/block-job-resume ===
447
+
445
+{u'return': {}}
448
+ if (unlikely(size < sizeof(uint32_t))) {
446
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
447
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
450
+ "MMIO write smaller than 32-bits,"
448
+{u'return': {}}
451
+ " offset=0x%"PRIx64", size=%u",
449
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
452
+ offset, size);
450
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
453
+ /* should be ignored, fall through for now */
451
+=== Testing block-job-pause/job-resume ===
454
+ }
452
+{u'return': {}}
455
+
453
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
456
switch (offset) {
454
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
457
- case 0xc:
455
+{u'return': {}}
458
+ case 0xc: /* INTMS */
456
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
457
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
458
+=== Testing job-pause/block-job-resume ===
461
+ "undefined access to interrupt mask set"
459
+{u'return': {}}
462
+ " when MSI-X is enabled");
460
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
463
+ /* should be ignored, fall through for now */
461
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
464
+ }
462
+{u'return': {}}
465
n->bar.intms |= data & 0xffffffff;
463
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
466
n->bar.intmc = n->bar.intms;
464
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
465
+=== Testing job-pause/job-resume ===
468
+ n->bar.intmc);
466
+{u'return': {}}
469
break;
467
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
470
- case 0x10:
468
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
471
+ case 0x10: /* INTMC */
469
+{u'return': {}}
472
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
470
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
473
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
471
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
474
+ "undefined access to interrupt mask clr"
472
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
475
+ " when MSI-X is enabled");
473
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
476
+ /* should be ignored, fall through for now */
474
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
477
+ }
475
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
478
n->bar.intms &= ~(data & 0xffffffff);
476
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
479
n->bar.intmc = n->bar.intms;
477
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
478
+{u'return': {}}
481
+ n->bar.intmc);
479
+
482
break;
480
+Waiting for PENDING state...
483
- case 0x14:
481
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
484
+ case 0x14: /* CC */
482
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
483
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
486
/* Windows first sends data, then sends enable bit */
484
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
485
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
486
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
487
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
490
488
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
489
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
492
n->bar.cc = data;
490
+{u'return': {}}
493
- if (nvme_start_ctrl(n)) {
491
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
494
+ if (unlikely(nvme_start_ctrl(n))) {
492
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
495
+ trace_nvme_err_startfail();
493
+{u'return': []}
496
n->bar.csts = NVME_CSTS_FAILED;
494
+
497
} else {
495
+
498
+ trace_nvme_mmio_start_success();
496
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False)
499
n->bar.csts = NVME_CSTS_READY;
497
+{u'return': {}}
500
}
498
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
501
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
499
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
502
+ trace_nvme_mmio_stopped();
500
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
503
nvme_clear_ctrl(n);
501
+
504
n->bar.csts &= ~NVME_CSTS_READY;
502
+Pause/resume in RUNNING
505
}
503
+=== Testing block-job-pause/block-job-resume ===
506
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
504
+{u'return': {}}
507
- nvme_clear_ctrl(n);
505
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
508
- n->bar.cc = data;
506
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
509
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
507
+{u'return': {}}
510
+ trace_nvme_mmio_shutdown_set();
508
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
511
+ nvme_clear_ctrl(n);
509
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
512
+ n->bar.cc = data;
510
+=== Testing block-job-pause/job-resume ===
513
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
511
+{u'return': {}}
514
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
512
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
515
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
513
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
516
- n->bar.cc = data;
514
+{u'return': {}}
517
+ trace_nvme_mmio_shutdown_cleared();
515
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
518
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
516
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
519
+ n->bar.cc = data;
517
+=== Testing job-pause/block-job-resume ===
520
+ }
518
+{u'return': {}}
521
+ break;
519
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
522
+ case 0x1C: /* CSTS */
520
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
523
+ if (data & (1 << 4)) {
521
+{u'return': {}}
524
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
522
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
525
+ "attempted to W1C CSTS.NSSRO"
523
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
526
+ " but CAP.NSSRS is zero (not supported)");
524
+=== Testing job-pause/job-resume ===
527
+ } else if (data != 0) {
525
+{u'return': {}}
528
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
526
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
529
+ "attempted to set a read only bit"
527
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
530
+ " of controller status");
528
+{u'return': {}}
531
+ }
529
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
532
+ break;
530
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
533
+ case 0x20: /* NSSR */
531
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
534
+ if (data == 0x4E564D65) {
532
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
535
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
533
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
536
+ } else {
534
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
537
+ /* The spec says that writes of other values have no effect */
535
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
538
+ return;
536
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
539
}
537
+{u'return': {}}
540
break;
538
+
541
- case 0x24:
539
+Waiting for PENDING state...
542
+ case 0x24: /* AQA */
540
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
543
n->bar.aqa = data & 0xffffffff;
541
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
544
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
542
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
545
break;
543
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
546
- case 0x28:
544
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
547
+ case 0x28: /* ASQ */
545
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
548
n->bar.asq = data;
546
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
549
+ trace_nvme_mmio_asqaddr(data);
547
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
550
break;
548
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
551
- case 0x2c:
549
+{u'return': {}}
552
+ case 0x2c: /* ASQ hi */
550
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
553
n->bar.asq |= data << 32;
551
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
554
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
552
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
555
break;
553
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
556
- case 0x30:
554
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
557
+ case 0x30: /* ACQ */
555
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
558
+ trace_nvme_mmio_acqaddr(data);
556
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
559
n->bar.acq = data;
557
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
560
break;
558
+{u'return': {}}
561
- case 0x34:
559
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
562
+ case 0x34: /* ACQ hi */
560
+{u'return': []}
563
n->bar.acq |= data << 32;
561
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
564
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
565
break;
566
+ case 0x38: /* CMBLOC */
567
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
568
+ "invalid write to reserved CMBLOC"
569
+ " when CMBSZ is zero, ignored");
570
+ return;
571
+ case 0x3C: /* CMBSZ */
572
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
573
+ "invalid write to read only CMBSZ, ignored");
574
+ return;
575
default:
576
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
577
+ "invalid MMIO write,"
578
+ " offset=0x%"PRIx64", data=%"PRIx64"",
579
+ offset, data);
580
break;
581
}
582
}
583
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
584
uint8_t *ptr = (uint8_t *)&n->bar;
585
uint64_t val = 0;
586
587
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
588
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
589
+ "MMIO read not 32-bit aligned,"
590
+ " offset=0x%"PRIx64"", addr);
591
+ /* should RAZ, fall through for now */
592
+ } else if (unlikely(size < sizeof(uint32_t))) {
593
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
594
+ "MMIO read smaller than 32-bits,"
595
+ " offset=0x%"PRIx64"", addr);
596
+ /* should RAZ, fall through for now */
597
+ }
598
+
599
if (addr < sizeof(n->bar)) {
600
memcpy(&val, ptr + addr, size);
601
+ } else {
602
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
603
+ "MMIO read beyond last register,"
604
+ " offset=0x%"PRIx64", returning 0", addr);
605
}
606
+
607
return val;
608
}
609
610
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
611
{
612
uint32_t qid;
613
614
- if (addr & ((1 << 2) - 1)) {
615
+ if (unlikely(addr & ((1 << 2) - 1))) {
616
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
617
+ "doorbell write not 32-bit aligned,"
618
+ " offset=0x%"PRIx64", ignoring", addr);
619
return;
620
}
621
622
if (((addr - 0x1000) >> 2) & 1) {
623
+ /* Completion queue doorbell write */
624
+
625
uint16_t new_head = val & 0xffff;
626
int start_sqs;
627
NvmeCQueue *cq;
628
629
qid = (addr - (0x1000 + (1 << 2))) >> 3;
630
- if (nvme_check_cqid(n, qid)) {
631
+ if (unlikely(nvme_check_cqid(n, qid))) {
632
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
633
+ "completion queue doorbell write"
634
+ " for nonexistent queue,"
635
+ " sqid=%"PRIu32", ignoring", qid);
636
return;
637
}
638
639
cq = n->cq[qid];
640
- if (new_head >= cq->size) {
641
+ if (unlikely(new_head >= cq->size)) {
642
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
643
+ "completion queue doorbell write value"
644
+ " beyond queue size, sqid=%"PRIu32","
645
+ " new_head=%"PRIu16", ignoring",
646
+ qid, new_head);
647
return;
648
}
649
650
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
651
nvme_isr_notify(n, cq);
652
}
653
} else {
654
+ /* Submission queue doorbell write */
655
+
656
uint16_t new_tail = val & 0xffff;
657
NvmeSQueue *sq;
658
659
qid = (addr - 0x1000) >> 3;
660
- if (nvme_check_sqid(n, qid)) {
661
+ if (unlikely(nvme_check_sqid(n, qid))) {
662
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
663
+ "submission queue doorbell write"
664
+ " for nonexistent queue,"
665
+ " sqid=%"PRIu32", ignoring", qid);
666
return;
667
}
668
669
sq = n->sq[qid];
670
- if (new_tail >= sq->size) {
671
+ if (unlikely(new_tail >= sq->size)) {
672
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
673
+ "submission queue doorbell write value"
674
+ " beyond queue size, sqid=%"PRIu32","
675
+ " new_tail=%"PRIu16", ignoring",
676
+ qid, new_tail);
677
return;
678
}
679
680
diff --git a/hw/block/trace-events b/hw/block/trace-events
562
index XXXXXXX..XXXXXXX 100644
681
index XXXXXXX..XXXXXXX 100644
563
--- a/tests/qemu-iotests/group
682
--- a/hw/block/trace-events
564
+++ b/tests/qemu-iotests/group
683
+++ b/hw/block/trace-events
565
@@ -XXX,XX +XXX,XX @@
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
566
215 rw auto quick
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
567
216 rw auto quick
686
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
568
218 rw auto quick
687
569
+219 rw auto
688
+# hw/block/nvme.c
689
+# nvme traces for successful events
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
691
+nvme_irq_pin(void) "pulsing IRQ pin"
692
+nvme_irq_masked(void) "IRQ is masked"
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
694
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
695
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
696
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
699
+nvme_identify_ctrl(void) "identify controller"
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
704
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
711
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
712
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
570
--
784
--
571
2.13.6
785
2.13.6
572
786
573
787
diff view generated by jsdifflib
1
BlockJob.driver is redundant with Job.driver and only used in very few
1
From: Fam Zheng <famz@redhat.com>
2
places any more. Remove it.
3
2
3
Management tools create overlays of running guests with qemu-img:
4
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
6
7
but this doesn't work anymore due to image locking:
8
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
10
Is another process using the image?
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
15
Signed-off-by: Fam Zheng <famz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
18
---
6
include/block/blockjob.h | 3 ---
19
block.c | 3 ++-
7
blockjob.c | 17 ++++++++++-------
20
1 file changed, 2 insertions(+), 1 deletion(-)
8
2 files changed, 10 insertions(+), 10 deletions(-)
9
21
10
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
22
diff --git a/block.c b/block.c
11
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/blockjob.h
24
--- a/block.c
13
+++ b/include/block/blockjob.h
25
+++ b/block.c
14
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
15
/** Data belonging to the generic Job infrastructure */
27
back_flags = flags;
16
Job job;
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
17
29
18
- /** The job type, including the job vtable. */
30
+ backing_options = qdict_new();
19
- const BlockJobDriver *driver;
31
if (backing_fmt) {
20
-
32
- backing_options = qdict_new();
21
/** The block device on which the job is operating. */
33
qdict_put_str(backing_options, "driver", backing_fmt);
22
BlockBackend *blk;
34
}
23
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
24
diff --git a/blockjob.c b/blockjob.c
36
25
index XXXXXXX..XXXXXXX 100644
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
26
--- a/blockjob.c
38
&local_err);
27
+++ b/blockjob.c
28
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
29
void *opaque)
30
{
31
BlockJob *job = opaque;
32
+ const JobDriver *drv = job->job.driver;
33
+ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver);
34
35
job->job.aio_context = new_context;
36
- if (job->driver->attached_aio_context) {
37
- job->driver->attached_aio_context(job, new_context);
38
+ if (bjdrv->attached_aio_context) {
39
+ bjdrv->attached_aio_context(job, new_context);
40
}
41
42
job_resume(&job->job);
43
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
44
void block_job_drain(Job *job)
45
{
46
BlockJob *bjob = container_of(job, BlockJob, job);
47
+ const JobDriver *drv = job->driver;
48
+ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver);
49
50
blk_drain(bjob->blk);
51
- if (bjob->driver->drain) {
52
- bjob->driver->drain(bjob);
53
+ if (bjdrv->drain) {
54
+ bjdrv->drain(bjob);
55
}
56
}
57
58
@@ -XXX,XX +XXX,XX @@ bool block_job_is_internal(BlockJob *job)
59
60
const BlockJobDriver *block_job_driver(BlockJob *job)
61
{
62
- return job->driver;
63
+ return container_of(job->job.driver, BlockJobDriver, job_driver);
64
}
65
66
/* Assumes the job_mutex is held */
67
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
68
assert(job->job.driver->user_resume == &block_job_user_resume);
69
assert(job->job.driver->drain == &block_job_drain);
70
71
- job->driver = driver;
72
- job->blk = blk;
73
+ job->blk = blk;
74
75
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
76
job->finalize_completed_notifier.notify = block_job_event_completed;
77
--
39
--
78
2.13.6
40
2.13.6
79
41
80
42
diff view generated by jsdifflib
1
This moves block_job_dismiss() to the Job layer.
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
9
---
6
include/block/blockjob.h | 9 ---------
10
blockdev.c | 11 -----------
7
include/qemu/job.h | 7 ++++++-
11
qemu-doc.texi | 6 ------
8
blockdev.c | 10 ++++++----
12
2 files changed, 17 deletions(-)
9
blockjob.c | 13 -------------
10
job.c | 15 ++++++++++++++-
11
tests/test-blockjob.c | 4 ++--
12
6 files changed, 28 insertions(+), 30 deletions(-)
13
13
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
17
+++ b/include/block/blockjob.h
18
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
19
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
20
21
/**
22
- * block_job_dismiss:
23
- * @job: The job to be dismissed.
24
- * @errp: Error object.
25
- *
26
- * Remove a concluded job from the query list.
27
- */
28
-void block_job_dismiss(BlockJob **job, Error **errp);
29
-
30
-/**
31
* block_job_progress_update:
32
* @job: The job that has made progress
33
* @done: How much progress the job made
34
diff --git a/include/qemu/job.h b/include/qemu/job.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/qemu/job.h
37
+++ b/include/qemu/job.h
38
@@ -XXX,XX +XXX,XX @@ int job_complete_sync(Job *job, Error **errp);
39
*/
40
void job_finalize(Job *job, Error **errp);
41
42
+/**
43
+ * Remove the concluded @job from the query list and resets the passed pointer
44
+ * to %NULL. Returns an error if the job is not actually concluded.
45
+ */
46
+void job_dismiss(Job **job, Error **errp);
47
+
48
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
49
50
/**
51
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
52
53
/* TODO To be removed from the public interface */
54
void job_state_transition(Job *job, JobStatus s1);
55
-void job_do_dismiss(Job *job);
56
57
#endif
58
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
59
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
60
--- a/blockdev.c
16
--- a/blockdev.c
61
+++ b/blockdev.c
17
+++ b/blockdev.c
62
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
63
void qmp_block_job_dismiss(const char *id, Error **errp)
19
.type = QEMU_OPT_STRING,
64
{
20
.help = "chs translation (auto, lba, none)",
65
AioContext *aio_context;
21
},{
66
- BlockJob *job = find_block_job(id, &aio_context, errp);
22
- .name = "boot",
67
+ BlockJob *bjob = find_block_job(id, &aio_context, errp);
23
- .type = QEMU_OPT_BOOL,
68
+ Job *job;
24
- .help = "(deprecated, ignored)",
69
25
- },{
70
- if (!job) {
26
.name = "addr",
71
+ if (!bjob) {
27
.type = QEMU_OPT_STRING,
72
return;
28
.help = "pci address (virtio only)",
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
30
goto fail;
73
}
31
}
74
32
75
- trace_qmp_block_job_dismiss(job);
33
- /* Deprecated option boot=[on|off] */
76
- block_job_dismiss(&job, errp);
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
77
+ trace_qmp_block_job_dismiss(bjob);
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
78
+ job = &bjob->job;
36
- "ignored. Future versions will reject this parameter. Please "
79
+ job_dismiss(&job, errp);
37
- "update your scripts.\n");
80
aio_context_release(aio_context);
81
}
82
83
diff --git a/blockjob.c b/blockjob.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/blockjob.c
86
+++ b/blockjob.c
87
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
88
return ratelimit_calculate_delay(&job->limit, n);
89
}
90
91
-void block_job_dismiss(BlockJob **jobptr, Error **errp)
92
-{
93
- BlockJob *job = *jobptr;
94
- /* similarly to _complete, this is QMP-interface only. */
95
- assert(job->job.id);
96
- if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
97
- return;
98
- }
38
- }
99
-
39
-
100
- job_do_dismiss(&job->job);
40
/* Other deprecated options */
101
- *jobptr = NULL;
41
if (!qtest_enabled()) {
102
-}
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
44
index XXXXXXX..XXXXXXX 100644
45
--- a/qemu-doc.texi
46
+++ b/qemu-doc.texi
47
@@ -XXX,XX +XXX,XX @@ deprecated.
48
49
@section System emulator command line arguments
50
51
-@subsection -drive boot=on|off (since 1.3.0)
103
-
52
-
104
void block_job_progress_update(BlockJob *job, uint64_t done)
53
-The ``boot=on|off'' option to the ``-drive'' argument is
105
{
54
-ignored. Applications should use the ``bootindex=N'' parameter
106
job->offset += done;
55
-to set an absolute ordering between devices instead.
107
diff --git a/job.c b/job.c
56
-
108
index XXXXXXX..XXXXXXX 100644
57
@subsection -tdf (since 1.3.0)
109
--- a/job.c
58
110
+++ b/job.c
59
The ``-tdf'' argument is ignored. The behaviour implemented
111
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
112
job_resume(job);
113
}
114
115
-void job_do_dismiss(Job *job)
116
+static void job_do_dismiss(Job *job)
117
{
118
assert(job);
119
job->busy = false;
120
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
121
job_unref(job);
122
}
123
124
+void job_dismiss(Job **jobptr, Error **errp)
125
+{
126
+ Job *job = *jobptr;
127
+ /* similarly to _complete, this is QMP-interface only. */
128
+ assert(job->id);
129
+ if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) {
130
+ return;
131
+ }
132
+
133
+ job_do_dismiss(job);
134
+ *jobptr = NULL;
135
+}
136
+
137
void job_early_fail(Job *job)
138
{
139
assert(job->status == JOB_STATUS_CREATED);
140
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/tests/test-blockjob.c
143
+++ b/tests/test-blockjob.c
144
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
145
146
job_cancel_sync(&job->job);
147
if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
148
- BlockJob *dummy = job;
149
- block_job_dismiss(&dummy, &error_abort);
150
+ Job *dummy = &job->job;
151
+ job_dismiss(&dummy, &error_abort);
152
}
153
assert(job->job.status == JOB_STATUS_NULL);
154
job_unref(&job->job);
155
--
60
--
156
2.13.6
61
2.13.6
157
62
158
63
diff view generated by jsdifflib
1
Now that we cancel all jobs and not only block jobs on shutdown, doing
1
From: Thomas Huth <thuth@redhat.com>
2
that in bdrv_close_all() isn't really appropriate any more. Move the
2
3
job_cancel_sync_all() call to the callers, and only assert that there
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
are no job running in bdrv_close_all().
4
complained that we should keep it, so let's remove this legacy option
5
5
now to simplify the code quite a bit.
6
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
11
---
8
block.c | 4 +---
12
vl.c | 86 ++-------------------------------------------------------
9
qemu-nbd.c | 8 +++++++-
13
qemu-doc.texi | 8 ------
10
vl.c | 1 +
14
qemu-options.hx | 19 ++-----------
11
3 files changed, 9 insertions(+), 4 deletions(-)
15
3 files changed, 4 insertions(+), 109 deletions(-)
12
16
13
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
16
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
18
19
void bdrv_close_all(void)
20
{
21
- /* TODO We do want to cancel all jobs instead of just block jobs on
22
- * shutdown, but bdrv_close_all() isn't the right place any more. */
23
- job_cancel_sync_all();
24
+ assert(job_next(NULL) == NULL);
25
nbd_export_close_all();
26
27
/* Drop references from requests still in flight, such as canceled block
28
diff --git a/qemu-nbd.c b/qemu-nbd.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/qemu-nbd.c
31
+++ b/qemu-nbd.c
32
@@ -XXX,XX +XXX,XX @@ static const char *socket_activation_validate_opts(const char *device,
33
return NULL;
34
}
35
36
+static void qemu_nbd_shutdown(void)
37
+{
38
+ job_cancel_sync_all();
39
+ bdrv_close_all();
40
+}
41
+
42
int main(int argc, char **argv)
43
{
44
BlockBackend *blk;
45
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
46
exit(EXIT_FAILURE);
47
}
48
bdrv_init();
49
- atexit(bdrv_close_all);
50
+ atexit(qemu_nbd_shutdown);
51
52
srcpath = argv[optind];
53
if (imageOpts) {
54
diff --git a/vl.c b/vl.c
17
diff --git a/vl.c b/vl.c
55
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
56
--- a/vl.c
19
--- a/vl.c
57
+++ b/vl.c
20
+++ b/vl.c
58
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
59
/* No more vcpu or device emulation activity beyond this point */
22
const char *boot_order = NULL;
60
vm_shutdown();
23
const char *boot_once = NULL;
61
24
DisplayState *ds;
62
+ job_cancel_sync_all();
25
- int cyls, heads, secs, translation;
63
bdrv_close_all();
26
QemuOpts *opts, *machine_opts;
64
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
65
res_free();
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
29
QemuOptsList *olist;
30
int optind;
31
const char *optarg;
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
33
34
cpu_model = NULL;
35
snapshot = 0;
36
- cyls = heads = secs = 0;
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
38
39
nb_nics = 0;
40
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
144
index XXXXXXX..XXXXXXX 100644
145
--- a/qemu-doc.texi
146
+++ b/qemu-doc.texi
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
148
``-object filter-dump'' argument which works in combination
149
with the modern ``-netdev`` backends instead.
150
151
-@subsection -hdachs (since 2.10.0)
152
-
153
-The ``-hdachs'' argument is now a synonym for setting
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
155
-on the ``ide-hd'' device using the ``-device'' argument.
156
-The new syntax allows different settings to be provided
157
-per disk.
158
-
159
@subsection -usbdevice (since 2.10.0)
160
161
The ``-usbdevice DEV'' argument is now a synonym for setting
162
diff --git a/qemu-options.hx b/qemu-options.hx
163
index XXXXXXX..XXXXXXX 100644
164
--- a/qemu-options.hx
165
+++ b/qemu-options.hx
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
167
@item media=@var{media}
168
This option defines the type of the media: disk or cdrom.
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
170
-These options have the same definition as they have in @option{-hdachs}.
171
-These parameters are deprecated, use the corresponding parameters
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
173
+lba). These parameters are deprecated, use the corresponding parameters
174
of @code{-device} instead.
175
@item snapshot=@var{snapshot}
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
179
ETEXI
180
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
182
- "-hdachs c,h,s[,t]\n" \
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
185
- QEMU_ARCH_ALL)
186
-STEXI
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
188
-@findex -hdachs
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
192
-all those parameters. This option is deprecated, please use
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
194
-ETEXI
195
-
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
66
--
199
--
67
2.13.6
200
2.13.6
68
201
69
202
diff view generated by jsdifflib
1
From: Peter Maydell <peter.maydell@linaro.org>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
In commit 8b9ad56e9cbfd852a, we removed the code that could result
3
Looks like we forgot to announce the deprecation of these options in
4
in our getting to sd_prealloc()'s out_with_err_set label with a
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
NULL blk pointer. That makes the NULL check in the error-handling
6
path unnecessary, and Coverity gripes about it (CID 1390636).
7
Delete the redundant check.
8
5
9
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
10
---
12
block/sheepdog.c | 4 +---
11
qemu-doc.texi | 15 +++++++++++++++
13
1 file changed, 1 insertion(+), 3 deletions(-)
12
1 file changed, 15 insertions(+)
14
13
15
diff --git a/block/sheepdog.c b/block/sheepdog.c
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
16
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
17
--- a/block/sheepdog.c
16
--- a/qemu-doc.texi
18
+++ b/block/sheepdog.c
17
+++ b/qemu-doc.texi
19
@@ -XXX,XX +XXX,XX @@ out:
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
20
error_setg_errno(errp, -ret, "Can't pre-allocate");
19
The ``-drive if=scsi'' argument is replaced by the the
21
}
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
22
out_with_err_set:
21
23
- if (blk) {
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
24
- blk_unref(blk);
23
+
25
- }
24
+The drive geometry arguments are replaced by the the geometry arguments
26
+ blk_unref(blk);
25
+that can be specified with the ``-device'' parameter.
27
g_free(buf);
26
+
28
27
+@subsection -drive serial=... (since 2.10.0)
29
return ret;
28
+
29
+The drive serial argument is replaced by the the serial argument
30
+that can be specified with the ``-device'' parameter.
31
+
32
+@subsection -drive addr=... (since 2.10.0)
33
+
34
+The drive addr argument is replaced by the the addr argument
35
+that can be specified with the ``-device'' parameter.
36
+
37
@subsection -net dump (since 2.10.0)
38
39
The ``--net dump'' argument is now replaced with the
30
--
40
--
31
2.13.6
41
2.13.6
32
42
33
43
diff view generated by jsdifflib
1
This doesn't actually move any transaction code to Job yet, but it
1
From: Fam Zheng <famz@redhat.com>
2
renames the type for transactions from BlockJobTxn to JobTxn and makes
3
them contain Jobs rather than BlockJobs
4
2
3
Signed-off-by: Fam Zheng <famz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
5
---
8
include/block/block_int.h | 2 +-
6
include/block/block_int.h | 1 -
9
include/block/blockjob.h | 11 ++++----
7
block/io.c | 18 ------------------
10
include/block/blockjob_int.h | 2 +-
8
2 files changed, 19 deletions(-)
11
include/qemu/job.h | 3 +++
12
block/backup.c | 2 +-
13
blockdev.c | 14 +++++------
14
blockjob.c | 60 +++++++++++++++++++++++---------------------
15
tests/test-blockjob-txn.c | 8 +++---
16
8 files changed, 54 insertions(+), 48 deletions(-)
17
9
18
diff --git a/include/block/block_int.h b/include/block/block_int.h
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
19
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/block_int.h
12
--- a/include/block/block_int.h
21
+++ b/include/block/block_int.h
13
+++ b/include/block/block_int.h
22
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
23
BlockdevOnError on_target_error,
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
24
int creation_flags,
16
25
BlockCompletionFunc *cb, void *opaque,
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
26
- BlockJobTxn *txn, Error **errp);
18
-bool bdrv_requests_pending(BlockDriverState *bs);
27
+ JobTxn *txn, Error **errp);
19
28
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
29
void hmp_drive_add_node(Monitor *mon, const char *optstr);
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
30
22
diff --git a/block/io.c b/block/io.c
31
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
32
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
33
--- a/include/block/blockjob.h
24
--- a/block/io.c
34
+++ b/include/block/blockjob.h
25
+++ b/block/io.c
35
@@ -XXX,XX +XXX,XX @@
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
36
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
27
assert(old >= 1);
37
38
typedef struct BlockJobDriver BlockJobDriver;
39
-typedef struct BlockJobTxn BlockJobTxn;
40
+typedef struct JobTxn JobTxn;
41
42
/**
43
* BlockJob:
44
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
45
/** BlockDriverStates that are involved in this block job */
46
GSList *nodes;
47
48
- BlockJobTxn *txn;
49
- QLIST_ENTRY(BlockJob) txn_list;
50
+ JobTxn *txn;
51
} BlockJob;
52
53
/**
54
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
55
* group. Jobs wait for each other before completing. Cancelling one job
56
* cancels all jobs in the transaction.
57
*/
58
-BlockJobTxn *block_job_txn_new(void);
59
+JobTxn *block_job_txn_new(void);
60
61
/**
62
* block_job_txn_unref:
63
@@ -XXX,XX +XXX,XX @@ BlockJobTxn *block_job_txn_new(void);
64
* or block_job_txn_new. If it's the last reference to the object, it will be
65
* freed.
66
*/
67
-void block_job_txn_unref(BlockJobTxn *txn);
68
+void block_job_txn_unref(JobTxn *txn);
69
70
/**
71
* block_job_txn_add_job:
72
@@ -XXX,XX +XXX,XX @@ void block_job_txn_unref(BlockJobTxn *txn);
73
* The caller must call either block_job_txn_unref() or block_job_completed()
74
* to release the reference that is automatically grabbed here.
75
*/
76
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job);
77
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
78
79
/**
80
* block_job_is_internal:
81
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob_int.h
84
+++ b/include/block/blockjob_int.h
85
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
86
* called from a wrapper that is specific to the job type.
87
*/
88
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
89
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
90
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
91
uint64_t shared_perm, int64_t speed, int flags,
92
BlockCompletionFunc *cb, void *opaque, Error **errp);
93
94
diff --git a/include/qemu/job.h b/include/qemu/job.h
95
index XXXXXXX..XXXXXXX 100644
96
--- a/include/qemu/job.h
97
+++ b/include/qemu/job.h
98
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
99
100
/** Element of the list of jobs */
101
QLIST_ENTRY(Job) job_list;
102
+
103
+ /** Element of the list of jobs in a job transaction */
104
+ QLIST_ENTRY(Job) txn_list;
105
} Job;
106
107
/**
108
diff --git a/block/backup.c b/block/backup.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/backup.c
111
+++ b/block/backup.c
112
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
113
BlockdevOnError on_target_error,
114
int creation_flags,
115
BlockCompletionFunc *cb, void *opaque,
116
- BlockJobTxn *txn, Error **errp)
117
+ JobTxn *txn, Error **errp)
118
{
119
int64_t len;
120
BlockDriverInfo bdi;
121
diff --git a/blockdev.c b/blockdev.c
122
index XXXXXXX..XXXXXXX 100644
123
--- a/blockdev.c
124
+++ b/blockdev.c
125
@@ -XXX,XX +XXX,XX @@ typedef struct BlkActionOps {
126
struct BlkActionState {
127
TransactionAction *action;
128
const BlkActionOps *ops;
129
- BlockJobTxn *block_job_txn;
130
+ JobTxn *block_job_txn;
131
TransactionProperties *txn_props;
132
QSIMPLEQ_ENTRY(BlkActionState) entry;
133
};
134
@@ -XXX,XX +XXX,XX @@ typedef struct DriveBackupState {
135
BlockJob *job;
136
} DriveBackupState;
137
138
-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
139
+static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
140
Error **errp);
141
142
static void drive_backup_prepare(BlkActionState *common, Error **errp)
143
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevBackupState {
144
BlockJob *job;
145
} BlockdevBackupState;
146
147
-static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
148
+static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
149
Error **errp);
150
151
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
152
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
153
Error **errp)
154
{
155
TransactionActionList *dev_entry = dev_list;
156
- BlockJobTxn *block_job_txn = NULL;
157
+ JobTxn *block_job_txn = NULL;
158
BlkActionState *state, *next;
159
Error *local_err = NULL;
160
161
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
162
QSIMPLEQ_INIT(&snap_bdrv_states);
163
164
/* Does this transaction get canceled as a group on failure?
165
- * If not, we don't really need to make a BlockJobTxn.
166
+ * If not, we don't really need to make a JobTxn.
167
*/
168
props = get_transaction_properties(props);
169
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
170
@@ -XXX,XX +XXX,XX @@ out:
171
aio_context_release(aio_context);
172
}
28
}
173
29
174
-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
30
-/* Check if any requests are in-flight (including throttled requests) */
175
+static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
31
-bool bdrv_requests_pending(BlockDriverState *bs)
176
Error **errp)
32
-{
177
{
33
- BdrvChild *child;
34
-
35
- if (atomic_read(&bs->in_flight)) {
36
- return true;
37
- }
38
-
39
- QLIST_FOREACH(child, &bs->children, next) {
40
- if (bdrv_requests_pending(child->bs)) {
41
- return true;
42
- }
43
- }
44
-
45
- return false;
46
-}
47
-
48
typedef struct {
49
Coroutine *co;
178
BlockDriverState *bs;
50
BlockDriverState *bs;
179
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
180
return bdrv_named_nodes_list(errp);
181
}
182
183
-BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
184
+BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
185
Error **errp)
186
{
187
BlockDriverState *bs;
188
diff --git a/blockjob.c b/blockjob.c
189
index XXXXXXX..XXXXXXX 100644
190
--- a/blockjob.c
191
+++ b/blockjob.c
192
@@ -XXX,XX +XXX,XX @@
193
#include "qemu/timer.h"
194
195
/* Transactional group of block jobs */
196
-struct BlockJobTxn {
197
+struct JobTxn {
198
199
/* Is this txn being cancelled? */
200
bool aborting;
201
202
/* List of jobs */
203
- QLIST_HEAD(, BlockJob) jobs;
204
+ QLIST_HEAD(, Job) jobs;
205
206
/* Reference count */
207
int refcnt;
208
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
209
}
210
}
211
212
-BlockJobTxn *block_job_txn_new(void)
213
+JobTxn *block_job_txn_new(void)
214
{
215
- BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
216
+ JobTxn *txn = g_new0(JobTxn, 1);
217
QLIST_INIT(&txn->jobs);
218
txn->refcnt = 1;
219
return txn;
220
}
221
222
-static void block_job_txn_ref(BlockJobTxn *txn)
223
+static void block_job_txn_ref(JobTxn *txn)
224
{
225
txn->refcnt++;
226
}
227
228
-void block_job_txn_unref(BlockJobTxn *txn)
229
+void block_job_txn_unref(JobTxn *txn)
230
{
231
if (txn && --txn->refcnt == 0) {
232
g_free(txn);
233
}
234
}
235
236
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
237
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job)
238
{
239
if (!txn) {
240
return;
241
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
242
assert(!job->txn);
243
job->txn = txn;
244
245
- QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
246
+ QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list);
247
block_job_txn_ref(txn);
248
}
249
250
void block_job_txn_del_job(BlockJob *job)
251
{
252
if (job->txn) {
253
- QLIST_REMOVE(job, txn_list);
254
+ QLIST_REMOVE(&job->job, txn_list);
255
block_job_txn_unref(job->txn);
256
job->txn = NULL;
257
}
258
@@ -XXX,XX +XXX,XX @@ static void job_cancel_async(Job *job, bool force)
259
job->force_cancel |= force;
260
}
261
262
-static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
263
+static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock)
264
{
265
AioContext *ctx;
266
- BlockJob *job, *next;
267
+ Job *job, *next;
268
+ BlockJob *bjob;
269
int rc = 0;
270
271
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
272
+ assert(is_block_job(job));
273
+ bjob = container_of(job, BlockJob, job);
274
+
275
if (lock) {
276
- ctx = blk_get_aio_context(job->blk);
277
+ ctx = job->aio_context;
278
aio_context_acquire(ctx);
279
}
280
- rc = fn(job);
281
+ rc = fn(bjob);
282
if (lock) {
283
aio_context_release(ctx);
284
}
285
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
286
static void block_job_completed_txn_abort(BlockJob *job)
287
{
288
AioContext *ctx;
289
- BlockJobTxn *txn = job->txn;
290
- BlockJob *other_job;
291
+ JobTxn *txn = job->txn;
292
+ Job *other_job;
293
294
if (txn->aborting) {
295
/*
296
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
297
298
/* We are the first failed job. Cancel other jobs. */
299
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
300
- ctx = blk_get_aio_context(other_job->blk);
301
+ ctx = other_job->aio_context;
302
aio_context_acquire(ctx);
303
}
304
305
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
306
* them; this job, however, may or may not be cancelled, depending
307
* on the caller, so leave it. */
308
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
309
- if (other_job != job) {
310
- job_cancel_async(&other_job->job, false);
311
+ if (other_job != &job->job) {
312
+ job_cancel_async(other_job, false);
313
}
314
}
315
while (!QLIST_EMPTY(&txn->jobs)) {
316
other_job = QLIST_FIRST(&txn->jobs);
317
- ctx = blk_get_aio_context(other_job->blk);
318
- if (!job_is_completed(&other_job->job)) {
319
- assert(job_is_cancelled(&other_job->job));
320
- job_finish_sync(&other_job->job, NULL, NULL);
321
+ ctx = other_job->aio_context;
322
+ if (!job_is_completed(other_job)) {
323
+ assert(job_is_cancelled(other_job));
324
+ job_finish_sync(other_job, NULL, NULL);
325
}
326
- job_finalize_single(&other_job->job);
327
+ job_finalize_single(other_job);
328
aio_context_release(ctx);
329
}
330
331
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
332
333
static void block_job_completed_txn_success(BlockJob *job)
334
{
335
- BlockJobTxn *txn = job->txn;
336
- BlockJob *other_job;
337
+ JobTxn *txn = job->txn;
338
+ Job *other_job;
339
340
job_state_transition(&job->job, JOB_STATUS_WAITING);
341
342
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
343
* txn.
344
*/
345
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
346
- if (!job_is_completed(&other_job->job)) {
347
+ if (!job_is_completed(other_job)) {
348
return;
349
}
350
- assert(other_job->job.ret == 0);
351
+ assert(other_job->ret == 0);
352
}
353
354
block_job_txn_apply(txn, block_job_transition_to_pending, false);
355
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
356
*/
357
358
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
359
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
360
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
361
uint64_t shared_perm, int64_t speed, int flags,
362
BlockCompletionFunc *cb, void *opaque, Error **errp)
363
{
364
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
365
index XXXXXXX..XXXXXXX 100644
366
--- a/tests/test-blockjob-txn.c
367
+++ b/tests/test-blockjob-txn.c
368
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
369
*/
370
static BlockJob *test_block_job_start(unsigned int iterations,
371
bool use_timer,
372
- int rc, int *result, BlockJobTxn *txn)
373
+ int rc, int *result, JobTxn *txn)
374
{
375
BlockDriverState *bs;
376
TestBlockJob *s;
377
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
378
static void test_single_job(int expected)
379
{
380
BlockJob *job;
381
- BlockJobTxn *txn;
382
+ JobTxn *txn;
383
int result = -EINPROGRESS;
384
385
txn = block_job_txn_new();
386
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
387
{
388
BlockJob *job1;
389
BlockJob *job2;
390
- BlockJobTxn *txn;
391
+ JobTxn *txn;
392
int result1 = -EINPROGRESS;
393
int result2 = -EINPROGRESS;
394
395
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
396
{
397
BlockJob *job1;
398
BlockJob *job2;
399
- BlockJobTxn *txn;
400
+ JobTxn *txn;
401
int result1 = -EINPROGRESS;
402
int result2 = -EINPROGRESS;
403
404
--
51
--
405
2.13.6
52
2.13.6
406
53
407
54
diff view generated by jsdifflib
1
The reference output file only works for file. 'qemu-img convert -p'
2
makes a lot more progress updates for NFS than for file, so disable the
3
test for NFS.
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
3
---
8
tests/qemu-iotests/086 | 2 +-
4
block/io.c | 6 ++++++
9
1 file changed, 1 insertion(+), 1 deletion(-)
5
1 file changed, 6 insertions(+)
10
6
11
diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086
7
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100755
8
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/086
9
--- a/block/io.c
14
+++ b/tests/qemu-iotests/086
10
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
16
. ./common.filter
12
BdrvNextIterator it;
17
13
GSList *aio_ctxs = NULL, *ctx;
18
_supported_fmt qcow2 raw
14
19
-_supported_proto file nfs
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
20
+_supported_proto file
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
21
_supported_os Linux
17
+ * nodes in several different AioContexts, so make sure we're in the main
22
18
+ * context. */
23
function run_qemu_img()
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
20
+
21
block_job_pause_all();
22
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
24
--
24
--
25
2.13.6
25
2.13.6
26
26
27
27
diff view generated by jsdifflib
Deleted patch
1
grep for "migrate" turns up a few test cases which use migration, but
2
haven't been in the "migration" group so far. Add them to the group.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Juan Quintela <quintela@redhat.com>
6
---
7
tests/qemu-iotests/group | 10 +++++-----
8
1 file changed, 5 insertions(+), 5 deletions(-)
9
10
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qemu-iotests/group
13
+++ b/tests/qemu-iotests/group
14
@@ -XXX,XX +XXX,XX @@
15
088 rw auto quick
16
089 rw auto quick
17
090 rw auto quick
18
-091 rw auto
19
+091 rw auto migration
20
092 rw auto quick
21
093 auto
22
094 rw auto quick
23
@@ -XXX,XX +XXX,XX @@
24
162 auto quick
25
163 rw auto
26
165 rw auto quick
27
-169 rw auto quick
28
+169 rw auto quick migration
29
170 rw auto quick
30
171 rw auto quick
31
172 auto
32
@@ -XXX,XX +XXX,XX @@
33
192 rw auto quick
34
194 rw auto migration quick
35
195 rw auto quick
36
-196 rw auto quick
37
+196 rw auto quick migration
38
197 rw auto quick
39
198 rw auto
40
-199 rw auto
41
+199 rw auto migration
42
200 rw auto
43
201 rw auto migration
44
202 rw auto quick
45
-203 rw auto
46
+203 rw auto migration
47
204 rw auto quick
48
205 rw auto quick
49
206 rw auto
50
--
51
2.13.6
52
53
diff view generated by jsdifflib
Deleted patch
1
185 and 191 define a MIG_SOCKET even though they don't do anything with
2
migration. Remove the useless variable.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Juan Quintela <quintela@redhat.com>
6
---
7
tests/qemu-iotests/185 | 2 --
8
tests/qemu-iotests/191 | 2 --
9
2 files changed, 4 deletions(-)
10
11
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/185
14
+++ b/tests/qemu-iotests/185
15
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
16
here=`pwd`
17
status=1 # failure is the default!
18
19
-MIG_SOCKET="${TEST_DIR}/migrate"
20
-
21
_cleanup()
22
{
23
rm -f "${TEST_IMG}.mid"
24
diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/191
27
+++ b/tests/qemu-iotests/191
28
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
29
here=`pwd`
30
status=1 # failure is the default!
31
32
-MIG_SOCKET="${TEST_DIR}/migrate"
33
-
34
_cleanup()
35
{
36
rm -f "${TEST_IMG}.mid"
37
--
38
2.13.6
39
40
diff view generated by jsdifflib
Deleted patch
1
Clarify that len is just an estimation of the end value of offset, and
2
that offset increases monotonically while len can change arbitrarily.
3
1
4
While touching the documentation of offset, move it directly after len
5
to match the order of the declaration below.
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: John Snow <jsnow@redhat.com>
10
---
11
qapi/block-core.json | 9 ++++++---
12
1 file changed, 6 insertions(+), 3 deletions(-)
13
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
17
+++ b/qapi/block-core.json
18
@@ -XXX,XX +XXX,XX @@
19
# @device: The job identifier. Originally the device name but other
20
# values are allowed since QEMU 2.7
21
#
22
-# @len: the maximum progress value
23
+# @len: Estimated @offset value at the completion of the job. This value can
24
+# arbitrarily change while the job is running, in both directions.
25
+#
26
+# @offset: Progress made until now. The unit is arbitrary and the value can
27
+# only meaningfully be used for the ratio of @offset to @len. The
28
+# value is monotonically increasing.
29
#
30
# @busy: false if the job is known to be in a quiescent state, with
31
# no pending I/O. Since 1.3.
32
@@ -XXX,XX +XXX,XX @@
33
# @paused: whether the job is paused or, if @busy is true, will
34
# pause itself as soon as possible. Since 1.3.
35
#
36
-# @offset: the current progress value
37
-#
38
# @speed: the rate limit, bytes per second
39
#
40
# @io-status: the status of the job (since 1.3)
41
--
42
2.13.6
43
44
diff view generated by jsdifflib
1
block_job_finish_sync() doesn't contain anything block job specific any
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
more, so it can be moved to Job.
2
and also doesn't notify other parent nodes of children, which both means
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
5
6
To keep things consistent, we also shouldn't call the block driver
7
callbacks recursively.
8
9
A proper recursive drain version that provides an actually working
10
drained section for child nodes will be introduced later.
3
11
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Fam Zheng <famz@redhat.com>
6
---
14
---
7
include/qemu/job.h | 9 +++++++++
15
block/io.c | 16 +++++++++-------
8
block/commit.c | 6 +++---
16
1 file changed, 9 insertions(+), 7 deletions(-)
9
blockjob.c | 55 +++++++++---------------------------------------------
10
job.c | 28 +++++++++++++++++++++++++++
11
4 files changed, 49 insertions(+), 49 deletions(-)
12
17
13
diff --git a/include/qemu/job.h b/include/qemu/job.h
18
diff --git a/block/io.c b/block/io.c
14
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qemu/job.h
20
--- a/block/io.c
16
+++ b/include/qemu/job.h
21
+++ b/block/io.c
17
@@ -XXX,XX +XXX,XX @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
18
*/
19
void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
20
21
+/**
22
+ * Synchronously finishes the given @job. If @finish is given, it is called to
23
+ * trigger completion or cancellation of the job.
24
+ *
25
+ * Returns 0 if the job is successfully completed, -ECANCELED if the job was
26
+ * cancelled before completing, and -errno in other error cases.
27
+ */
28
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
29
+
30
/* TODO To be removed from the public interface */
31
void job_state_transition(Job *job, JobStatus s1);
32
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
33
diff --git a/block/commit.c b/block/commit.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block/commit.c
36
+++ b/block/commit.c
37
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
38
blk_unref(s->top);
39
40
/* If there is more than one reference to the job (e.g. if called from
41
- * block_job_finish_sync()), block_job_completed() won't free it and
42
- * therefore the blockers on the intermediate nodes remain. This would
43
- * cause bdrv_set_backing_hd() to fail. */
44
+ * job_finish_sync()), block_job_completed() won't free it and therefore
45
+ * the blockers on the intermediate nodes remain. This would cause
46
+ * bdrv_set_backing_hd() to fail. */
47
block_job_remove_all_bdrv(bjob);
48
49
block_job_completed(&s->common, ret);
50
diff --git a/blockjob.c b/blockjob.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/blockjob.c
53
+++ b/blockjob.c
54
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
55
return rc;
56
}
23
}
57
24
58
-static int block_job_finish_sync(BlockJob *job,
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
59
- void (*finish)(BlockJob *, Error **errp),
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
60
- Error **errp)
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
61
-{
62
- Error *local_err = NULL;
63
- int ret;
64
-
65
- assert(blk_bs(job->blk)->job == job);
66
-
67
- job_ref(&job->job);
68
-
69
- if (finish) {
70
- finish(job, &local_err);
71
- }
72
- if (local_err) {
73
- error_propagate(errp, local_err);
74
- job_unref(&job->job);
75
- return -EBUSY;
76
- }
77
- /* job_drain calls job_enter, and it should be enough to induce progress
78
- * until the job completes or moves to the main thread.
79
- */
80
- while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
81
- job_drain(&job->job);
82
- }
83
- while (!job_is_completed(&job->job)) {
84
- aio_poll(qemu_get_aio_context(), true);
85
- }
86
- ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
87
- ? -ECANCELED : job->job.ret;
88
- job_unref(&job->job);
89
- return ret;
90
-}
91
-
92
static void block_job_completed_txn_abort(BlockJob *job)
93
{
28
{
94
AioContext *ctx;
29
BdrvChild *child, *tmp;
95
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
96
ctx = blk_get_aio_context(other_job->blk);
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
97
if (!job_is_completed(&other_job->job)) {
32
bdrv_coroutine_enter(bs, data.co);
98
assert(job_is_cancelled(&other_job->job));
33
BDRV_POLL_WHILE(bs, !data.done);
99
- block_job_finish_sync(other_job, NULL, NULL);
34
100
+ job_finish_sync(&other_job->job, NULL, NULL);
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
101
}
36
- bdrv_drain_invoke(child->bs, begin);
102
job_finalize_single(&other_job->job);
37
+ if (recursive) {
103
aio_context_release(ctx);
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
104
@@ -XXX,XX +XXX,XX @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
39
+ bdrv_drain_invoke(child->bs, begin, true);
105
}
40
+ }
106
107
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
108
- * used with block_job_finish_sync() without the need for (rather nasty)
109
- * function pointer casts there. */
110
-static void block_job_cancel_err(BlockJob *job, Error **errp)
111
+ * used with job_finish_sync() without the need for (rather nasty) function
112
+ * pointer casts there. */
113
+static void block_job_cancel_err(Job *job, Error **errp)
114
{
115
- block_job_cancel(job, false);
116
+ BlockJob *bjob = container_of(job, BlockJob, job);
117
+ assert(is_block_job(job));
118
+ block_job_cancel(bjob, false);
119
}
120
121
int block_job_cancel_sync(BlockJob *job)
122
{
123
- return block_job_finish_sync(job, &block_job_cancel_err, NULL);
124
+ return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
125
}
126
127
void block_job_cancel_sync_all(void)
128
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
129
}
41
}
130
}
42
}
131
43
132
-static void block_job_complete(BlockJob *job, Error **errp)
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
133
-{
45
bdrv_parent_drained_begin(bs);
134
- job_complete(&job->job, errp);
46
}
135
-}
47
136
-
48
- bdrv_drain_invoke(bs, true);
137
int block_job_complete_sync(BlockJob *job, Error **errp)
49
+ bdrv_drain_invoke(bs, true, false);
138
{
50
bdrv_drain_recurse(bs);
139
- return block_job_finish_sync(job, &block_job_complete, errp);
140
+ return job_finish_sync(&job->job, job_complete, errp);
141
}
51
}
142
52
143
void block_job_progress_update(BlockJob *job, uint64_t done)
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
144
diff --git a/job.c b/job.c
54
}
145
index XXXXXXX..XXXXXXX 100644
55
146
--- a/job.c
56
/* Re-enable things in child-to-parent order */
147
+++ b/job.c
57
- bdrv_drain_invoke(bs, false);
148
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque)
58
+ bdrv_drain_invoke(bs, false, false);
149
aio_bh_schedule_oneshot(qemu_get_aio_context(),
59
bdrv_parent_drained_end(bs);
150
job_defer_to_main_loop_bh, data);
60
aio_enable_external(bdrv_get_aio_context(bs));
151
}
61
}
152
+
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
153
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
63
aio_context_acquire(aio_context);
154
+{
64
aio_disable_external(aio_context);
155
+ Error *local_err = NULL;
65
bdrv_parent_drained_begin(bs);
156
+ int ret;
66
- bdrv_drain_invoke(bs, true);
157
+
67
+ bdrv_drain_invoke(bs, true, true);
158
+ job_ref(job);
68
aio_context_release(aio_context);
159
+
69
160
+ if (finish) {
70
if (!g_slist_find(aio_ctxs, aio_context)) {
161
+ finish(job, &local_err);
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
162
+ }
72
163
+ if (local_err) {
73
/* Re-enable things in child-to-parent order */
164
+ error_propagate(errp, local_err);
74
aio_context_acquire(aio_context);
165
+ job_unref(job);
75
- bdrv_drain_invoke(bs, false);
166
+ return -EBUSY;
76
+ bdrv_drain_invoke(bs, false, true);
167
+ }
77
bdrv_parent_drained_end(bs);
168
+ /* job_drain calls job_enter, and it should be enough to induce progress
78
aio_enable_external(aio_context);
169
+ * until the job completes or moves to the main thread. */
79
aio_context_release(aio_context);
170
+ while (!job->deferred_to_main_loop && !job_is_completed(job)) {
171
+ job_drain(job);
172
+ }
173
+ while (!job_is_completed(job)) {
174
+ aio_poll(qemu_get_aio_context(), true);
175
+ }
176
+ ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret;
177
+ job_unref(job);
178
+ return ret;
179
+}
180
--
80
--
181
2.13.6
81
2.13.6
182
82
183
83
diff view generated by jsdifflib
1
This moves the logic that implements job transactions from BlockJob to
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
Job.
2
test case so that it can be run for the other variants as well. At the
3
moment this is only bdrv_drain_begin/end(), but in a while, we'll add
4
another one.
5
6
Also, add a backing file to the test node to test whether the operations
7
work recursively.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
10
---
7
include/block/blockjob.h | 54 ----------
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
8
include/block/blockjob_int.h | 10 --
12
1 file changed, 62 insertions(+), 7 deletions(-)
9
include/qemu/job.h | 71 +++++++++++--
10
blockdev.c | 6 +-
11
blockjob.c | 238 +------------------------------------------
12
job.c | 234 ++++++++++++++++++++++++++++++++++++++++--
13
tests/test-blockjob-txn.c | 12 +--
14
tests/test-blockjob.c | 2 +-
15
8 files changed, 303 insertions(+), 324 deletions(-)
16
13
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
18
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
16
--- a/tests/test-bdrv-drain.c
20
+++ b/include/block/blockjob.h
17
+++ b/tests/test-bdrv-drain.c
21
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
22
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
19
23
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
24
typedef struct BlockJobDriver BlockJobDriver;
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
25
-typedef struct JobTxn JobTxn;
26
27
/**
28
* BlockJob:
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
30
31
/** BlockDriverStates that are involved in this block job */
32
GSList *nodes;
33
-
34
- JobTxn *txn;
35
} BlockJob;
36
37
/**
38
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
39
void block_job_cancel(BlockJob *job, bool force);
40
41
/**
42
- * block_job_finalize:
43
- * @job: The job to fully commit and finish.
44
- * @errp: Error object.
45
- *
46
- * For jobs that have finished their work and are pending
47
- * awaiting explicit acknowledgement to commit their work,
48
- * This will commit that work.
49
- *
50
- * FIXME: Make the below statement universally true:
51
- * For jobs that support the manual workflow mode, all graph
52
- * changes that occur as a result will occur after this command
53
- * and before a successful reply.
54
- */
55
-void block_job_finalize(BlockJob *job, Error **errp);
56
-
57
-/**
58
* block_job_dismiss:
59
* @job: The job to be dismissed.
60
* @errp: Error object.
61
@@ -XXX,XX +XXX,XX @@ int block_job_complete_sync(BlockJob *job, Error **errp);
62
void block_job_iostatus_reset(BlockJob *job);
63
64
/**
65
- * block_job_txn_new:
66
- *
67
- * Allocate and return a new block job transaction. Jobs can be added to the
68
- * transaction using block_job_txn_add_job().
69
- *
70
- * The transaction is automatically freed when the last job completes or is
71
- * cancelled.
72
- *
73
- * All jobs in the transaction either complete successfully or fail/cancel as a
74
- * group. Jobs wait for each other before completing. Cancelling one job
75
- * cancels all jobs in the transaction.
76
- */
77
-JobTxn *block_job_txn_new(void);
78
-
79
-/**
80
- * block_job_txn_unref:
81
- *
82
- * Release a reference that was previously acquired with block_job_txn_add_job
83
- * or block_job_txn_new. If it's the last reference to the object, it will be
84
- * freed.
85
- */
86
-void block_job_txn_unref(JobTxn *txn);
87
-
88
-/**
89
- * block_job_txn_add_job:
90
- * @txn: The transaction (may be NULL)
91
- * @job: Job to add to the transaction
92
- *
93
- * Add @job to the transaction. The @job must not already be in a transaction.
94
- * The caller must call either block_job_txn_unref() or block_job_completed()
95
- * to release the reference that is automatically grabbed here.
96
- */
97
-void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
98
-
99
-/**
100
* block_job_is_internal:
101
* @job: The job to determine if it is user-visible or not.
102
*
103
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/blockjob_int.h
106
+++ b/include/block/blockjob_int.h
107
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
108
/** Generic JobDriver callbacks and settings */
109
JobDriver job_driver;
110
111
- /**
112
- * If the callback is not NULL, prepare will be invoked when all the jobs
113
- * belonging to the same transaction complete; or upon this job's completion
114
- * if it is not in a transaction.
115
- *
116
- * This callback will not be invoked if the job has already failed.
117
- * If it fails, abort and then clean will be called.
118
- */
119
- int (*prepare)(BlockJob *job);
120
-
121
/*
122
* If the callback is not NULL, it will be invoked before the job is
123
* resumed in a new AioContext. This is the place to move any resources
124
diff --git a/include/qemu/job.h b/include/qemu/job.h
125
index XXXXXXX..XXXXXXX 100644
126
--- a/include/qemu/job.h
127
+++ b/include/qemu/job.h
128
@@ -XXX,XX +XXX,XX @@
129
#include "block/aio.h"
130
131
typedef struct JobDriver JobDriver;
132
+typedef struct JobTxn JobTxn;
133
+
22
+
134
23
+ .bdrv_child_perm = bdrv_format_default_perms,
135
/**
24
};
136
* Long-running operation.
25
137
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
26
static void aio_ret_cb(void *opaque, int ret)
138
/** Element of the list of jobs */
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
139
QLIST_ENTRY(Job) job_list;
28
*aio_ret = ret;
140
141
+ /** Transaction this job is part of */
142
+ JobTxn *txn;
143
+
144
/** Element of the list of jobs in a job transaction */
145
QLIST_ENTRY(Job) txn_list;
146
} Job;
147
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
148
void (*drain)(Job *job);
149
150
/**
151
+ * If the callback is not NULL, prepare will be invoked when all the jobs
152
+ * belonging to the same transaction complete; or upon this job's completion
153
+ * if it is not in a transaction.
154
+ *
155
+ * This callback will not be invoked if the job has already failed.
156
+ * If it fails, abort and then clean will be called.
157
+ */
158
+ int (*prepare)(Job *job);
159
+
160
+ /**
161
* If the callback is not NULL, it will be invoked when all the jobs
162
* belonging to the same transaction complete; or upon this job's
163
* completion if it is not in a transaction. Skipped if NULL.
164
@@ -XXX,XX +XXX,XX @@ typedef enum JobCreateFlags {
165
JOB_MANUAL_DISMISS = 0x04,
166
} JobCreateFlags;
167
168
+/**
169
+ * Allocate and return a new job transaction. Jobs can be added to the
170
+ * transaction using job_txn_add_job().
171
+ *
172
+ * The transaction is automatically freed when the last job completes or is
173
+ * cancelled.
174
+ *
175
+ * All jobs in the transaction either complete successfully or fail/cancel as a
176
+ * group. Jobs wait for each other before completing. Cancelling one job
177
+ * cancels all jobs in the transaction.
178
+ */
179
+JobTxn *job_txn_new(void);
180
+
181
+/**
182
+ * Release a reference that was previously acquired with job_txn_add_job or
183
+ * job_txn_new. If it's the last reference to the object, it will be freed.
184
+ */
185
+void job_txn_unref(JobTxn *txn);
186
+
187
+/**
188
+ * @txn: The transaction (may be NULL)
189
+ * @job: Job to add to the transaction
190
+ *
191
+ * Add @job to the transaction. The @job must not already be in a transaction.
192
+ * The caller must call either job_txn_unref() or block_job_completed() to
193
+ * release the reference that is automatically grabbed here.
194
+ *
195
+ * If @txn is NULL, the function does nothing.
196
+ */
197
+void job_txn_add_job(JobTxn *txn, Job *job);
198
199
/**
200
* Create a new long-running job and return it.
201
*
202
* @job_id: The id of the newly-created job, or %NULL for internal jobs
203
* @driver: The class object for the newly-created job.
204
+ * @txn: The transaction this job belongs to, if any. %NULL otherwise.
205
* @ctx: The AioContext to run the job coroutine in.
206
* @flags: Creation flags for the job. See @JobCreateFlags.
207
* @cb: Completion function for the job.
208
* @opaque: Opaque pointer value passed to @cb.
209
* @errp: Error object.
210
*/
211
-void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
212
- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
213
+void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
214
+ AioContext *ctx, int flags, BlockCompletionFunc *cb,
215
+ void *opaque, Error **errp);
216
217
/**
218
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
219
@@ -XXX,XX +XXX,XX @@ void job_event_cancelled(Job *job);
220
/** To be called when a successfully completed job is finalised. */
221
void job_event_completed(Job *job);
222
223
-/** To be called when the job transitions to PENDING */
224
-void job_event_pending(Job *job);
225
-
226
/**
227
* Conditionally enter the job coroutine if the job is ready to run, not
228
* already busy and fn() returns true. fn() is called while under the job_lock
229
@@ -XXX,XX +XXX,XX @@ void job_early_fail(Job *job);
230
/** Asynchronously complete the specified @job. */
231
void job_complete(Job *job, Error **errp);;
232
233
+/**
234
+ * For a @job that has finished its work and is pending awaiting explicit
235
+ * acknowledgement to commit its work, this will commit that work.
236
+ *
237
+ * FIXME: Make the below statement universally true:
238
+ * For jobs that support the manual workflow mode, all graph changes that occur
239
+ * as a result will occur after this command and before a successful reply.
240
+ */
241
+void job_finalize(Job *job, Error **errp);
242
+
243
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
244
245
/**
246
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_do_yield(Job *job, uint64_t ns);
247
bool job_should_pause(Job *job);
248
bool job_started(Job *job);
249
void job_do_dismiss(Job *job);
250
-int job_finalize_single(Job *job);
251
void job_update_rc(Job *job);
252
-
253
-typedef struct BlockJob BlockJob;
254
-void block_job_txn_del_job(BlockJob *job);
255
+void job_cancel_async(Job *job, bool force);
256
+void job_completed_txn_abort(Job *job);
257
+void job_completed_txn_success(Job *job);
258
259
#endif
260
diff --git a/blockdev.c b/blockdev.c
261
index XXXXXXX..XXXXXXX 100644
262
--- a/blockdev.c
263
+++ b/blockdev.c
264
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
265
*/
266
props = get_transaction_properties(props);
267
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
268
- block_job_txn = block_job_txn_new();
269
+ block_job_txn = job_txn_new();
270
}
271
272
/* drain all i/o before any operations */
273
@@ -XXX,XX +XXX,XX @@ exit:
274
if (!has_props) {
275
qapi_free_TransactionProperties(props);
276
}
277
- block_job_txn_unref(block_job_txn);
278
+ job_txn_unref(block_job_txn);
279
}
29
}
280
30
281
void qmp_eject(bool has_device, const char *device,
31
-static void test_drv_cb_drain_all(void)
282
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
32
+enum drain_type {
283
}
33
+ BDRV_DRAIN_ALL,
284
34
+ BDRV_DRAIN,
285
trace_qmp_block_job_finalize(job);
286
- block_job_finalize(job, errp);
287
+ job_finalize(&job->job, errp);
288
aio_context_release(aio_context);
289
}
290
291
diff --git a/blockjob.c b/blockjob.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockjob.c
294
+++ b/blockjob.c
295
@@ -XXX,XX +XXX,XX @@
296
#include "qemu/coroutine.h"
297
#include "qemu/timer.h"
298
299
-/* Transactional group of block jobs */
300
-struct JobTxn {
301
-
302
- /* Is this txn being cancelled? */
303
- bool aborting;
304
-
305
- /* List of jobs */
306
- QLIST_HEAD(, Job) jobs;
307
-
308
- /* Reference count */
309
- int refcnt;
310
-};
311
-
312
/*
313
* The block job API is composed of two categories of functions.
314
*
315
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
316
}
317
}
318
319
-JobTxn *block_job_txn_new(void)
320
-{
321
- JobTxn *txn = g_new0(JobTxn, 1);
322
- QLIST_INIT(&txn->jobs);
323
- txn->refcnt = 1;
324
- return txn;
325
-}
326
-
327
-static void block_job_txn_ref(JobTxn *txn)
328
-{
329
- txn->refcnt++;
330
-}
331
-
332
-void block_job_txn_unref(JobTxn *txn)
333
-{
334
- if (txn && --txn->refcnt == 0) {
335
- g_free(txn);
336
- }
337
-}
338
-
339
-void block_job_txn_add_job(JobTxn *txn, BlockJob *job)
340
-{
341
- if (!txn) {
342
- return;
343
- }
344
-
345
- assert(!job->txn);
346
- job->txn = txn;
347
-
348
- QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list);
349
- block_job_txn_ref(txn);
350
-}
351
-
352
-void block_job_txn_del_job(BlockJob *job)
353
-{
354
- if (job->txn) {
355
- QLIST_REMOVE(&job->job, txn_list);
356
- block_job_txn_unref(job->txn);
357
- job->txn = NULL;
358
- }
359
-}
360
-
361
static void block_job_attached_aio_context(AioContext *new_context,
362
void *opaque);
363
static void block_job_detach_aio_context(void *opaque);
364
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
365
BlockJob *bjob = container_of(job, BlockJob, job);
366
BlockDriverState *bs = blk_bs(bjob->blk);
367
368
- assert(!bjob->txn);
369
-
370
bs->job = NULL;
371
block_job_remove_all_bdrv(bjob);
372
blk_remove_aio_context_notifier(bjob->blk,
373
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
374
return job->driver;
375
}
376
377
-static int block_job_prepare(BlockJob *job)
378
-{
379
- if (job->job.ret == 0 && job->driver->prepare) {
380
- job->job.ret = job->driver->prepare(job);
381
- }
382
- return job->job.ret;
383
-}
384
-
385
-static void job_cancel_async(Job *job, bool force)
386
-{
387
- if (job->user_paused) {
388
- /* Do not call job_enter here, the caller will handle it. */
389
- job->user_paused = false;
390
- if (job->driver->user_resume) {
391
- job->driver->user_resume(job);
392
- }
393
- assert(job->pause_count > 0);
394
- job->pause_count--;
395
- }
396
- job->cancelled = true;
397
- /* To prevent 'force == false' overriding a previous 'force == true' */
398
- job->force_cancel |= force;
399
-}
400
-
401
-static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock)
402
-{
403
- AioContext *ctx;
404
- Job *job, *next;
405
- BlockJob *bjob;
406
- int rc = 0;
407
-
408
- QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
409
- assert(is_block_job(job));
410
- bjob = container_of(job, BlockJob, job);
411
-
412
- if (lock) {
413
- ctx = job->aio_context;
414
- aio_context_acquire(ctx);
415
- }
416
- rc = fn(bjob);
417
- if (lock) {
418
- aio_context_release(ctx);
419
- }
420
- if (rc) {
421
- break;
422
- }
423
- }
424
- return rc;
425
-}
426
-
427
-static void block_job_completed_txn_abort(BlockJob *job)
428
-{
429
- AioContext *ctx;
430
- JobTxn *txn = job->txn;
431
- Job *other_job;
432
-
433
- if (txn->aborting) {
434
- /*
435
- * We are cancelled by another job, which will handle everything.
436
- */
437
- return;
438
- }
439
- txn->aborting = true;
440
- block_job_txn_ref(txn);
441
-
442
- /* We are the first failed job. Cancel other jobs. */
443
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
444
- ctx = other_job->aio_context;
445
- aio_context_acquire(ctx);
446
- }
447
-
448
- /* Other jobs are effectively cancelled by us, set the status for
449
- * them; this job, however, may or may not be cancelled, depending
450
- * on the caller, so leave it. */
451
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
452
- if (other_job != &job->job) {
453
- job_cancel_async(other_job, false);
454
- }
455
- }
456
- while (!QLIST_EMPTY(&txn->jobs)) {
457
- other_job = QLIST_FIRST(&txn->jobs);
458
- ctx = other_job->aio_context;
459
- if (!job_is_completed(other_job)) {
460
- assert(job_is_cancelled(other_job));
461
- job_finish_sync(other_job, NULL, NULL);
462
- }
463
- job_finalize_single(other_job);
464
- aio_context_release(ctx);
465
- }
466
-
467
- block_job_txn_unref(txn);
468
-}
469
-
470
-static int block_job_needs_finalize(BlockJob *job)
471
-{
472
- return !job->job.auto_finalize;
473
-}
474
-
475
-static int block_job_finalize_single(BlockJob *job)
476
-{
477
- return job_finalize_single(&job->job);
478
-}
479
-
480
-static void block_job_do_finalize(BlockJob *job)
481
-{
482
- int rc;
483
- assert(job && job->txn);
484
-
485
- /* prepare the transaction to complete */
486
- rc = block_job_txn_apply(job->txn, block_job_prepare, true);
487
- if (rc) {
488
- block_job_completed_txn_abort(job);
489
- } else {
490
- block_job_txn_apply(job->txn, block_job_finalize_single, true);
491
- }
492
-}
493
-
494
-static int block_job_transition_to_pending(BlockJob *job)
495
-{
496
- job_state_transition(&job->job, JOB_STATUS_PENDING);
497
- if (!job->job.auto_finalize) {
498
- job_event_pending(&job->job);
499
- }
500
- return 0;
501
-}
502
-
503
-static void block_job_completed_txn_success(BlockJob *job)
504
-{
505
- JobTxn *txn = job->txn;
506
- Job *other_job;
507
-
508
- job_state_transition(&job->job, JOB_STATUS_WAITING);
509
-
510
- /*
511
- * Successful completion, see if there are other running jobs in this
512
- * txn.
513
- */
514
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
515
- if (!job_is_completed(other_job)) {
516
- return;
517
- }
518
- assert(other_job->ret == 0);
519
- }
520
-
521
- block_job_txn_apply(txn, block_job_transition_to_pending, false);
522
-
523
- /* If no jobs need manual finalization, automatically do so */
524
- if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
525
- block_job_do_finalize(job);
526
- }
527
-}
528
-
529
/* Assumes the job_mutex is held */
530
static bool job_timer_pending(Job *job)
531
{
532
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
533
return ratelimit_calculate_delay(&job->limit, n);
534
}
535
536
-void block_job_finalize(BlockJob *job, Error **errp)
537
-{
538
- assert(job && job->job.id);
539
- if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) {
540
- return;
541
- }
542
- block_job_do_finalize(job);
543
-}
544
-
545
void block_job_dismiss(BlockJob **jobptr, Error **errp)
546
{
547
BlockJob *job = *jobptr;
548
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
549
if (!job_started(&job->job)) {
550
block_job_completed(job, -ECANCELED);
551
} else if (job->job.deferred_to_main_loop) {
552
- block_job_completed_txn_abort(job);
553
+ job_completed_txn_abort(&job->job);
554
} else {
555
block_job_enter(job);
556
}
557
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
558
return NULL;
559
}
560
561
- job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
562
+ job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk),
563
flags, cb, opaque, errp);
564
if (job == NULL) {
565
blk_unref(blk);
566
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
567
}
568
}
569
570
- /* Single jobs are modeled as single-job transactions for sake of
571
- * consolidating the job management logic */
572
- if (!txn) {
573
- txn = block_job_txn_new();
574
- block_job_txn_add_job(txn, job);
575
- block_job_txn_unref(txn);
576
- } else {
577
- block_job_txn_add_job(txn, job);
578
- }
579
-
580
return job;
581
}
582
583
void block_job_completed(BlockJob *job, int ret)
584
{
585
- assert(job && job->txn && !job_is_completed(&job->job));
586
+ assert(job && job->job.txn && !job_is_completed(&job->job));
587
assert(blk_bs(job->blk)->job == job);
588
job->job.ret = ret;
589
job_update_rc(&job->job);
590
trace_block_job_completed(job, ret, job->job.ret);
591
if (job->job.ret) {
592
- block_job_completed_txn_abort(job);
593
+ job_completed_txn_abort(&job->job);
594
} else {
595
- block_job_completed_txn_success(job);
596
+ job_completed_txn_success(&job->job);
597
}
598
}
599
600
diff --git a/job.c b/job.c
601
index XXXXXXX..XXXXXXX 100644
602
--- a/job.c
603
+++ b/job.c
604
@@ -XXX,XX +XXX,XX @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
605
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
606
};
607
608
+/* Transactional group of jobs */
609
+struct JobTxn {
610
+
611
+ /* Is this txn being cancelled? */
612
+ bool aborting;
613
+
614
+ /* List of jobs */
615
+ QLIST_HEAD(, Job) jobs;
616
+
617
+ /* Reference count */
618
+ int refcnt;
619
+};
35
+};
620
+
36
+
621
/* Right now, this mutex is only needed to synchronize accesses to job->busy
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
622
* and job->sleep_timer, such as concurrent calls to job_do_yield and
623
* job_enter. */
624
@@ -XXX,XX +XXX,XX @@ static void __attribute__((__constructor__)) job_init(void)
625
qemu_mutex_init(&job_mutex);
626
}
627
628
+JobTxn *job_txn_new(void)
629
+{
38
+{
630
+ JobTxn *txn = g_new0(JobTxn, 1);
39
+ switch (drain_type) {
631
+ QLIST_INIT(&txn->jobs);
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
632
+ txn->refcnt = 1;
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
633
+ return txn;
42
+ default: g_assert_not_reached();
634
+}
635
+
636
+static void job_txn_ref(JobTxn *txn)
637
+{
638
+ txn->refcnt++;
639
+}
640
+
641
+void job_txn_unref(JobTxn *txn)
642
+{
643
+ if (txn && --txn->refcnt == 0) {
644
+ g_free(txn);
645
+ }
43
+ }
646
+}
44
+}
647
+
45
+
648
+void job_txn_add_job(JobTxn *txn, Job *job)
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
649
+{
47
+{
650
+ if (!txn) {
48
+ switch (drain_type) {
651
+ return;
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
652
+ }
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
653
+
51
+ default: g_assert_not_reached();
654
+ assert(!job->txn);
655
+ job->txn = txn;
656
+
657
+ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
658
+ job_txn_ref(txn);
659
+}
660
+
661
+static void job_txn_del_job(Job *job)
662
+{
663
+ if (job->txn) {
664
+ QLIST_REMOVE(job, txn_list);
665
+ job_txn_unref(job->txn);
666
+ job->txn = NULL;
667
+ }
52
+ }
668
+}
53
+}
669
+
54
+
670
+static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
56
{
57
BlockBackend *blk;
58
- BlockDriverState *bs;
59
- BDRVTestState *s;
60
+ BlockDriverState *bs, *backing;
61
+ BDRVTestState *s, *backing_s;
62
BlockAIOCB *acb;
63
int aio_ret;
64
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
66
s = bs->opaque;
67
blk_insert_bs(blk, bs, &error_abort);
68
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
70
+ backing_s = backing->opaque;
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
72
+
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
74
g_assert_cmpint(s->drain_count, ==, 0);
75
- bdrv_drain_all_begin();
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
77
+
78
+ do_drain_begin(drain_type, bs);
79
+
80
g_assert_cmpint(s->drain_count, ==, 1);
81
- bdrv_drain_all_end();
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
83
+
84
+ do_drain_end(drain_type, bs);
85
+
86
g_assert_cmpint(s->drain_count, ==, 0);
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
88
89
/* Now do the same while a request is pending */
90
aio_ret = -EINPROGRESS;
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
93
94
g_assert_cmpint(s->drain_count, ==, 0);
95
- bdrv_drain_all_begin();
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
97
+
98
+ do_drain_begin(drain_type, bs);
99
+
100
g_assert_cmpint(aio_ret, ==, 0);
101
g_assert_cmpint(s->drain_count, ==, 1);
102
- bdrv_drain_all_end();
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
104
+
105
+ do_drain_end(drain_type, bs);
106
+
107
g_assert_cmpint(s->drain_count, ==, 0);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
109
110
+ bdrv_unref(backing);
111
bdrv_unref(bs);
112
blk_unref(blk);
113
}
114
115
+static void test_drv_cb_drain_all(void)
671
+{
116
+{
672
+ AioContext *ctx;
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
673
+ Job *job, *next;
674
+ int rc = 0;
675
+
676
+ QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
677
+ if (lock) {
678
+ ctx = job->aio_context;
679
+ aio_context_acquire(ctx);
680
+ }
681
+ rc = fn(job);
682
+ if (lock) {
683
+ aio_context_release(ctx);
684
+ }
685
+ if (rc) {
686
+ break;
687
+ }
688
+ }
689
+ return rc;
690
+}
118
+}
691
+
119
+
692
+
120
+static void test_drv_cb_drain(void)
693
/* TODO Make static once the whole state machine is in job.c */
694
void job_state_transition(Job *job, JobStatus s1)
695
{
696
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
697
job_enter(job);
698
}
699
700
-void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
701
- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp)
702
+void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
703
+ AioContext *ctx, int flags, BlockCompletionFunc *cb,
704
+ void *opaque, Error **errp)
705
{
706
Job *job;
707
708
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
709
710
QLIST_INSERT_HEAD(&jobs, job, job_list);
711
712
+ /* Single jobs are modeled as single-job transactions for sake of
713
+ * consolidating the job management logic */
714
+ if (!txn) {
715
+ txn = job_txn_new();
716
+ job_txn_add_job(txn, job);
717
+ job_txn_unref(txn);
718
+ } else {
719
+ job_txn_add_job(txn, job);
720
+ }
721
+
722
return job;
723
}
724
725
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
726
if (--job->refcnt == 0) {
727
assert(job->status == JOB_STATUS_NULL);
728
assert(!timer_pending(&job->sleep_timer));
729
+ assert(!job->txn);
730
731
if (job->driver->free) {
732
job->driver->free(job);
733
@@ -XXX,XX +XXX,XX @@ void job_event_completed(Job *job)
734
notifier_list_notify(&job->on_finalize_completed, job);
735
}
736
737
-void job_event_pending(Job *job)
738
+static void job_event_pending(Job *job)
739
{
740
notifier_list_notify(&job->on_pending, job);
741
}
742
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
743
job->paused = false;
744
job->deferred_to_main_loop = true;
745
746
- /* TODO Don't assume it's a BlockJob */
747
- block_job_txn_del_job((BlockJob*) job);
748
+ job_txn_del_job(job);
749
750
job_state_transition(job, JOB_STATUS_NULL);
751
job_unref(job);
752
@@ -XXX,XX +XXX,XX @@ static void job_clean(Job *job)
753
}
754
}
755
756
-int job_finalize_single(Job *job)
757
+static int job_finalize_single(Job *job)
758
{
759
assert(job_is_completed(job));
760
761
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
762
}
763
}
764
765
- /* TODO Don't assume it's a BlockJob */
766
- block_job_txn_del_job((BlockJob*) job);
767
+ job_txn_del_job(job);
768
job_conclude(job);
769
return 0;
770
}
771
772
+void job_cancel_async(Job *job, bool force)
773
+{
121
+{
774
+ if (job->user_paused) {
122
+ test_drv_cb_common(BDRV_DRAIN, false);
775
+ /* Do not call job_enter here, the caller will handle it. */
776
+ job->user_paused = false;
777
+ if (job->driver->user_resume) {
778
+ job->driver->user_resume(job);
779
+ }
780
+ assert(job->pause_count > 0);
781
+ job->pause_count--;
782
+ }
783
+ job->cancelled = true;
784
+ /* To prevent 'force == false' overriding a previous 'force == true' */
785
+ job->force_cancel |= force;
786
+}
123
+}
787
+
124
+
788
+void job_completed_txn_abort(Job *job)
125
int main(int argc, char **argv)
789
+{
790
+ AioContext *ctx;
791
+ JobTxn *txn = job->txn;
792
+ Job *other_job;
793
+
794
+ if (txn->aborting) {
795
+ /*
796
+ * We are cancelled by another job, which will handle everything.
797
+ */
798
+ return;
799
+ }
800
+ txn->aborting = true;
801
+ job_txn_ref(txn);
802
+
803
+ /* We are the first failed job. Cancel other jobs. */
804
+ QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
805
+ ctx = other_job->aio_context;
806
+ aio_context_acquire(ctx);
807
+ }
808
+
809
+ /* Other jobs are effectively cancelled by us, set the status for
810
+ * them; this job, however, may or may not be cancelled, depending
811
+ * on the caller, so leave it. */
812
+ QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
813
+ if (other_job != job) {
814
+ job_cancel_async(other_job, false);
815
+ }
816
+ }
817
+ while (!QLIST_EMPTY(&txn->jobs)) {
818
+ other_job = QLIST_FIRST(&txn->jobs);
819
+ ctx = other_job->aio_context;
820
+ if (!job_is_completed(other_job)) {
821
+ assert(job_is_cancelled(other_job));
822
+ job_finish_sync(other_job, NULL, NULL);
823
+ }
824
+ job_finalize_single(other_job);
825
+ aio_context_release(ctx);
826
+ }
827
+
828
+ job_txn_unref(txn);
829
+}
830
+
831
+static int job_prepare(Job *job)
832
+{
833
+ if (job->ret == 0 && job->driver->prepare) {
834
+ job->ret = job->driver->prepare(job);
835
+ }
836
+ return job->ret;
837
+}
838
+
839
+static int job_needs_finalize(Job *job)
840
+{
841
+ return !job->auto_finalize;
842
+}
843
+
844
+static void job_do_finalize(Job *job)
845
+{
846
+ int rc;
847
+ assert(job && job->txn);
848
+
849
+ /* prepare the transaction to complete */
850
+ rc = job_txn_apply(job->txn, job_prepare, true);
851
+ if (rc) {
852
+ job_completed_txn_abort(job);
853
+ } else {
854
+ job_txn_apply(job->txn, job_finalize_single, true);
855
+ }
856
+}
857
+
858
+void job_finalize(Job *job, Error **errp)
859
+{
860
+ assert(job && job->id);
861
+ if (job_apply_verb(job, JOB_VERB_FINALIZE, errp)) {
862
+ return;
863
+ }
864
+ job_do_finalize(job);
865
+}
866
+
867
+static int job_transition_to_pending(Job *job)
868
+{
869
+ job_state_transition(job, JOB_STATUS_PENDING);
870
+ if (!job->auto_finalize) {
871
+ job_event_pending(job);
872
+ }
873
+ return 0;
874
+}
875
+
876
+void job_completed_txn_success(Job *job)
877
+{
878
+ JobTxn *txn = job->txn;
879
+ Job *other_job;
880
+
881
+ job_state_transition(job, JOB_STATUS_WAITING);
882
+
883
+ /*
884
+ * Successful completion, see if there are other running jobs in this
885
+ * txn.
886
+ */
887
+ QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
888
+ if (!job_is_completed(other_job)) {
889
+ return;
890
+ }
891
+ assert(other_job->ret == 0);
892
+ }
893
+
894
+ job_txn_apply(txn, job_transition_to_pending, false);
895
+
896
+ /* If no jobs need manual finalization, automatically do so */
897
+ if (job_txn_apply(txn, job_needs_finalize, false) == 0) {
898
+ job_do_finalize(job);
899
+ }
900
+}
901
+
902
void job_complete(Job *job, Error **errp)
903
{
126
{
904
/* Should not be reachable via external interface for internal jobs */
127
bdrv_init();
905
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
906
index XXXXXXX..XXXXXXX 100644
129
g_test_init(&argc, &argv, NULL);
907
--- a/tests/test-blockjob-txn.c
130
908
+++ b/tests/test-blockjob-txn.c
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
909
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
910
JobTxn *txn;
133
911
int result = -EINPROGRESS;
134
return g_test_run();
912
913
- txn = block_job_txn_new();
914
+ txn = job_txn_new();
915
job = test_block_job_start(1, true, expected, &result, txn);
916
job_start(&job->job);
917
918
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
919
}
920
g_assert_cmpint(result, ==, expected);
921
922
- block_job_txn_unref(txn);
923
+ job_txn_unref(txn);
924
}
135
}
925
926
static void test_single_job_success(void)
927
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
928
int result1 = -EINPROGRESS;
929
int result2 = -EINPROGRESS;
930
931
- txn = block_job_txn_new();
932
+ txn = job_txn_new();
933
job1 = test_block_job_start(1, true, expected1, &result1, txn);
934
job2 = test_block_job_start(2, true, expected2, &result2, txn);
935
job_start(&job1->job);
936
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
937
/* Release our reference now to trigger as many nice
938
* use-after-free bugs as possible.
939
*/
940
- block_job_txn_unref(txn);
941
+ job_txn_unref(txn);
942
943
if (expected1 == -ECANCELED) {
944
block_job_cancel(job1, false);
945
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
946
int result1 = -EINPROGRESS;
947
int result2 = -EINPROGRESS;
948
949
- txn = block_job_txn_new();
950
+ txn = job_txn_new();
951
job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
952
job2 = test_block_job_start(2, false, 0, &result2, txn);
953
job_start(&job1->job);
954
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
955
g_assert_cmpint(result1, ==, -ECANCELED);
956
g_assert_cmpint(result2, ==, -ECANCELED);
957
958
- block_job_txn_unref(txn);
959
+ job_txn_unref(txn);
960
}
961
962
int main(int argc, char **argv)
963
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
964
index XXXXXXX..XXXXXXX 100644
965
--- a/tests/test-blockjob.c
966
+++ b/tests/test-blockjob.c
967
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
968
}
969
assert(job->job.status == JOB_STATUS_PENDING);
970
971
- block_job_finalize(job, &error_abort);
972
+ job_finalize(&job->job, &error_abort);
973
assert(job->job.status == JOB_STATUS_CONCLUDED);
974
975
cancel_common(s);
976
--
136
--
977
2.13.6
137
2.13.6
978
138
979
139
diff view generated by jsdifflib
1
This moves the job list from BlockJob to Job. Now we can check for
1
This is currently only working correctly for bdrv_drain(), not for
2
duplicate IDs in job_create().
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
---
6
---
8
include/block/blockjob.h | 3 ---
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
9
include/qemu/job.h | 19 +++++++++++++++++++
8
1 file changed, 45 insertions(+)
10
blockjob.c | 46 ++++++++++++++++++++++++----------------------
11
job.c | 31 +++++++++++++++++++++++++++++++
12
4 files changed, 74 insertions(+), 25 deletions(-)
13
9
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
12
--- a/tests/test-bdrv-drain.c
17
+++ b/include/block/blockjob.h
13
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
19
*/
15
test_drv_cb_common(BDRV_DRAIN, false);
20
bool deferred_to_main_loop;
16
}
21
17
22
- /** Element of the list of block jobs */
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
23
- QLIST_ENTRY(BlockJob) job_list;
19
+{
24
-
20
+ BlockBackend *blk;
25
/** Status that is published by the query-block-jobs QMP API */
21
+ BlockDriverState *bs, *backing;
26
BlockDeviceIoStatus iostatus;
27
28
diff --git a/include/qemu/job.h b/include/qemu/job.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/include/qemu/job.h
31
+++ b/include/qemu/job.h
32
@@ -XXX,XX +XXX,XX @@
33
#define JOB_H
34
35
#include "qapi/qapi-types-block-core.h"
36
+#include "qemu/queue.h"
37
38
typedef struct JobDriver JobDriver;
39
40
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
41
42
/** The type of this job. */
43
const JobDriver *driver;
44
+
22
+
45
+ /** Element of the list of jobs */
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
46
+ QLIST_ENTRY(Job) job_list;
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
47
} Job;
25
+ &error_abort);
48
26
+ blk_insert_bs(blk, bs, &error_abort);
49
/**
50
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
51
/** Returns the enum string for the JobType of a given Job. */
52
const char *job_type_str(const Job *job);
53
54
+/**
55
+ * Get the next element from the list of block jobs after @job, or the
56
+ * first one if @job is %NULL.
57
+ *
58
+ * Returns the requested job, or %NULL if there are no more jobs left.
59
+ */
60
+Job *job_next(Job *job);
61
+
27
+
62
+/**
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
63
+ * Get the job identified by @id (which must not be %NULL).
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
64
+ *
65
+ * Returns the requested job, or %NULL if it doesn't exist.
66
+ */
67
+Job *job_get(const char *id);
68
+
30
+
69
#endif
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
70
diff --git a/blockjob.c b/blockjob.c
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
71
index XXXXXXX..XXXXXXX 100644
33
+
72
--- a/blockjob.c
34
+ do_drain_begin(drain_type, bs);
73
+++ b/blockjob.c
35
+
74
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
75
int refcnt;
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
76
};
38
+
77
39
+ do_drain_end(drain_type, bs);
78
-static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
40
+
79
-
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
80
/*
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
81
* The block job API is composed of two categories of functions.
43
+
82
*
44
+ bdrv_unref(backing);
83
@@ -XXX,XX +XXX,XX @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
45
+ bdrv_unref(bs);
84
* blockjob_int.h.
46
+ blk_unref(blk);
85
*/
86
87
-BlockJob *block_job_next(BlockJob *job)
88
+static bool is_block_job(Job *job)
89
{
90
- if (!job) {
91
- return QLIST_FIRST(&block_jobs);
92
- }
93
- return QLIST_NEXT(job, job_list);
94
+ return job_type(job) == JOB_TYPE_BACKUP ||
95
+ job_type(job) == JOB_TYPE_COMMIT ||
96
+ job_type(job) == JOB_TYPE_MIRROR ||
97
+ job_type(job) == JOB_TYPE_STREAM;
98
+}
47
+}
99
+
48
+
100
+BlockJob *block_job_next(BlockJob *bjob)
49
+static void test_quiesce_drain_all(void)
101
+{
50
+{
102
+ Job *job = bjob ? &bjob->job : NULL;
51
+ // XXX drain_all doesn't quiesce
103
+
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
104
+ do {
105
+ job = job_next(job);
106
+ } while (job && !is_block_job(job));
107
+
108
+ return job ? container_of(job, BlockJob, job) : NULL;
109
}
110
111
BlockJob *block_job_get(const char *id)
112
{
113
- BlockJob *job;
114
+ Job *job = job_get(id);
115
116
- QLIST_FOREACH(job, &block_jobs, job_list) {
117
- if (job->job.id && !strcmp(id, job->job.id)) {
118
- return job;
119
- }
120
+ if (job && is_block_job(job)) {
121
+ return container_of(job, BlockJob, job);
122
+ } else {
123
+ return NULL;
124
}
125
-
126
- return NULL;
127
}
128
129
BlockJobTxn *block_job_txn_new(void)
130
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
131
assert(job->status == BLOCK_JOB_STATUS_NULL);
132
assert(!job->txn);
133
BlockDriverState *bs = blk_bs(job->blk);
134
- QLIST_REMOVE(job, job_list);
135
bs->job = NULL;
136
block_job_remove_all_bdrv(job);
137
blk_remove_aio_context_notifier(job->blk,
138
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
139
BlockJob *job;
140
AioContext *aio_context;
141
142
- while ((job = QLIST_FIRST(&block_jobs))) {
143
+ while ((job = block_job_next(NULL))) {
144
aio_context = blk_get_aio_context(job->blk);
145
aio_context_acquire(aio_context);
146
block_job_cancel_sync(job);
147
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
148
error_setg(errp, "Cannot specify job ID for internal block job");
149
return NULL;
150
}
151
- if (block_job_get(job_id)) {
152
- error_setg(errp, "Job ID '%s' already in use", job_id);
153
- return NULL;
154
- }
155
}
156
157
blk = blk_new(perm, shared_perm);
158
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
159
return NULL;
160
}
161
162
+ assert(is_block_job(&job->job));
163
+
164
job->driver = driver;
165
job->blk = blk;
166
job->cb = cb;
167
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
168
169
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
170
171
- QLIST_INSERT_HEAD(&block_jobs, job, job_list);
172
-
173
blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
174
block_job_detach_aio_context, job);
175
176
diff --git a/job.c b/job.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/job.c
179
+++ b/job.c
180
@@ -XXX,XX +XXX,XX @@
181
#include "qemu/job.h"
182
#include "qemu/id.h"
183
184
+static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
185
+
186
JobType job_type(const Job *job)
187
{
188
return job->driver->job_type;
189
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job)
190
return JobType_str(job_type(job));
191
}
192
193
+Job *job_next(Job *job)
194
+{
195
+ if (!job) {
196
+ return QLIST_FIRST(&jobs);
197
+ }
198
+ return QLIST_NEXT(job, job_list);
199
+}
53
+}
200
+
54
+
201
+Job *job_get(const char *id)
55
+static void test_quiesce_drain(void)
202
+{
56
+{
203
+ Job *job;
57
+ test_quiesce_common(BDRV_DRAIN, false);
204
+
205
+ QLIST_FOREACH(job, &jobs, job_list) {
206
+ if (job->id && !strcmp(id, job->id)) {
207
+ return job;
208
+ }
209
+ }
210
+
211
+ return NULL;
212
+}
58
+}
213
+
59
+
214
void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
60
int main(int argc, char **argv)
215
{
61
{
216
Job *job;
62
bdrv_init();
217
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
218
error_setg(errp, "Invalid job ID '%s'", job_id);
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
219
return NULL;
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
220
}
66
221
+ if (job_get(job_id)) {
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
222
+ error_setg(errp, "Job ID '%s' already in use", job_id);
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
223
+ return NULL;
224
+ }
225
}
226
227
job = g_malloc0(driver->instance_size);
228
job->driver = driver;
229
job->id = g_strdup(job_id);
230
231
+ QLIST_INSERT_HEAD(&jobs, job, job_list);
232
+
69
+
233
return job;
70
return g_test_run();
234
}
235
236
void job_delete(Job *job)
237
{
238
+ QLIST_REMOVE(job, job_list);
239
+
240
g_free(job->id);
241
g_free(job);
242
}
71
}
243
--
72
--
244
2.13.6
73
2.13.6
245
74
246
75
diff view generated by jsdifflib
1
QAPI types aren't externally visible, so we can rename them without
1
Block jobs already paused themselves when their main BlockBackend
2
causing problems. Before we add a job type to Job, rename the enum
2
entered a drained section. This is not good enough: We also want to
3
so it can be used for more than just block jobs.
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
5
6
This implements .drained_begin/end callbacks in child_job in order to
7
consider all block nodes related to the job, and removes the
8
BlockBackend callbacks which are unnecessary now because the root of the
9
job main BlockBackend is always referenced with a child_job, too.
4
10
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
---
12
---
10
qapi/block-core.json | 14 +++++++-------
13
blockjob.c | 22 +++++++++-------------
11
include/block/blockjob_int.h | 2 +-
14
1 file changed, 9 insertions(+), 13 deletions(-)
12
block/backup.c | 2 +-
13
block/commit.c | 2 +-
14
block/mirror.c | 4 ++--
15
block/stream.c | 2 +-
16
blockjob.c | 6 +++---
17
7 files changed, 16 insertions(+), 16 deletions(-)
18
15
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
22
+++ b/qapi/block-core.json
23
@@ -XXX,XX +XXX,XX @@
24
'data': ['top', 'full', 'none', 'incremental'] }
25
26
##
27
-# @BlockJobType:
28
+# @JobType:
29
#
30
-# Type of a block job.
31
+# Type of a background job.
32
#
33
# @commit: block commit job type, see "block-commit"
34
#
35
@@ -XXX,XX +XXX,XX @@
36
#
37
# Since: 1.7
38
##
39
-{ 'enum': 'BlockJobType',
40
+{ 'enum': 'JobType',
41
'data': ['commit', 'stream', 'mirror', 'backup'] }
42
43
##
44
@@ -XXX,XX +XXX,XX @@
45
#
46
##
47
{ 'event': 'BLOCK_JOB_COMPLETED',
48
- 'data': { 'type' : 'BlockJobType',
49
+ 'data': { 'type' : 'JobType',
50
'device': 'str',
51
'len' : 'int',
52
'offset': 'int',
53
@@ -XXX,XX +XXX,XX @@
54
#
55
##
56
{ 'event': 'BLOCK_JOB_CANCELLED',
57
- 'data': { 'type' : 'BlockJobType',
58
+ 'data': { 'type' : 'JobType',
59
'device': 'str',
60
'len' : 'int',
61
'offset': 'int',
62
@@ -XXX,XX +XXX,XX @@
63
#
64
##
65
{ 'event': 'BLOCK_JOB_READY',
66
- 'data': { 'type' : 'BlockJobType',
67
+ 'data': { 'type' : 'JobType',
68
'device': 'str',
69
'len' : 'int',
70
'offset': 'int',
71
@@ -XXX,XX +XXX,XX @@
72
#
73
##
74
{ 'event': 'BLOCK_JOB_PENDING',
75
- 'data': { 'type' : 'BlockJobType',
76
+ 'data': { 'type' : 'JobType',
77
'id' : 'str' } }
78
79
##
80
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
81
index XXXXXXX..XXXXXXX 100644
82
--- a/include/block/blockjob_int.h
83
+++ b/include/block/blockjob_int.h
84
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
85
JobDriver job_driver;
86
87
/** String describing the operation, part of query-block-jobs QMP API */
88
- BlockJobType job_type;
89
+ JobType job_type;
90
91
/** Mandatory: Entrypoint for the Coroutine. */
92
CoroutineEntry *start;
93
diff --git a/block/backup.c b/block/backup.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/backup.c
96
+++ b/block/backup.c
97
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
98
.job_driver = {
99
.instance_size = sizeof(BackupBlockJob),
100
},
101
- .job_type = BLOCK_JOB_TYPE_BACKUP,
102
+ .job_type = JOB_TYPE_BACKUP,
103
.start = backup_run,
104
.commit = backup_commit,
105
.abort = backup_abort,
106
diff --git a/block/commit.c b/block/commit.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/block/commit.c
109
+++ b/block/commit.c
110
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
111
.job_driver = {
112
.instance_size = sizeof(CommitBlockJob),
113
},
114
- .job_type = BLOCK_JOB_TYPE_COMMIT,
115
+ .job_type = JOB_TYPE_COMMIT,
116
.start = commit_run,
117
};
118
119
diff --git a/block/mirror.c b/block/mirror.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/block/mirror.c
122
+++ b/block/mirror.c
123
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
124
.job_driver = {
125
.instance_size = sizeof(MirrorBlockJob),
126
},
127
- .job_type = BLOCK_JOB_TYPE_MIRROR,
128
+ .job_type = JOB_TYPE_MIRROR,
129
.start = mirror_run,
130
.complete = mirror_complete,
131
.pause = mirror_pause,
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
133
.job_driver = {
134
.instance_size = sizeof(MirrorBlockJob),
135
},
136
- .job_type = BLOCK_JOB_TYPE_COMMIT,
137
+ .job_type = JOB_TYPE_COMMIT,
138
.start = mirror_run,
139
.complete = mirror_complete,
140
.pause = mirror_pause,
141
diff --git a/block/stream.c b/block/stream.c
142
index XXXXXXX..XXXXXXX 100644
143
--- a/block/stream.c
144
+++ b/block/stream.c
145
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
146
.job_driver = {
147
.instance_size = sizeof(StreamBlockJob),
148
},
149
- .job_type = BLOCK_JOB_TYPE_STREAM,
150
+ .job_type = JOB_TYPE_STREAM,
151
.start = stream_run,
152
};
153
154
diff --git a/blockjob.c b/blockjob.c
16
diff --git a/blockjob.c b/blockjob.c
155
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
156
--- a/blockjob.c
18
--- a/blockjob.c
157
+++ b/blockjob.c
19
+++ b/blockjob.c
158
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
21
job->id);
22
}
23
24
-static const BdrvChildRole child_job = {
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
159
{
31
{
160
BlockJob *job = c->opaque;
32
- BlockJob *job = opaque;
161
return g_strdup_printf("%s job '%s'",
33
+ BlockJob *job = c->opaque;
162
- BlockJobType_str(job->driver->job_type),
34
block_job_pause(job);
163
+ JobType_str(job->driver->job_type),
164
job->job.id);
165
}
35
}
166
36
167
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
37
-static void block_job_drained_end(void *opaque)
168
return NULL;
38
+static void child_job_drained_end(BdrvChild *c)
169
}
39
{
170
info = g_new0(BlockJobInfo, 1);
40
- BlockJob *job = opaque;
171
- info->type = g_strdup(BlockJobType_str(job->driver->job_type));
41
+ BlockJob *job = c->opaque;
172
+ info->type = g_strdup(JobType_str(job->driver->job_type));
42
block_job_resume(job);
173
info->device = g_strdup(job->job.id);
43
}
174
info->len = job->len;
44
175
info->busy = atomic_read(&job->busy);
45
-static const BlockDevOps block_job_dev_ops = {
46
- .drained_begin = block_job_drained_begin,
47
- .drained_end = block_job_drained_end,
48
+static const BdrvChildRole child_job = {
49
+ .get_parent_desc = child_job_get_parent_desc,
50
+ .drained_begin = child_job_drained_begin,
51
+ .drained_end = child_job_drained_end,
52
+ .stay_at_node = true,
53
};
54
55
void block_job_remove_all_bdrv(BlockJob *job)
176
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
177
block_job_sleep_timer_cb, job);
178
179
error_setg(&job->blocker, "block device is in use by block job: %s",
180
- BlockJobType_str(driver->job_type));
181
+ JobType_str(driver->job_type));
182
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
183
bs->job = job;
58
bs->job = job;
184
59
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
62
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
185
--
64
--
186
2.13.6
65
2.13.6
187
66
188
67
diff view generated by jsdifflib
1
This adds a minimal query-jobs implementation that shouldn't pose many
1
Block jobs must be paused if any of the involved nodes are drained.
2
design questions. It can later be extended to expose more information,
3
and especially job-specific information.
4
2
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
4
---
7
qapi/job.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
8
include/qemu/job.h | 3 +++
6
1 file changed, 121 insertions(+)
9
job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
job.c | 2 +-
11
4 files changed, 104 insertions(+), 1 deletion(-)
12
7
13
diff --git a/qapi/job.json b/qapi/job.json
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
14
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
15
--- a/qapi/job.json
10
--- a/tests/test-bdrv-drain.c
16
+++ b/qapi/job.json
11
+++ b/tests/test-bdrv-drain.c
17
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@
18
# Since: 2.13
13
19
##
14
#include "qemu/osdep.h"
20
{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
15
#include "block/block.h"
16
+#include "block/blockjob_int.h"
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
19
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
test_quiesce_common(BDRV_DRAIN, false);
22
}
23
21
+
24
+
22
+##
25
+typedef struct TestBlockJob {
23
+# @JobInfo:
26
+ BlockJob common;
24
+#
27
+ bool should_complete;
25
+# Information about a job.
28
+} TestBlockJob;
26
+#
27
+# @id: The job identifier
28
+#
29
+# @type: The kind of job that is being performed
30
+#
31
+# @status: Current job state/status
32
+#
33
+# @current-progress: Progress made until now. The unit is arbitrary and the
34
+# value can only meaningfully be used for the ratio of
35
+# @current-progress to @total-progress. The value is
36
+# monotonically increasing.
37
+#
38
+# @total-progress: Estimated @current-progress value at the completion of
39
+# the job. This value can arbitrarily change while the
40
+# job is running, in both directions.
41
+#
42
+# @error: If this field is present, the job failed; if it is
43
+# still missing in the CONCLUDED state, this indicates
44
+# successful completion.
45
+#
46
+# The value is a human-readable error message to describe
47
+# the reason for the job failure. It should not be parsed
48
+# by applications.
49
+#
50
+# Since: 2.13
51
+##
52
+{ 'struct': 'JobInfo',
53
+ 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus',
54
+ 'current-progress': 'int', 'total-progress': 'int',
55
+ '*error': 'str' } }
56
+
29
+
57
+##
30
+static void test_job_completed(BlockJob *job, void *opaque)
58
+# @query-jobs:
31
+{
59
+#
32
+ block_job_completed(job, 0);
60
+# Return information about jobs.
33
+}
61
+#
62
+# Returns: a list with a @JobInfo for each active job
63
+#
64
+# Since: 2.13
65
+##
66
+{ 'command': 'query-jobs', 'returns': ['JobInfo'] }
67
diff --git a/include/qemu/job.h b/include/qemu/job.h
68
index XXXXXXX..XXXXXXX 100644
69
--- a/include/qemu/job.h
70
+++ b/include/qemu/job.h
71
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
72
/** Returns the enum string for the JobType of a given Job. */
73
const char *job_type_str(const Job *job);
74
75
+/** Returns true if the job should not be visible to the management layer. */
76
+bool job_is_internal(Job *job);
77
+
34
+
78
/** Returns whether the job is scheduled for cancellation. */
35
+static void coroutine_fn test_job_start(void *opaque)
79
bool job_is_cancelled(Job *job);
36
+{
80
37
+ TestBlockJob *s = opaque;
81
diff --git a/job-qmp.c b/job-qmp.c
82
index XXXXXXX..XXXXXXX 100644
83
--- a/job-qmp.c
84
+++ b/job-qmp.c
85
@@ -XXX,XX +XXX,XX @@ void qmp_job_dismiss(const char *id, Error **errp)
86
job_dismiss(&job, errp);
87
aio_context_release(aio_context);
88
}
89
+
38
+
90
+static JobInfo *job_query_single(Job *job, Error **errp)
39
+ while (!s->should_complete) {
91
+{
40
+ block_job_sleep_ns(&s->common, 100000);
92
+ JobInfo *info;
93
+ const char *errmsg = NULL;
94
+
95
+ assert(!job_is_internal(job));
96
+
97
+ if (job->ret < 0) {
98
+ errmsg = strerror(-job->ret);
99
+ }
41
+ }
100
+
42
+
101
+ info = g_new(JobInfo, 1);
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
102
+ *info = (JobInfo) {
103
+ .id = g_strdup(job->id),
104
+ .type = job_type(job),
105
+ .status = job->status,
106
+ .current_progress = job->progress_current,
107
+ .total_progress = job->progress_total,
108
+ .has_error = !!errmsg,
109
+ .error = g_strdup(errmsg),
110
+ };
111
+
112
+ return info;
113
+}
44
+}
114
+
45
+
115
+JobInfoList *qmp_query_jobs(Error **errp)
46
+static void test_job_complete(BlockJob *job, Error **errp)
116
+{
47
+{
117
+ JobInfoList *head = NULL, **p_next = &head;
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
118
+ Job *job;
49
+ s->should_complete = true;
50
+}
119
+
51
+
120
+ for (job = job_next(NULL); job; job = job_next(job)) {
52
+BlockJobDriver test_job_driver = {
121
+ JobInfoList *elem;
53
+ .instance_size = sizeof(TestBlockJob),
122
+ AioContext *aio_context;
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
56
+};
123
+
57
+
124
+ if (job_is_internal(job)) {
58
+static void test_blockjob_common(enum drain_type drain_type)
125
+ continue;
59
+{
126
+ }
60
+ BlockBackend *blk_src, *blk_target;
127
+ elem = g_new0(JobInfoList, 1);
61
+ BlockDriverState *src, *target;
128
+ aio_context = job->aio_context;
62
+ BlockJob *job;
129
+ aio_context_acquire(aio_context);
63
+ int ret;
130
+ elem->value = job_query_single(job, errp);
64
+
131
+ aio_context_release(aio_context);
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
132
+ if (!elem->value) {
66
+ &error_abort);
133
+ g_free(elem);
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
134
+ qapi_free_JobInfoList(head);
68
+ blk_insert_bs(blk_src, src, &error_abort);
135
+ return NULL;
69
+
136
+ }
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
137
+ *p_next = elem;
71
+ &error_abort);
138
+ p_next = &elem->next;
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
73
+ blk_insert_bs(blk_target, target, &error_abort);
74
+
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
79
+
80
+ g_assert_cmpint(job->pause_count, ==, 0);
81
+ g_assert_false(job->paused);
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
83
+
84
+ do_drain_begin(drain_type, src);
85
+
86
+ if (drain_type == BDRV_DRAIN_ALL) {
87
+ /* bdrv_drain_all() drains both src and target, and involves an
88
+ * additional block_job_pause_all() */
89
+ g_assert_cmpint(job->pause_count, ==, 3);
90
+ } else {
91
+ g_assert_cmpint(job->pause_count, ==, 1);
139
+ }
92
+ }
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
140
+
96
+
141
+ return head;
97
+ do_drain_end(drain_type, src);
98
+
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
142
+}
129
+}
143
diff --git a/job.c b/job.c
130
+
144
index XXXXXXX..XXXXXXX 100644
131
+static void test_blockjob_drain_all(void)
145
--- a/job.c
132
+{
146
+++ b/job.c
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
147
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
134
+}
148
return rc;
135
+
149
}
136
+static void test_blockjob_drain(void)
150
137
+{
151
-static bool job_is_internal(Job *job)
138
+ test_blockjob_common(BDRV_DRAIN);
152
+bool job_is_internal(Job *job)
139
+}
140
+
141
int main(int argc, char **argv)
153
{
142
{
154
return (job->id == NULL);
143
bdrv_init();
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
147
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
150
+
151
return g_test_run();
155
}
152
}
156
--
153
--
157
2.13.6
154
2.13.6
158
155
159
156
diff view generated by jsdifflib
1
This commit moves some core functions for dealing with the job coroutine
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
from BlockJob to Job. This includes primarily entering the coroutine
2
so we don't need an additional block_job_pause_all() call.
3
(both for the first and reentering) and yielding explicitly and at pause
4
points.
5
3
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
5
---
9
include/block/blockjob.h | 40 --------
6
block/io.c | 4 ----
10
include/block/blockjob_int.h | 26 -----
7
tests/test-bdrv-drain.c | 10 ++++------
11
include/qemu/job.h | 76 +++++++++++++++
8
2 files changed, 4 insertions(+), 10 deletions(-)
12
block/backup.c | 2 +-
13
block/commit.c | 4 +-
14
block/mirror.c | 22 ++---
15
block/replication.c | 2 +-
16
block/stream.c | 4 +-
17
blockdev.c | 8 +-
18
blockjob.c | 219 ++++++++-----------------------------------
19
job.c | 137 +++++++++++++++++++++++++++
20
tests/test-bdrv-drain.c | 38 ++++----
21
tests/test-blockjob-txn.c | 12 +--
22
tests/test-blockjob.c | 14 +--
23
14 files changed, 305 insertions(+), 299 deletions(-)
24
9
25
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
10
diff --git a/block/io.c b/block/io.c
26
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/blockjob.h
12
--- a/block/io.c
28
+++ b/include/block/blockjob.h
13
+++ b/block/io.c
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
30
BlockBackend *blk;
15
* context. */
31
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
32
/**
17
33
- * The coroutine that executes the job. If not NULL, it is
18
- block_job_pause_all();
34
- * reentered when busy is false and the job is cancelled.
35
- */
36
- Coroutine *co;
37
-
19
-
38
- /**
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
39
* Set to true if the job should abort immediately without waiting
21
AioContext *aio_context = bdrv_get_aio_context(bs);
40
* for data to be in sync.
22
41
*/
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
42
bool force;
24
aio_enable_external(aio_context);
43
25
aio_context_release(aio_context);
44
/**
26
}
45
- * Counter for pause request. If non-zero, the block job is either paused,
46
- * or if busy == true will pause itself as soon as possible.
47
- */
48
- int pause_count;
49
-
27
-
50
- /**
28
- block_job_resume_all();
51
* Set to true if the job is paused by user. Can be unpaused with the
52
* block-job-resume QMP command.
53
*/
54
bool user_paused;
55
56
/**
57
- * Set to false by the job while the coroutine has yielded and may be
58
- * re-entered by block_job_enter(). There may still be I/O or event loop
59
- * activity pending. Accessed under block_job_mutex (in blockjob.c).
60
- */
61
- bool busy;
62
-
63
- /**
64
- * Set to true by the job while it is in a quiescent state, where
65
- * no I/O or event loop activity is pending.
66
- */
67
- bool paused;
68
-
69
- /**
70
* Set to true when the job is ready to be completed.
71
*/
72
bool ready;
73
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
74
/** ret code passed to block_job_completed. */
75
int ret;
76
77
- /**
78
- * Timer that is used by @block_job_sleep_ns. Accessed under
79
- * block_job_mutex (in blockjob.c).
80
- */
81
- QEMUTimer sleep_timer;
82
-
83
/** True if this job should automatically finalize itself */
84
bool auto_finalize;
85
86
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
87
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
88
89
/**
90
- * block_job_start:
91
- * @job: A job that has not yet been started.
92
- *
93
- * Begins execution of a block job.
94
- * Takes ownership of one reference to the job object.
95
- */
96
-void block_job_start(BlockJob *job);
97
-
98
-/**
99
* block_job_cancel:
100
* @job: The job to be canceled.
101
* @force: Quit a job without waiting for data to be in sync.
102
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
103
index XXXXXXX..XXXXXXX 100644
104
--- a/include/block/blockjob_int.h
105
+++ b/include/block/blockjob_int.h
106
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
107
/** Generic JobDriver callbacks and settings */
108
JobDriver job_driver;
109
110
- /** Mandatory: Entrypoint for the Coroutine. */
111
- CoroutineEntry *start;
112
-
113
/**
114
* Optional callback for job types whose completion must be triggered
115
* manually.
116
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
117
*/
118
void (*clean)(BlockJob *job);
119
120
- /**
121
- * If the callback is not NULL, it will be invoked when the job transitions
122
- * into the paused state. Paused jobs must not perform any asynchronous
123
- * I/O or event loop activity. This callback is used to quiesce jobs.
124
- */
125
- void coroutine_fn (*pause)(BlockJob *job);
126
-
127
- /**
128
- * If the callback is not NULL, it will be invoked when the job transitions
129
- * out of the paused state. Any asynchronous I/O or event loop activity
130
- * should be restarted from this callback.
131
- */
132
- void coroutine_fn (*resume)(BlockJob *job);
133
-
134
/*
135
* If the callback is not NULL, it will be invoked before the job is
136
* resumed in a new AioContext. This is the place to move any resources
137
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job);
138
void block_job_completed(BlockJob *job, int ret);
139
140
/**
141
- * block_job_pause_point:
142
- * @job: The job that is ready to pause.
143
- *
144
- * Pause now if block_job_pause() has been called. Block jobs that perform
145
- * lots of I/O must call this between requests so that the job can be paused.
146
- */
147
-void coroutine_fn block_job_pause_point(BlockJob *job);
148
-
149
-/**
150
* block_job_enter:
151
* @job: The job to enter.
152
*
153
diff --git a/include/qemu/job.h b/include/qemu/job.h
154
index XXXXXXX..XXXXXXX 100644
155
--- a/include/qemu/job.h
156
+++ b/include/qemu/job.h
157
@@ -XXX,XX +XXX,XX @@
158
159
#include "qapi/qapi-types-block-core.h"
160
#include "qemu/queue.h"
161
+#include "qemu/coroutine.h"
162
163
typedef struct JobDriver JobDriver;
164
165
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
166
AioContext *aio_context;
167
168
/**
169
+ * The coroutine that executes the job. If not NULL, it is reentered when
170
+ * busy is false and the job is cancelled.
171
+ */
172
+ Coroutine *co;
173
+
174
+ /**
175
+ * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in
176
+ * job.c).
177
+ */
178
+ QEMUTimer sleep_timer;
179
+
180
+ /**
181
+ * Counter for pause request. If non-zero, the block job is either paused,
182
+ * or if busy == true will pause itself as soon as possible.
183
+ */
184
+ int pause_count;
185
+
186
+ /**
187
+ * Set to false by the job while the coroutine has yielded and may be
188
+ * re-entered by block_job_enter(). There may still be I/O or event loop
189
+ * activity pending. Accessed under block_job_mutex (in blockjob.c).
190
+ */
191
+ bool busy;
192
+
193
+ /**
194
+ * Set to true by the job while it is in a quiescent state, where
195
+ * no I/O or event loop activity is pending.
196
+ */
197
+ bool paused;
198
+
199
+ /**
200
* Set to true if the job should cancel itself. The flag must
201
* always be tested just before toggling the busy flag from false
202
* to true. After a job has been cancelled, it should only yield
203
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
204
/** Enum describing the operation */
205
JobType job_type;
206
207
+ /** Mandatory: Entrypoint for the Coroutine. */
208
+ CoroutineEntry *start;
209
+
210
+ /**
211
+ * If the callback is not NULL, it will be invoked when the job transitions
212
+ * into the paused state. Paused jobs must not perform any asynchronous
213
+ * I/O or event loop activity. This callback is used to quiesce jobs.
214
+ */
215
+ void coroutine_fn (*pause)(Job *job);
216
+
217
+ /**
218
+ * If the callback is not NULL, it will be invoked when the job transitions
219
+ * out of the paused state. Any asynchronous I/O or event loop activity
220
+ * should be restarted from this callback.
221
+ */
222
+ void coroutine_fn (*resume)(Job *job);
223
+
224
/** Called when the job is freed */
225
void (*free)(Job *job);
226
};
227
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
228
*/
229
void job_unref(Job *job);
230
231
+/**
232
+ * Conditionally enter the job coroutine if the job is ready to run, not
233
+ * already busy and fn() returns true. fn() is called while under the job_lock
234
+ * critical section.
235
+ */
236
+void job_enter_cond(Job *job, bool(*fn)(Job *job));
237
+
238
+/**
239
+ * @job: A job that has not yet been started.
240
+ *
241
+ * Begins execution of a job.
242
+ * Takes ownership of one reference to the job object.
243
+ */
244
+void job_start(Job *job);
245
+
246
+/**
247
+ * @job: The job that is ready to pause.
248
+ *
249
+ * Pause now if job_pause() has been called. Jobs that perform lots of I/O
250
+ * must call this between requests so that the job can be paused.
251
+ */
252
+void coroutine_fn job_pause_point(Job *job);
253
+
254
+
255
/** Returns the JobType of a given Job. */
256
JobType job_type(const Job *job);
257
258
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
259
260
/* TODO To be removed from the public interface */
261
void job_state_transition(Job *job, JobStatus s1);
262
+void coroutine_fn job_do_yield(Job *job, uint64_t ns);
263
+bool job_should_pause(Job *job);
264
+bool job_started(Job *job);
265
266
#endif
267
diff --git a/block/backup.c b/block/backup.c
268
index XXXXXXX..XXXXXXX 100644
269
--- a/block/backup.c
270
+++ b/block/backup.c
271
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
272
.instance_size = sizeof(BackupBlockJob),
273
.job_type = JOB_TYPE_BACKUP,
274
.free = block_job_free,
275
+ .start = backup_run,
276
},
277
- .start = backup_run,
278
.commit = backup_commit,
279
.abort = backup_abort,
280
.clean = backup_clean,
281
diff --git a/block/commit.c b/block/commit.c
282
index XXXXXXX..XXXXXXX 100644
283
--- a/block/commit.c
284
+++ b/block/commit.c
285
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
286
.instance_size = sizeof(CommitBlockJob),
287
.job_type = JOB_TYPE_COMMIT,
288
.free = block_job_free,
289
+ .start = commit_run,
290
},
291
- .start = commit_run,
292
};
293
294
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
295
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
296
s->on_error = on_error;
297
298
trace_commit_start(bs, base, top, s);
299
- block_job_start(&s->common);
300
+ job_start(&s->common.job);
301
return;
302
303
fail:
304
diff --git a/block/mirror.c b/block/mirror.c
305
index XXXXXXX..XXXXXXX 100644
306
--- a/block/mirror.c
307
+++ b/block/mirror.c
308
@@ -XXX,XX +XXX,XX @@ static void mirror_iteration_done(MirrorOp *op, int ret)
309
g_free(op);
310
311
if (s->waiting_for_io) {
312
- qemu_coroutine_enter(s->common.co);
313
+ qemu_coroutine_enter(s->common.job.co);
314
}
315
}
29
}
316
30
317
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
31
void bdrv_drain_all(void)
318
mirror_wait_for_io(s);
319
}
320
321
- block_job_pause_point(&s->common);
322
+ job_pause_point(&s->common.job);
323
324
/* Find the number of consective dirty chunks following the first dirty
325
* one, and wait for in flight requests in them. */
326
@@ -XXX,XX +XXX,XX @@ static void mirror_throttle(MirrorBlockJob *s)
327
s->last_pause_ns = now;
328
block_job_sleep_ns(&s->common, 0);
329
} else {
330
- block_job_pause_point(&s->common);
331
+ job_pause_point(&s->common.job);
332
}
333
}
334
335
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
336
goto immediate_exit;
337
}
338
339
- block_job_pause_point(&s->common);
340
+ job_pause_point(&s->common.job);
341
342
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
343
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
344
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
345
block_job_enter(&s->common);
346
}
347
348
-static void mirror_pause(BlockJob *job)
349
+static void mirror_pause(Job *job)
350
{
351
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
352
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
353
354
mirror_wait_for_all_io(s);
355
}
356
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
357
.instance_size = sizeof(MirrorBlockJob),
358
.job_type = JOB_TYPE_MIRROR,
359
.free = block_job_free,
360
+ .start = mirror_run,
361
+ .pause = mirror_pause,
362
},
363
- .start = mirror_run,
364
.complete = mirror_complete,
365
- .pause = mirror_pause,
366
.attached_aio_context = mirror_attached_aio_context,
367
.drain = mirror_drain,
368
};
369
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
370
.instance_size = sizeof(MirrorBlockJob),
371
.job_type = JOB_TYPE_COMMIT,
372
.free = block_job_free,
373
+ .start = mirror_run,
374
+ .pause = mirror_pause,
375
},
376
- .start = mirror_run,
377
.complete = mirror_complete,
378
- .pause = mirror_pause,
379
.attached_aio_context = mirror_attached_aio_context,
380
.drain = mirror_drain,
381
};
382
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
383
}
384
385
trace_mirror_start(bs, s, opaque);
386
- block_job_start(&s->common);
387
+ job_start(&s->common.job);
388
return;
389
390
fail:
391
diff --git a/block/replication.c b/block/replication.c
392
index XXXXXXX..XXXXXXX 100644
393
--- a/block/replication.c
394
+++ b/block/replication.c
395
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
396
aio_context_release(aio_context);
397
return;
398
}
399
- block_job_start(job);
400
+ job_start(&job->job);
401
break;
402
default:
403
aio_context_release(aio_context);
404
diff --git a/block/stream.c b/block/stream.c
405
index XXXXXXX..XXXXXXX 100644
406
--- a/block/stream.c
407
+++ b/block/stream.c
408
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
409
.instance_size = sizeof(StreamBlockJob),
410
.job_type = JOB_TYPE_STREAM,
411
.free = block_job_free,
412
+ .start = stream_run,
413
},
414
- .start = stream_run,
415
};
416
417
void stream_start(const char *job_id, BlockDriverState *bs,
418
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
419
420
s->on_error = on_error;
421
trace_stream_start(bs, base, s);
422
- block_job_start(&s->common);
423
+ job_start(&s->common.job);
424
return;
425
426
fail:
427
diff --git a/blockdev.c b/blockdev.c
428
index XXXXXXX..XXXXXXX 100644
429
--- a/blockdev.c
430
+++ b/blockdev.c
431
@@ -XXX,XX +XXX,XX @@ static void drive_backup_commit(BlkActionState *common)
432
aio_context_acquire(aio_context);
433
434
assert(state->job);
435
- block_job_start(state->job);
436
+ job_start(&state->job->job);
437
438
aio_context_release(aio_context);
439
}
440
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_commit(BlkActionState *common)
441
aio_context_acquire(aio_context);
442
443
assert(state->job);
444
- block_job_start(state->job);
445
+ job_start(&state->job->job);
446
447
aio_context_release(aio_context);
448
}
449
@@ -XXX,XX +XXX,XX @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
450
BlockJob *job;
451
job = do_drive_backup(arg, NULL, errp);
452
if (job) {
453
- block_job_start(job);
454
+ job_start(&job->job);
455
}
456
}
457
458
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
459
BlockJob *job;
460
job = do_blockdev_backup(arg, NULL, errp);
461
if (job) {
462
- block_job_start(job);
463
+ job_start(&job->job);
464
}
465
}
466
467
diff --git a/blockjob.c b/blockjob.c
468
index XXXXXXX..XXXXXXX 100644
469
--- a/blockjob.c
470
+++ b/blockjob.c
471
@@ -XXX,XX +XXX,XX @@
472
#include "qemu/coroutine.h"
473
#include "qemu/timer.h"
474
475
-/* Right now, this mutex is only needed to synchronize accesses to job->busy
476
- * and job->sleep_timer, such as concurrent calls to block_job_do_yield and
477
- * block_job_enter. */
478
-static QemuMutex block_job_mutex;
479
-
480
-static void block_job_lock(void)
481
-{
482
- qemu_mutex_lock(&block_job_mutex);
483
-}
484
-
485
-static void block_job_unlock(void)
486
-{
487
- qemu_mutex_unlock(&block_job_mutex);
488
-}
489
-
490
-static void __attribute__((__constructor__)) block_job_init(void)
491
-{
492
- qemu_mutex_init(&block_job_mutex);
493
-}
494
-
495
static void block_job_event_cancelled(BlockJob *job);
496
static void block_job_event_completed(BlockJob *job, const char *msg);
497
static int block_job_event_pending(BlockJob *job);
498
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
499
500
/* Transactional group of block jobs */
501
struct BlockJobTxn {
502
@@ -XXX,XX +XXX,XX @@ static void block_job_txn_del_job(BlockJob *job)
503
}
504
}
505
506
-/* Assumes the block_job_mutex is held */
507
-static bool block_job_timer_pending(BlockJob *job)
508
-{
509
- return timer_pending(&job->sleep_timer);
510
-}
511
-
512
-/* Assumes the block_job_mutex is held */
513
-static bool block_job_timer_not_pending(BlockJob *job)
514
+/* Assumes the job_mutex is held */
515
+static bool job_timer_not_pending(Job *job)
516
{
517
- return !block_job_timer_pending(job);
518
+ return !timer_pending(&job->sleep_timer);
519
}
520
521
static void block_job_pause(BlockJob *job)
522
{
523
- job->pause_count++;
524
+ job->job.pause_count++;
525
}
526
527
static void block_job_resume(BlockJob *job)
528
{
529
- assert(job->pause_count > 0);
530
- job->pause_count--;
531
- if (job->pause_count) {
532
+ assert(job->job.pause_count > 0);
533
+ job->job.pause_count--;
534
+ if (job->job.pause_count) {
535
return;
536
}
537
538
/* kick only if no timer is pending */
539
- block_job_enter_cond(job, block_job_timer_not_pending);
540
+ job_enter_cond(&job->job, job_timer_not_pending);
541
}
542
543
static void block_job_attached_aio_context(AioContext *new_context,
544
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
545
block_job_detach_aio_context, bjob);
546
blk_unref(bjob->blk);
547
error_free(bjob->blocker);
548
- assert(!timer_pending(&bjob->sleep_timer));
549
+ assert(!timer_pending(&bjob->job.sleep_timer));
550
}
551
552
static void block_job_attached_aio_context(AioContext *new_context,
553
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
554
555
static void block_job_drain(BlockJob *job)
556
{
557
- /* If job is !job->busy this kicks it into the next pause point. */
558
+ /* If job is !job->job.busy this kicks it into the next pause point. */
559
block_job_enter(job);
560
561
blk_drain(job->blk);
562
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
563
564
block_job_pause(job);
565
566
- while (!job->paused && !job->completed) {
567
+ while (!job->job.paused && !job->completed) {
568
block_job_drain(job);
569
}
570
571
@@ -XXX,XX +XXX,XX @@ bool block_job_is_internal(BlockJob *job)
572
return (job->job.id == NULL);
573
}
574
575
-static bool block_job_started(BlockJob *job)
576
-{
577
- return job->co;
578
-}
579
-
580
const BlockJobDriver *block_job_driver(BlockJob *job)
581
{
582
return job->driver;
583
}
584
585
-/**
586
- * All jobs must allow a pause point before entering their job proper. This
587
- * ensures that jobs can be paused prior to being started, then resumed later.
588
- */
589
-static void coroutine_fn block_job_co_entry(void *opaque)
590
-{
591
- BlockJob *job = opaque;
592
-
593
- assert(job && job->driver && job->driver->start);
594
- block_job_pause_point(job);
595
- job->driver->start(job);
596
-}
597
-
598
static void block_job_sleep_timer_cb(void *opaque)
599
{
600
BlockJob *job = opaque;
601
@@ -XXX,XX +XXX,XX @@ static void block_job_sleep_timer_cb(void *opaque)
602
block_job_enter(job);
603
}
604
605
-void block_job_start(BlockJob *job)
606
-{
607
- assert(job && !block_job_started(job) && job->paused &&
608
- job->driver && job->driver->start);
609
- job->co = qemu_coroutine_create(block_job_co_entry, job);
610
- job->pause_count--;
611
- job->busy = true;
612
- job->paused = false;
613
- job_state_transition(&job->job, JOB_STATUS_RUNNING);
614
- bdrv_coroutine_enter(blk_bs(job->blk), job->co);
615
-}
616
-
617
static void block_job_decommission(BlockJob *job)
618
{
619
assert(job);
620
job->completed = true;
621
- job->busy = false;
622
- job->paused = false;
623
+ job->job.busy = false;
624
+ job->job.paused = false;
625
job->job.deferred_to_main_loop = true;
626
block_job_txn_del_job(job);
627
job_state_transition(&job->job, JOB_STATUS_NULL);
628
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
629
static void block_job_conclude(BlockJob *job)
630
{
631
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
632
- if (job->auto_dismiss || !block_job_started(job)) {
633
+ if (job->auto_dismiss || !job_started(&job->job)) {
634
block_job_do_dismiss(job);
635
}
636
}
637
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
638
}
639
640
/* Emit events only if we actually started */
641
- if (block_job_started(job)) {
642
+ if (job_started(&job->job)) {
643
if (job_is_cancelled(&job->job)) {
644
block_job_event_cancelled(job);
645
} else {
646
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
647
if (job->user_paused) {
648
/* Do not call block_job_enter here, the caller will handle it. */
649
job->user_paused = false;
650
- job->pause_count--;
651
+ job->job.pause_count--;
652
}
653
job->job.cancelled = true;
654
/* To prevent 'force == false' overriding a previous 'force == true' */
655
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
656
}
657
}
658
659
+/* Assumes the job_mutex is held */
660
+static bool job_timer_pending(Job *job)
661
+{
662
+ return timer_pending(&job->sleep_timer);
663
+}
664
+
665
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
666
{
667
int64_t old_speed = job->speed;
668
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
669
}
670
671
/* kick only if a timer is pending */
672
- block_job_enter_cond(job, block_job_timer_pending);
673
+ job_enter_cond(&job->job, job_timer_pending);
674
}
675
676
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
677
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
678
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
679
return;
680
}
681
- if (job->pause_count || job_is_cancelled(&job->job) ||
682
+ if (job->job.pause_count || job_is_cancelled(&job->job) ||
683
!job->driver->complete)
684
{
685
error_setg(errp, "The active block job '%s' cannot be completed",
686
@@ -XXX,XX +XXX,XX @@ bool block_job_user_paused(BlockJob *job)
687
void block_job_user_resume(BlockJob *job, Error **errp)
688
{
689
assert(job);
690
- if (!job->user_paused || job->pause_count <= 0) {
691
+ if (!job->user_paused || job->job.pause_count <= 0) {
692
error_setg(errp, "Can't resume a job that was not paused");
693
return;
694
}
695
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
696
return;
697
}
698
block_job_cancel_async(job, force);
699
- if (!block_job_started(job)) {
700
+ if (!job_started(&job->job)) {
701
block_job_completed(job, -ECANCELED);
702
} else if (job->job.deferred_to_main_loop) {
703
block_job_completed_txn_abort(job);
704
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
705
info->type = g_strdup(job_type_str(&job->job));
706
info->device = g_strdup(job->job.id);
707
info->len = job->len;
708
- info->busy = atomic_read(&job->busy);
709
- info->paused = job->pause_count > 0;
710
+ info->busy = atomic_read(&job->job.busy);
711
+ info->paused = job->job.pause_count > 0;
712
info->offset = job->offset;
713
info->speed = job->speed;
714
info->io_status = job->iostatus;
715
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
716
job->blk = blk;
717
job->cb = cb;
718
job->opaque = opaque;
719
- job->busy = false;
720
- job->paused = true;
721
- job->pause_count = 1;
722
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
723
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
724
- aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
725
+ aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer,
726
QEMU_CLOCK_REALTIME, SCALE_NS,
727
block_job_sleep_timer_cb, job);
728
729
@@ -XXX,XX +XXX,XX @@ void block_job_completed(BlockJob *job, int ret)
730
}
731
}
732
733
-static bool block_job_should_pause(BlockJob *job)
734
-{
735
- return job->pause_count > 0;
736
-}
737
-
738
-/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
739
- * Reentering the job coroutine with block_job_enter() before the timer has
740
- * expired is allowed and cancels the timer.
741
- *
742
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
743
- * called explicitly. */
744
-static void block_job_do_yield(BlockJob *job, uint64_t ns)
745
-{
746
- block_job_lock();
747
- if (ns != -1) {
748
- timer_mod(&job->sleep_timer, ns);
749
- }
750
- job->busy = false;
751
- block_job_unlock();
752
- qemu_coroutine_yield();
753
-
754
- /* Set by block_job_enter before re-entering the coroutine. */
755
- assert(job->busy);
756
-}
757
-
758
-void coroutine_fn block_job_pause_point(BlockJob *job)
759
-{
760
- assert(job && block_job_started(job));
761
-
762
- if (!block_job_should_pause(job)) {
763
- return;
764
- }
765
- if (job_is_cancelled(&job->job)) {
766
- return;
767
- }
768
-
769
- if (job->driver->pause) {
770
- job->driver->pause(job);
771
- }
772
-
773
- if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) {
774
- JobStatus status = job->job.status;
775
- job_state_transition(&job->job, status == JOB_STATUS_READY
776
- ? JOB_STATUS_STANDBY
777
- : JOB_STATUS_PAUSED);
778
- job->paused = true;
779
- block_job_do_yield(job, -1);
780
- job->paused = false;
781
- job_state_transition(&job->job, status);
782
- }
783
-
784
- if (job->driver->resume) {
785
- job->driver->resume(job);
786
- }
787
-}
788
-
789
-/*
790
- * Conditionally enter a block_job pending a call to fn() while
791
- * under the block_job_lock critical section.
792
- */
793
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
794
-{
795
- if (!block_job_started(job)) {
796
- return;
797
- }
798
- if (job->job.deferred_to_main_loop) {
799
- return;
800
- }
801
-
802
- block_job_lock();
803
- if (job->busy) {
804
- block_job_unlock();
805
- return;
806
- }
807
-
808
- if (fn && !fn(job)) {
809
- block_job_unlock();
810
- return;
811
- }
812
-
813
- assert(!job->job.deferred_to_main_loop);
814
- timer_del(&job->sleep_timer);
815
- job->busy = true;
816
- block_job_unlock();
817
- aio_co_wake(job->co);
818
-}
819
-
820
void block_job_enter(BlockJob *job)
821
{
822
- block_job_enter_cond(job, NULL);
823
+ job_enter_cond(&job->job, NULL);
824
}
825
826
void block_job_sleep_ns(BlockJob *job, int64_t ns)
827
{
828
- assert(job->busy);
829
+ assert(job->job.busy);
830
831
/* Check cancellation *before* setting busy = false, too! */
832
if (job_is_cancelled(&job->job)) {
833
return;
834
}
835
836
- if (!block_job_should_pause(job)) {
837
- block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
838
+ if (!job_should_pause(&job->job)) {
839
+ job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
840
}
841
842
- block_job_pause_point(job);
843
+ job_pause_point(&job->job);
844
}
845
846
void block_job_yield(BlockJob *job)
847
{
848
- assert(job->busy);
849
+ assert(job->job.busy);
850
851
/* Check cancellation *before* setting busy = false, too! */
852
if (job_is_cancelled(&job->job)) {
853
return;
854
}
855
856
- if (!block_job_should_pause(job)) {
857
- block_job_do_yield(job, -1);
858
+ if (!job_should_pause(&job->job)) {
859
+ job_do_yield(&job->job, -1);
860
}
861
862
- block_job_pause_point(job);
863
+ job_pause_point(&job->job);
864
}
865
866
void block_job_iostatus_reset(BlockJob *job)
867
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
868
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
869
return;
870
}
871
- assert(job->user_paused && job->pause_count > 0);
872
+ assert(job->user_paused && job->job.pause_count > 0);
873
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
874
}
875
876
diff --git a/job.c b/job.c
877
index XXXXXXX..XXXXXXX 100644
878
--- a/job.c
879
+++ b/job.c
880
@@ -XXX,XX +XXX,XX @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
881
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
882
};
883
884
+/* Right now, this mutex is only needed to synchronize accesses to job->busy
885
+ * and job->sleep_timer, such as concurrent calls to job_do_yield and
886
+ * job_enter. */
887
+static QemuMutex job_mutex;
888
+
889
+static void job_lock(void)
890
+{
891
+ qemu_mutex_lock(&job_mutex);
892
+}
893
+
894
+static void job_unlock(void)
895
+{
896
+ qemu_mutex_unlock(&job_mutex);
897
+}
898
+
899
+static void __attribute__((__constructor__)) job_init(void)
900
+{
901
+ qemu_mutex_init(&job_mutex);
902
+}
903
+
904
/* TODO Make static once the whole state machine is in job.c */
905
void job_state_transition(Job *job, JobStatus s1)
906
{
907
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
908
return job->cancelled;
909
}
910
911
+bool job_started(Job *job)
912
+{
913
+ return job->co;
914
+}
915
+
916
+bool job_should_pause(Job *job)
917
+{
918
+ return job->pause_count > 0;
919
+}
920
+
921
Job *job_next(Job *job)
922
{
923
if (!job) {
924
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
925
job->id = g_strdup(job_id);
926
job->refcnt = 1;
927
job->aio_context = ctx;
928
+ job->busy = false;
929
+ job->paused = true;
930
+ job->pause_count = 1;
931
932
job_state_transition(job, JOB_STATUS_CREATED);
933
934
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
935
}
936
}
937
938
+void job_enter_cond(Job *job, bool(*fn)(Job *job))
939
+{
940
+ if (!job_started(job)) {
941
+ return;
942
+ }
943
+ if (job->deferred_to_main_loop) {
944
+ return;
945
+ }
946
+
947
+ job_lock();
948
+ if (job->busy) {
949
+ job_unlock();
950
+ return;
951
+ }
952
+
953
+ if (fn && !fn(job)) {
954
+ job_unlock();
955
+ return;
956
+ }
957
+
958
+ assert(!job->deferred_to_main_loop);
959
+ timer_del(&job->sleep_timer);
960
+ job->busy = true;
961
+ job_unlock();
962
+ aio_co_wake(job->co);
963
+}
964
+
965
+/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
966
+ * Reentering the job coroutine with block_job_enter() before the timer has
967
+ * expired is allowed and cancels the timer.
968
+ *
969
+ * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
970
+ * called explicitly. */
971
+void coroutine_fn job_do_yield(Job *job, uint64_t ns)
972
+{
973
+ job_lock();
974
+ if (ns != -1) {
975
+ timer_mod(&job->sleep_timer, ns);
976
+ }
977
+ job->busy = false;
978
+ job_unlock();
979
+ qemu_coroutine_yield();
980
+
981
+ /* Set by job_enter_cond() before re-entering the coroutine. */
982
+ assert(job->busy);
983
+}
984
+
985
+void coroutine_fn job_pause_point(Job *job)
986
+{
987
+ assert(job && job_started(job));
988
+
989
+ if (!job_should_pause(job)) {
990
+ return;
991
+ }
992
+ if (job_is_cancelled(job)) {
993
+ return;
994
+ }
995
+
996
+ if (job->driver->pause) {
997
+ job->driver->pause(job);
998
+ }
999
+
1000
+ if (job_should_pause(job) && !job_is_cancelled(job)) {
1001
+ JobStatus status = job->status;
1002
+ job_state_transition(job, status == JOB_STATUS_READY
1003
+ ? JOB_STATUS_STANDBY
1004
+ : JOB_STATUS_PAUSED);
1005
+ job->paused = true;
1006
+ job_do_yield(job, -1);
1007
+ job->paused = false;
1008
+ job_state_transition(job, status);
1009
+ }
1010
+
1011
+ if (job->driver->resume) {
1012
+ job->driver->resume(job);
1013
+ }
1014
+}
1015
+
1016
+/**
1017
+ * All jobs must allow a pause point before entering their job proper. This
1018
+ * ensures that jobs can be paused prior to being started, then resumed later.
1019
+ */
1020
+static void coroutine_fn job_co_entry(void *opaque)
1021
+{
1022
+ Job *job = opaque;
1023
+
1024
+ assert(job && job->driver && job->driver->start);
1025
+ job_pause_point(job);
1026
+ job->driver->start(job);
1027
+}
1028
+
1029
+
1030
+void job_start(Job *job)
1031
+{
1032
+ assert(job && !job_started(job) && job->paused &&
1033
+ job->driver && job->driver->start);
1034
+ job->co = qemu_coroutine_create(job_co_entry, job);
1035
+ job->pause_count--;
1036
+ job->busy = true;
1037
+ job->paused = false;
1038
+ job_state_transition(job, JOB_STATUS_RUNNING);
1039
+ aio_co_enter(job->aio_context, job->co);
1040
+}
1041
+
1042
typedef struct {
1043
Job *job;
1044
JobDeferToMainLoopFn *fn;
1045
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
1046
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
1047
--- a/tests/test-bdrv-drain.c
34
--- a/tests/test-bdrv-drain.c
1048
+++ b/tests/test-bdrv-drain.c
35
+++ b/tests/test-bdrv-drain.c
1049
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
1050
.job_driver = {
1051
.instance_size = sizeof(TestBlockJob),
1052
.free = block_job_free,
1053
+ .start = test_job_start,
1054
},
1055
- .start = test_job_start,
1056
.complete = test_job_complete,
1057
};
1058
1059
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
1060
job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL,
1061
0, 0, NULL, NULL, &error_abort);
1062
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
1063
- block_job_start(job);
1064
+ job_start(&job->job);
1065
1066
- g_assert_cmpint(job->pause_count, ==, 0);
1067
- g_assert_false(job->paused);
1068
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1069
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1070
+ g_assert_false(job->job.paused);
1071
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1072
1073
do_drain_begin(drain_type, src);
37
do_drain_begin(drain_type, src);
1074
38
1075
if (drain_type == BDRV_DRAIN_ALL) {
39
if (drain_type == BDRV_DRAIN_ALL) {
1076
/* bdrv_drain_all() drains both src and target */
40
- /* bdrv_drain_all() drains both src and target, and involves an
1077
- g_assert_cmpint(job->pause_count, ==, 2);
41
- * additional block_job_pause_all() */
1078
+ g_assert_cmpint(job->job.pause_count, ==, 2);
42
- g_assert_cmpint(job->pause_count, ==, 3);
43
+ /* bdrv_drain_all() drains both src and target */
44
+ g_assert_cmpint(job->pause_count, ==, 2);
1079
} else {
45
} else {
1080
- g_assert_cmpint(job->pause_count, ==, 1);
46
g_assert_cmpint(job->pause_count, ==, 1);
1081
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1082
}
47
}
1083
/* XXX We don't wait until the job is actually paused. Is this okay? */
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
1084
- /* g_assert_true(job->paused); */
1085
- g_assert_false(job->busy); /* The job is paused */
1086
+ /* g_assert_true(job->job.paused); */
1087
+ g_assert_false(job->job.busy); /* The job is paused */
1088
1089
do_drain_end(drain_type, src);
1090
1091
- g_assert_cmpint(job->pause_count, ==, 0);
1092
- g_assert_false(job->paused);
1093
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1094
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1095
+ g_assert_false(job->job.paused);
1096
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1097
1098
do_drain_begin(drain_type, target);
49
do_drain_begin(drain_type, target);
1099
50
1100
if (drain_type == BDRV_DRAIN_ALL) {
51
if (drain_type == BDRV_DRAIN_ALL) {
1101
/* bdrv_drain_all() drains both src and target */
52
- /* bdrv_drain_all() drains both src and target, and involves an
1102
- g_assert_cmpint(job->pause_count, ==, 2);
53
- * additional block_job_pause_all() */
1103
+ g_assert_cmpint(job->job.pause_count, ==, 2);
54
- g_assert_cmpint(job->pause_count, ==, 3);
55
+ /* bdrv_drain_all() drains both src and target */
56
+ g_assert_cmpint(job->pause_count, ==, 2);
1104
} else {
57
} else {
1105
- g_assert_cmpint(job->pause_count, ==, 1);
58
g_assert_cmpint(job->pause_count, ==, 1);
1106
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1107
}
59
}
1108
/* XXX We don't wait until the job is actually paused. Is this okay? */
1109
- /* g_assert_true(job->paused); */
1110
- g_assert_false(job->busy); /* The job is paused */
1111
+ /* g_assert_true(job->job.paused); */
1112
+ g_assert_false(job->job.busy); /* The job is paused */
1113
1114
do_drain_end(drain_type, target);
1115
1116
- g_assert_cmpint(job->pause_count, ==, 0);
1117
- g_assert_false(job->paused);
1118
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1119
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1120
+ g_assert_false(job->job.paused);
1121
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1122
1123
ret = block_job_complete_sync(job, &error_abort);
1124
g_assert_cmpint(ret, ==, 0);
1125
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
1126
index XXXXXXX..XXXXXXX 100644
1127
--- a/tests/test-blockjob-txn.c
1128
+++ b/tests/test-blockjob-txn.c
1129
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
1130
.job_driver = {
1131
.instance_size = sizeof(TestBlockJob),
1132
.free = block_job_free,
1133
+ .start = test_block_job_run,
1134
},
1135
- .start = test_block_job_run,
1136
};
1137
1138
/* Create a block job that completes with a given return code after a given
1139
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
1140
1141
txn = block_job_txn_new();
1142
job = test_block_job_start(1, true, expected, &result, txn);
1143
- block_job_start(job);
1144
+ job_start(&job->job);
1145
1146
if (expected == -ECANCELED) {
1147
block_job_cancel(job, false);
1148
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
1149
txn = block_job_txn_new();
1150
job1 = test_block_job_start(1, true, expected1, &result1, txn);
1151
job2 = test_block_job_start(2, true, expected2, &result2, txn);
1152
- block_job_start(job1);
1153
- block_job_start(job2);
1154
+ job_start(&job1->job);
1155
+ job_start(&job2->job);
1156
1157
/* Release our reference now to trigger as many nice
1158
* use-after-free bugs as possible.
1159
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
1160
txn = block_job_txn_new();
1161
job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
1162
job2 = test_block_job_start(2, false, 0, &result2, txn);
1163
- block_job_start(job1);
1164
- block_job_start(job2);
1165
+ job_start(&job1->job);
1166
+ job_start(&job2->job);
1167
1168
block_job_cancel(job1, false);
1169
1170
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
1171
index XXXXXXX..XXXXXXX 100644
1172
--- a/tests/test-blockjob.c
1173
+++ b/tests/test-blockjob.c
1174
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
1175
.job_driver = {
1176
.instance_size = sizeof(CancelJob),
1177
.free = block_job_free,
1178
+ .start = cancel_job_start,
1179
},
1180
- .start = cancel_job_start,
1181
.complete = cancel_job_complete,
1182
};
1183
1184
@@ -XXX,XX +XXX,XX @@ static void test_cancel_running(void)
1185
1186
s = create_common(&job);
1187
1188
- block_job_start(job);
1189
+ job_start(&job->job);
1190
assert(job->job.status == JOB_STATUS_RUNNING);
1191
1192
cancel_common(s);
1193
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
1194
1195
s = create_common(&job);
1196
1197
- block_job_start(job);
1198
+ job_start(&job->job);
1199
assert(job->job.status == JOB_STATUS_RUNNING);
1200
1201
block_job_user_pause(job, &error_abort);
1202
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
1203
1204
s = create_common(&job);
1205
1206
- block_job_start(job);
1207
+ job_start(&job->job);
1208
assert(job->job.status == JOB_STATUS_RUNNING);
1209
1210
s->should_converge = true;
1211
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
1212
1213
s = create_common(&job);
1214
1215
- block_job_start(job);
1216
+ job_start(&job->job);
1217
assert(job->job.status == JOB_STATUS_RUNNING);
1218
1219
s->should_converge = true;
1220
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
1221
1222
s = create_common(&job);
1223
1224
- block_job_start(job);
1225
+ job_start(&job->job);
1226
assert(job->job.status == JOB_STATUS_RUNNING);
1227
1228
s->should_converge = true;
1229
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
1230
1231
s = create_common(&job);
1232
1233
- block_job_start(job);
1234
+ job_start(&job->job);
1235
assert(job->job.status == JOB_STATUS_RUNNING);
1236
1237
s->should_converge = true;
1238
--
60
--
1239
2.13.6
61
2.13.6
1240
62
1241
63
diff view generated by jsdifflib
1
We cannot yet move the whole logic around job cancelling to Job because
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
it depends on quite a few other things that are still only in BlockJob,
2
aio_disable_external() to the outermost drain section, but the block
3
but we can move the cancelled field at least.
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
4
6
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
8
---
9
include/block/blockjob.h | 8 --------
9
block/io.c | 12 +++++++-----
10
include/block/blockjob_int.h | 8 --------
10
1 file changed, 7 insertions(+), 5 deletions(-)
11
include/qemu/job.h | 11 +++++++++++
12
block/backup.c | 6 +++---
13
block/commit.c | 4 ++--
14
block/mirror.c | 20 ++++++++++----------
15
block/stream.c | 4 ++--
16
blockjob.c | 28 +++++++++++++---------------
17
job.c | 5 +++++
18
tests/test-blockjob-txn.c | 6 +++---
19
tests/test-blockjob.c | 2 +-
20
11 files changed, 50 insertions(+), 52 deletions(-)
21
11
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
12
diff --git a/block/io.c b/block/io.c
23
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
14
--- a/block/io.c
25
+++ b/include/block/blockjob.h
15
+++ b/block/io.c
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
27
Coroutine *co;
17
28
18
void bdrv_drained_end(BlockDriverState *bs)
29
/**
19
{
30
- * Set to true if the job should cancel itself. The flag must
20
+ int old_quiesce_counter;
31
- * always be tested just before toggling the busy flag from false
32
- * to true. After a job has been cancelled, it should only yield
33
- * if #aio_poll will ("sooner or later") reenter the coroutine.
34
- */
35
- bool cancelled;
36
-
37
- /**
38
* Set to true if the job should abort immediately without waiting
39
* for data to be in sync.
40
*/
41
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/blockjob_int.h
44
+++ b/include/block/blockjob_int.h
45
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job);
46
void block_job_completed(BlockJob *job, int ret);
47
48
/**
49
- * block_job_is_cancelled:
50
- * @job: The job being queried.
51
- *
52
- * Returns whether the job is scheduled for cancellation.
53
- */
54
-bool block_job_is_cancelled(BlockJob *job);
55
-
56
-/**
57
* block_job_pause_point:
58
* @job: The job that is ready to pause.
59
*
60
diff --git a/include/qemu/job.h b/include/qemu/job.h
61
index XXXXXXX..XXXXXXX 100644
62
--- a/include/qemu/job.h
63
+++ b/include/qemu/job.h
64
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
65
/** Current state; See @JobStatus for details. */
66
JobStatus status;
67
68
+ /**
69
+ * Set to true if the job should cancel itself. The flag must
70
+ * always be tested just before toggling the busy flag from false
71
+ * to true. After a job has been cancelled, it should only yield
72
+ * if #aio_poll will ("sooner or later") reenter the coroutine.
73
+ */
74
+ bool cancelled;
75
+
21
+
76
/** Element of the list of jobs */
22
if (qemu_in_coroutine()) {
77
QLIST_ENTRY(Job) job_list;
23
bdrv_co_yield_to_drain(bs, false);
78
} Job;
79
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
80
/** Returns the enum string for the JobType of a given Job. */
81
const char *job_type_str(const Job *job);
82
83
+/** Returns whether the job is scheduled for cancellation. */
84
+bool job_is_cancelled(Job *job);
85
+
86
/**
87
* Get the next element from the list of block jobs after @job, or the
88
* first one if @job is %NULL.
89
diff --git a/block/backup.c b/block/backup.c
90
index XXXXXXX..XXXXXXX 100644
91
--- a/block/backup.c
92
+++ b/block/backup.c
93
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
94
{
95
uint64_t delay_ns;
96
97
- if (block_job_is_cancelled(&job->common)) {
98
+ if (job_is_cancelled(&job->common.job)) {
99
return true;
100
}
101
102
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
103
job->bytes_read = 0;
104
block_job_sleep_ns(&job->common, delay_ns);
105
106
- if (block_job_is_cancelled(&job->common)) {
107
+ if (job_is_cancelled(&job->common.job)) {
108
return true;
109
}
110
111
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
112
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
113
/* All bits are set in copy_bitmap to allow any cluster to be copied.
114
* This does not actually require them to be copied. */
115
- while (!block_job_is_cancelled(&job->common)) {
116
+ while (!job_is_cancelled(&job->common.job)) {
117
/* Yield until the job is cancelled. We just let our before_write
118
* notify callback service CoW requests. */
119
block_job_yield(&job->common);
120
diff --git a/block/commit.c b/block/commit.c
121
index XXXXXXX..XXXXXXX 100644
122
--- a/block/commit.c
123
+++ b/block/commit.c
124
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
125
* the normal backing chain can be restored. */
126
blk_unref(s->base);
127
128
- if (!block_job_is_cancelled(&s->common) && ret == 0) {
129
+ if (!job_is_cancelled(&s->common.job) && ret == 0) {
130
/* success */
131
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
132
s->backing_file_str);
133
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
134
* with no pending I/O here so that bdrv_drain_all() returns.
135
*/
136
block_job_sleep_ns(&s->common, delay_ns);
137
- if (block_job_is_cancelled(&s->common)) {
138
+ if (job_is_cancelled(&s->common.job)) {
139
break;
140
}
141
/* Copy if allocated above the base */
142
diff --git a/block/mirror.c b/block/mirror.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/mirror.c
145
+++ b/block/mirror.c
146
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
147
148
mirror_throttle(s);
149
150
- if (block_job_is_cancelled(&s->common)) {
151
+ if (job_is_cancelled(&s->common.job)) {
152
s->initial_zeroing_ongoing = false;
153
return 0;
154
}
155
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
156
157
mirror_throttle(s);
158
159
- if (block_job_is_cancelled(&s->common)) {
160
+ if (job_is_cancelled(&s->common.job)) {
161
return 0;
162
}
163
164
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
165
checking for a NULL string */
166
int ret = 0;
167
168
- if (block_job_is_cancelled(&s->common)) {
169
+ if (job_is_cancelled(&s->common.job)) {
170
goto immediate_exit;
171
}
172
173
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
174
/* Report BLOCK_JOB_READY and wait for complete. */
175
block_job_event_ready(&s->common);
176
s->synced = true;
177
- while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
178
+ while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
179
block_job_yield(&s->common);
180
}
181
- s->common.cancelled = false;
182
+ s->common.job.cancelled = false;
183
goto immediate_exit;
184
}
185
186
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
187
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
188
if (!s->is_none_mode) {
189
ret = mirror_dirty_init(s);
190
- if (ret < 0 || block_job_is_cancelled(&s->common)) {
191
+ if (ret < 0 || job_is_cancelled(&s->common.job)) {
192
goto immediate_exit;
193
}
194
}
195
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
196
}
197
198
should_complete = s->should_complete ||
199
- block_job_is_cancelled(&s->common);
200
+ job_is_cancelled(&s->common.job);
201
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
202
}
203
204
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
205
* completion.
206
*/
207
assert(QLIST_EMPTY(&bs->tracked_requests));
208
- s->common.cancelled = false;
209
+ s->common.job.cancelled = false;
210
need_drain = false;
211
break;
212
}
213
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
214
}
215
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
216
block_job_sleep_ns(&s->common, delay_ns);
217
- if (block_job_is_cancelled(&s->common) &&
218
+ if (job_is_cancelled(&s->common.job) &&
219
(!s->synced || s->common.force))
220
{
221
break;
222
@@ -XXX,XX +XXX,XX @@ immediate_exit:
223
* the target is a copy of the source.
224
*/
225
assert(ret < 0 || ((s->common.force || !s->synced) &&
226
- block_job_is_cancelled(&s->common)));
227
+ job_is_cancelled(&s->common.job)));
228
assert(need_drain);
229
mirror_wait_for_all_io(s);
230
}
231
diff --git a/block/stream.c b/block/stream.c
232
index XXXXXXX..XXXXXXX 100644
233
--- a/block/stream.c
234
+++ b/block/stream.c
235
@@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque)
236
BlockDriverState *base = s->base;
237
Error *local_err = NULL;
238
239
- if (!block_job_is_cancelled(&s->common) && bs->backing &&
240
+ if (!job_is_cancelled(&s->common.job) && bs->backing &&
241
data->ret == 0) {
242
const char *base_id = NULL, *base_fmt = NULL;
243
if (base) {
244
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
245
* with no pending I/O here so that bdrv_drain_all() returns.
246
*/
247
block_job_sleep_ns(&s->common, delay_ns);
248
- if (block_job_is_cancelled(&s->common)) {
249
+ if (job_is_cancelled(&s->common.job)) {
250
break;
251
}
252
253
diff --git a/blockjob.c b/blockjob.c
254
index XXXXXXX..XXXXXXX 100644
255
--- a/blockjob.c
256
+++ b/blockjob.c
257
@@ -XXX,XX +XXX,XX @@ static void block_job_conclude(BlockJob *job)
258
259
static void block_job_update_rc(BlockJob *job)
260
{
261
- if (!job->ret && block_job_is_cancelled(job)) {
262
+ if (!job->ret && job_is_cancelled(&job->job)) {
263
job->ret = -ECANCELED;
264
}
265
if (job->ret) {
266
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
267
268
/* Emit events only if we actually started */
269
if (block_job_started(job)) {
270
- if (block_job_is_cancelled(job)) {
271
+ if (job_is_cancelled(&job->job)) {
272
block_job_event_cancelled(job);
273
} else {
274
const char *msg = NULL;
275
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
276
job->user_paused = false;
277
job->pause_count--;
278
}
279
- job->cancelled = true;
280
+ job->job.cancelled = true;
281
/* To prevent 'force == false' overriding a previous 'force == true' */
282
job->force |= force;
283
}
284
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
285
while (!job->completed) {
286
aio_poll(qemu_get_aio_context(), true);
287
}
288
- ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
289
+ ret = (job_is_cancelled(&job->job) && job->ret == 0)
290
+ ? -ECANCELED : job->ret;
291
job_unref(&job->job);
292
return ret;
293
}
294
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
295
other_job = QLIST_FIRST(&txn->jobs);
296
ctx = blk_get_aio_context(other_job->blk);
297
if (!other_job->completed) {
298
- assert(other_job->cancelled);
299
+ assert(job_is_cancelled(&other_job->job));
300
block_job_finish_sync(other_job, NULL, NULL);
301
}
302
block_job_finalize_single(other_job);
303
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
304
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
305
return;
24
return;
306
}
25
}
307
- if (job->pause_count || job->cancelled || !job->driver->complete) {
26
assert(bs->quiesce_counter > 0);
308
+ if (job->pause_count || job_is_cancelled(&job->job) ||
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
309
+ !job->driver->complete)
28
- return;
310
+ {
29
- }
311
error_setg(errp, "The active block job '%s' cannot be completed",
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
312
job->job.id);
31
313
return;
32
/* Re-enable things in child-to-parent order */
314
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
33
bdrv_drain_invoke(bs, false, false);
315
if (!block_job_should_pause(job)) {
34
- bdrv_parent_drained_end(bs);
316
return;
35
- aio_enable_external(bdrv_get_aio_context(bs));
317
}
36
+ if (old_quiesce_counter == 1) {
318
- if (block_job_is_cancelled(job)) {
37
+ bdrv_parent_drained_end(bs);
319
+ if (job_is_cancelled(&job->job)) {
38
+ aio_enable_external(bdrv_get_aio_context(bs));
320
return;
39
+ }
321
}
322
323
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
324
job->driver->pause(job);
325
}
326
327
- if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
328
+ if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) {
329
JobStatus status = job->job.status;
330
job_state_transition(&job->job, status == JOB_STATUS_READY
331
? JOB_STATUS_STANDBY
332
@@ -XXX,XX +XXX,XX @@ void block_job_enter(BlockJob *job)
333
block_job_enter_cond(job, NULL);
334
}
40
}
335
41
336
-bool block_job_is_cancelled(BlockJob *job)
42
/*
337
-{
338
- return job->cancelled;
339
-}
340
-
341
void block_job_sleep_ns(BlockJob *job, int64_t ns)
342
{
343
assert(job->busy);
344
345
/* Check cancellation *before* setting busy = false, too! */
346
- if (block_job_is_cancelled(job)) {
347
+ if (job_is_cancelled(&job->job)) {
348
return;
349
}
350
351
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job)
352
assert(job->busy);
353
354
/* Check cancellation *before* setting busy = false, too! */
355
- if (block_job_is_cancelled(job)) {
356
+ if (job_is_cancelled(&job->job)) {
357
return;
358
}
359
360
diff --git a/job.c b/job.c
361
index XXXXXXX..XXXXXXX 100644
362
--- a/job.c
363
+++ b/job.c
364
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job)
365
return JobType_str(job_type(job));
366
}
367
368
+bool job_is_cancelled(Job *job)
369
+{
370
+ return job->cancelled;
371
+}
372
+
373
Job *job_next(Job *job)
374
{
375
if (!job) {
376
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
377
index XXXXXXX..XXXXXXX 100644
378
--- a/tests/test-blockjob-txn.c
379
+++ b/tests/test-blockjob-txn.c
380
@@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(BlockJob *job, void *opaque)
381
BlockDriverState *bs = blk_bs(job->blk);
382
int rc = (intptr_t)opaque;
383
384
- if (block_job_is_cancelled(job)) {
385
+ if (job_is_cancelled(&job->job)) {
386
rc = -ECANCELED;
387
}
388
389
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
390
block_job_yield(job);
391
}
392
393
- if (block_job_is_cancelled(job)) {
394
+ if (job_is_cancelled(&job->job)) {
395
break;
396
}
397
}
398
@@ -XXX,XX +XXX,XX @@ typedef struct {
399
static void test_block_job_cb(void *opaque, int ret)
400
{
401
TestBlockJobCBData *data = opaque;
402
- if (!ret && block_job_is_cancelled(&data->job->common)) {
403
+ if (!ret && job_is_cancelled(&data->job->common.job)) {
404
ret = -ECANCELED;
405
}
406
*data->result = ret;
407
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
408
index XXXXXXX..XXXXXXX 100644
409
--- a/tests/test-blockjob.c
410
+++ b/tests/test-blockjob.c
411
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
412
CancelJob *s = opaque;
413
414
while (!s->should_complete) {
415
- if (block_job_is_cancelled(&s->common)) {
416
+ if (job_is_cancelled(&s->common.job)) {
417
goto defer;
418
}
419
420
--
43
--
421
2.13.6
44
2.13.6
422
45
423
46
diff view generated by jsdifflib
1
The transition to the READY state was still performed in the BlockJob
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
layer, in the same function that sent the BLOCK_JOB_READY QMP event.
2
---
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
4
1 file changed, 57 insertions(+)
3
5
4
This patch brings the state transition to the Job layer and implements
5
the QMP event using a notifier called from the Job layer, like we
6
already do for other events related to state transitions.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/blockjob.h | 3 +++
12
include/block/blockjob_int.h | 8 --------
13
include/qemu/job.h | 9 ++++++---
14
block/mirror.c | 6 +++---
15
blockjob.c | 33 ++++++++++++++++++---------------
16
job.c | 16 +++++++++++++---
17
tests/test-bdrv-drain.c | 2 +-
18
tests/test-blockjob.c | 2 +-
19
8 files changed, 45 insertions(+), 34 deletions(-)
20
21
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob.h
24
+++ b/include/block/blockjob.h
25
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
26
/** Called when the job transitions to PENDING */
27
Notifier pending_notifier;
28
29
+ /** Called when the job transitions to READY */
30
+ Notifier ready_notifier;
31
+
32
/** BlockDriverStates that are involved in this block job */
33
GSList *nodes;
34
} BlockJob;
35
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
36
index XXXXXXX..XXXXXXX 100644
37
--- a/include/block/blockjob_int.h
38
+++ b/include/block/blockjob_int.h
39
@@ -XXX,XX +XXX,XX @@ void block_job_drain(Job *job);
40
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
41
42
/**
43
- * block_job_event_ready:
44
- * @job: The job which is now ready to be completed.
45
- *
46
- * Send a BLOCK_JOB_READY event for the specified job.
47
- */
48
-void block_job_event_ready(BlockJob *job);
49
-
50
-/**
51
* block_job_error_action:
52
* @job: The job to signal an error for.
53
* @on_err: The error action setting.
54
diff --git a/include/qemu/job.h b/include/qemu/job.h
55
index XXXXXXX..XXXXXXX 100644
56
--- a/include/qemu/job.h
57
+++ b/include/qemu/job.h
58
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
59
/** Notifiers called when the job transitions to PENDING */
60
NotifierList on_pending;
61
62
+ /** Notifiers called when the job transitions to READY */
63
+ NotifierList on_ready;
64
+
65
/** Element of the list of jobs */
66
QLIST_ENTRY(Job) job_list;
67
68
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
69
/** The @job could not be started, free it. */
70
void job_early_fail(Job *job);
71
72
+/** Moves the @job from RUNNING to READY */
73
+void job_transition_to_ready(Job *job);
74
+
75
/**
76
* @job: The job being completed.
77
* @ret: The status code.
78
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
79
*/
80
int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
81
82
-/* TODO To be removed from the public interface */
83
-void job_state_transition(Job *job, JobStatus s1);
84
-
85
#endif
86
diff --git a/block/mirror.c b/block/mirror.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/mirror.c
89
+++ b/block/mirror.c
90
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
91
}
92
93
if (s->bdev_length == 0) {
94
- /* Report BLOCK_JOB_READY and wait for complete. */
95
- block_job_event_ready(&s->common);
96
+ /* Transition to the READY state and wait for complete. */
97
+ job_transition_to_ready(&s->common.job);
98
s->synced = true;
99
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
100
job_yield(&s->common.job);
101
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
102
* report completion. This way, block-job-cancel will leave
103
* the target in a consistent state.
104
*/
105
- block_job_event_ready(&s->common);
106
+ job_transition_to_ready(&s->common.job);
107
s->synced = true;
108
}
109
110
diff --git a/blockjob.c b/blockjob.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/blockjob.c
113
+++ b/blockjob.c
114
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
115
&error_abort);
116
}
117
118
+static void block_job_event_ready(Notifier *n, void *opaque)
119
+{
120
+ BlockJob *job = opaque;
121
+
122
+ if (block_job_is_internal(job)) {
123
+ return;
124
+ }
125
+
126
+ qapi_event_send_block_job_ready(job_type(&job->job),
127
+ job->job.id,
128
+ job->len,
129
+ job->offset,
130
+ job->speed, &error_abort);
131
+}
132
+
133
+
134
/*
135
* API for block job drivers and the block layer. These functions are
136
* declared in blockjob_int.h.
137
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
138
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
139
job->finalize_completed_notifier.notify = block_job_event_completed;
140
job->pending_notifier.notify = block_job_event_pending;
141
+ job->ready_notifier.notify = block_job_event_ready;
142
143
notifier_list_add(&job->job.on_finalize_cancelled,
144
&job->finalize_cancelled_notifier);
145
notifier_list_add(&job->job.on_finalize_completed,
146
&job->finalize_completed_notifier);
147
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
148
+ notifier_list_add(&job->job.on_ready, &job->ready_notifier);
149
150
error_setg(&job->blocker, "block device is in use by block job: %s",
151
job_type_str(&job->job));
152
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job)
153
block_job_iostatus_reset(bjob);
154
}
155
156
-void block_job_event_ready(BlockJob *job)
157
-{
158
- job_state_transition(&job->job, JOB_STATUS_READY);
159
-
160
- if (block_job_is_internal(job)) {
161
- return;
162
- }
163
-
164
- qapi_event_send_block_job_ready(job_type(&job->job),
165
- job->job.id,
166
- job->len,
167
- job->offset,
168
- job->speed, &error_abort);
169
-}
170
-
171
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
172
int is_read, int error)
173
{
174
diff --git a/job.c b/job.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/job.c
177
+++ b/job.c
178
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
179
return rc;
180
}
181
182
-
183
-/* TODO Make static once the whole state machine is in job.c */
184
-void job_state_transition(Job *job, JobStatus s1)
185
+static void job_state_transition(Job *job, JobStatus s1)
186
{
187
JobStatus s0 = job->status;
188
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
189
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
190
notifier_list_init(&job->on_finalize_cancelled);
191
notifier_list_init(&job->on_finalize_completed);
192
notifier_list_init(&job->on_pending);
193
+ notifier_list_init(&job->on_ready);
194
195
job_state_transition(job, JOB_STATUS_CREATED);
196
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
197
@@ -XXX,XX +XXX,XX @@ static void job_event_pending(Job *job)
198
notifier_list_notify(&job->on_pending, job);
199
}
200
201
+static void job_event_ready(Job *job)
202
+{
203
+ notifier_list_notify(&job->on_ready, job);
204
+}
205
+
206
void job_enter_cond(Job *job, bool(*fn)(Job *job))
207
{
208
if (!job_started(job)) {
209
@@ -XXX,XX +XXX,XX @@ static int job_transition_to_pending(Job *job)
210
return 0;
211
}
212
213
+void job_transition_to_ready(Job *job)
214
+{
215
+ job_state_transition(job, JOB_STATUS_READY);
216
+ job_event_ready(job);
217
+}
218
+
219
static void job_completed_txn_success(Job *job)
220
{
221
JobTxn *txn = job->txn;
222
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
223
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
224
--- a/tests/test-bdrv-drain.c
8
--- a/tests/test-bdrv-drain.c
225
+++ b/tests/test-bdrv-drain.c
9
+++ b/tests/test-bdrv-drain.c
226
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
227
{
11
enum drain_type {
228
TestBlockJob *s = opaque;
12
BDRV_DRAIN_ALL,
229
13
BDRV_DRAIN,
230
- block_job_event_ready(&s->common);
14
+ DRAIN_TYPE_MAX,
231
+ job_transition_to_ready(&s->common.job);
15
};
232
while (!s->should_complete) {
16
233
job_sleep_ns(&s->common.job, 100000);
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
234
}
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
235
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
19
test_quiesce_common(BDRV_DRAIN, false);
236
index XXXXXXX..XXXXXXX 100644
20
}
237
--- a/tests/test-blockjob.c
21
238
+++ b/tests/test-blockjob.c
22
+static void test_nested(void)
239
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
23
+{
240
}
24
+ BlockBackend *blk;
241
25
+ BlockDriverState *bs, *backing;
242
if (!job_is_ready(&s->common.job) && s->should_converge) {
26
+ BDRVTestState *s, *backing_s;
243
- block_job_event_ready(&s->common);
27
+ enum drain_type outer, inner;
244
+ job_transition_to_ready(&s->common.job);
28
+
245
}
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
246
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
247
job_sleep_ns(&s->common.job, 100000);
31
+ &error_abort);
32
+ s = bs->opaque;
33
+ blk_insert_bs(blk, bs, &error_abort);
34
+
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
36
+ backing_s = backing->opaque;
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
38
+
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
43
+ (inner != BDRV_DRAIN_ALL);
44
+ int backing_quiesce = 0;
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
46
+ (inner != BDRV_DRAIN);
47
+
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
50
+ g_assert_cmpint(s->drain_count, ==, 0);
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
52
+
53
+ do_drain_begin(outer, bs);
54
+ do_drain_begin(inner, bs);
55
+
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
58
+ g_assert_cmpint(s->drain_count, ==, 2);
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
60
+
61
+ do_drain_end(inner, bs);
62
+ do_drain_end(outer, bs);
63
+
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
66
+ g_assert_cmpint(s->drain_count, ==, 0);
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
68
+ }
69
+ }
70
+
71
+ bdrv_unref(backing);
72
+ bdrv_unref(bs);
73
+ blk_unref(blk);
74
+}
75
+
76
77
typedef struct TestBlockJob {
78
BlockJob common;
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
82
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
84
+
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
87
248
--
88
--
249
2.13.6
89
2.13.6
250
90
251
91
diff view generated by jsdifflib
1
This moves the .complete callback that tells a READY job to complete
1
This is in preparation for subtree drains, i.e. drained sections that
2
from BlockJobDriver to JobDriver. The wrapper function job_complete()
2
affect not only a single node, but recursively all child nodes, too.
3
doesn't require anything block job specific any more and can be moved
3
4
to Job.
4
Calling the parent callbacks for drain is pointless when we just came
5
from that parent node recursively and leads to multiple increases of
6
bs->quiesce_counter in a single drain call. Don't do it.
7
8
In order for this to work correctly, the parent callback must be called
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
11
If we have a node N with two parents A and B, recursive draining of A
12
should cause the quiesce_counter of B to increase because its child N is
13
drained independently of B. If now B is recursively drained, too, A must
14
increase its quiesce_counter because N is drained independently of A
15
only now, even if N is going from quiesce_counter 1 to 2.
5
16
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
18
---
9
include/block/blockjob.h | 10 ----------
19
include/block/block.h | 4 ++--
10
include/block/blockjob_int.h | 6 ------
20
block.c | 13 +++++++++----
11
include/qemu/job.h | 8 ++++++++
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
12
block/mirror.c | 10 +++++-----
22
3 files changed, 45 insertions(+), 19 deletions(-)
13
blockdev.c | 2 +-
23
14
blockjob.c | 23 +++++------------------
24
diff --git a/include/block/block.h b/include/block/block.h
15
job.c | 16 ++++++++++++++++
16
tests/test-bdrv-drain.c | 6 +++---
17
tests/test-blockjob.c | 10 +++++-----
18
9 files changed, 43 insertions(+), 48 deletions(-)
19
20
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
21
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/blockjob.h
26
--- a/include/block/block.h
23
+++ b/include/block/blockjob.h
27
+++ b/include/block/block.h
24
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
25
void block_job_cancel(BlockJob *job, bool force);
29
* Begin a quiesced section of all users of @bs. This is part of
30
* bdrv_drained_begin.
31
*/
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
26
34
27
/**
35
/**
28
- * block_job_complete:
36
* bdrv_parent_drained_end:
29
- * @job: The job to be completed.
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
30
- * @errp: Error object.
38
* End a quiesced section of all users of @bs. This is part of
31
- *
39
* bdrv_drained_end.
32
- * Asynchronously complete the specified job.
40
*/
33
- */
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
34
-void block_job_complete(BlockJob *job, Error **errp);
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
35
-
43
36
-
44
/**
37
-/**
45
* bdrv_drained_begin:
38
* block_job_finalize:
46
diff --git a/block.c b/block.c
39
* @job: The job to fully commit and finish.
40
* @errp: Error object.
41
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
42
index XXXXXXX..XXXXXXX 100644
47
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/blockjob_int.h
48
--- a/block.c
44
+++ b/include/block/blockjob_int.h
49
+++ b/block.c
45
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
46
JobDriver job_driver;
51
BlockDriverState *new_bs)
47
52
{
48
/**
53
BlockDriverState *old_bs = child->bs;
49
- * Optional callback for job types whose completion must be triggered
54
+ int i;
50
- * manually.
55
51
- */
56
if (old_bs && new_bs) {
52
- void (*complete)(BlockJob *job, Error **errp);
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
53
-
58
}
54
- /**
59
if (old_bs) {
55
* If the callback is not NULL, prepare will be invoked when all the jobs
60
if (old_bs->quiesce_counter && child->role->drained_end) {
56
* belonging to the same transaction complete; or upon this job's completion
61
- child->role->drained_end(child);
57
* if it is not in a transaction.
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
58
diff --git a/include/qemu/job.h b/include/qemu/job.h
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
97
diff --git a/block/io.c b/block/io.c
59
index XXXXXXX..XXXXXXX 100644
98
index XXXXXXX..XXXXXXX 100644
60
--- a/include/qemu/job.h
99
--- a/block/io.c
61
+++ b/include/qemu/job.h
100
+++ b/block/io.c
62
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
101
@@ -XXX,XX +XXX,XX @@
63
*/
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
64
void (*user_resume)(Job *job);
103
int64_t offset, int bytes, BdrvRequestFlags flags);
65
104
66
+ /**
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
67
+ * Optional callback for job types whose completion must be triggered
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
68
+ * manually.
107
{
69
+ */
108
BdrvChild *c, *next;
70
+ void (*complete)(Job *job, Error **errp);
109
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
111
+ if (c == ignore) {
112
+ continue;
113
+ }
114
if (c->role->drained_begin) {
115
c->role->drained_begin(c);
116
}
117
}
118
}
119
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
71
+
146
+
72
/*
147
static void bdrv_co_drain_bh_cb(void *opaque)
73
* If the callback is not NULL, it will be invoked when the job has to be
148
{
74
* synchronously cancelled or completed; it should drain any activities
149
BdrvCoDrainData *data = opaque;
75
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
76
/** The @job could not be started, free it. */
151
77
void job_early_fail(Job *job);
152
bdrv_dec_in_flight(bs);
78
153
if (data->begin) {
79
+/** Asynchronously complete the specified @job. */
154
- bdrv_drained_begin(bs);
80
+void job_complete(Job *job, Error **errp);;
155
+ bdrv_do_drained_begin(bs, data->parent);
81
156
} else {
82
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
157
- bdrv_drained_end(bs);
83
158
+ bdrv_do_drained_end(bs, data->parent);
84
diff --git a/block/mirror.c b/block/mirror.c
159
}
85
index XXXXXXX..XXXXXXX 100644
160
86
--- a/block/mirror.c
161
data->done = true;
87
+++ b/block/mirror.c
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
88
@@ -XXX,XX +XXX,XX @@ immediate_exit:
163
}
89
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
164
90
}
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
91
166
- bool begin)
92
-static void mirror_complete(BlockJob *job, Error **errp)
167
+ bool begin, BdrvChild *parent)
93
+static void mirror_complete(Job *job, Error **errp)
168
{
94
{
169
BdrvCoDrainData data;
95
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
170
96
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
97
BlockDriverState *target;
172
.bs = bs,
98
173
.done = false,
99
target = blk_bs(s->target);
174
.begin = begin,
100
175
+ .parent = parent,
101
if (!s->synced) {
176
};
102
error_setg(errp, "The active block job '%s' cannot be completed",
177
bdrv_inc_in_flight(bs);
103
- job->job.id);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
104
+ job->id);
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
105
return;
189
return;
106
}
190
}
107
191
108
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
192
/* Stop things in parent-to-child order */
109
.drain = block_job_drain,
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
110
.start = mirror_run,
194
aio_disable_external(bdrv_get_aio_context(bs));
111
.pause = mirror_pause,
195
- bdrv_parent_drained_begin(bs);
112
+ .complete = mirror_complete,
196
}
113
},
197
114
- .complete = mirror_complete,
198
+ bdrv_parent_drained_begin(bs, parent);
115
.attached_aio_context = mirror_attached_aio_context,
199
bdrv_drain_invoke(bs, true, false);
116
.drain = mirror_drain,
200
bdrv_drain_recurse(bs);
117
};
201
}
118
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
202
119
.drain = block_job_drain,
203
-void bdrv_drained_end(BlockDriverState *bs)
120
.start = mirror_run,
204
+void bdrv_drained_begin(BlockDriverState *bs)
121
.pause = mirror_pause,
122
+ .complete = mirror_complete,
123
},
124
- .complete = mirror_complete,
125
.attached_aio_context = mirror_attached_aio_context,
126
.drain = mirror_drain,
127
};
128
diff --git a/blockdev.c b/blockdev.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/blockdev.c
131
+++ b/blockdev.c
132
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_complete(const char *device, Error **errp)
133
}
134
135
trace_qmp_block_job_complete(job);
136
- block_job_complete(job, errp);
137
+ job_complete(&job->job, errp);
138
aio_context_release(aio_context);
139
}
140
141
diff --git a/blockjob.c b/blockjob.c
142
index XXXXXXX..XXXXXXX 100644
143
--- a/blockjob.c
144
+++ b/blockjob.c
145
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
146
return ratelimit_calculate_delay(&job->limit, n);
147
}
148
149
-void block_job_complete(BlockJob *job, Error **errp)
150
-{
151
- /* Should not be reachable via external interface for internal jobs */
152
- assert(job->job.id);
153
- if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
154
- return;
155
- }
156
- if (job->job.pause_count || job_is_cancelled(&job->job) ||
157
- !job->driver->complete)
158
- {
159
- error_setg(errp, "The active block job '%s' cannot be completed",
160
- job->job.id);
161
- return;
162
- }
163
-
164
- job->driver->complete(job, errp);
165
-}
166
-
167
void block_job_finalize(BlockJob *job, Error **errp)
168
{
169
assert(job && job->job.id);
170
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
171
}
172
}
173
174
+static void block_job_complete(BlockJob *job, Error **errp)
175
+{
205
+{
176
+ job_complete(&job->job, errp);
206
+ bdrv_do_drained_begin(bs, NULL);
177
+}
207
+}
178
+
208
+
179
int block_job_complete_sync(BlockJob *job, Error **errp)
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
180
{
210
{
181
return block_job_finish_sync(job, &block_job_complete, errp);
211
int old_quiesce_counter;
182
diff --git a/job.c b/job.c
212
183
index XXXXXXX..XXXXXXX 100644
213
if (qemu_in_coroutine()) {
184
--- a/job.c
214
- bdrv_co_yield_to_drain(bs, false);
185
+++ b/job.c
215
+ bdrv_co_yield_to_drain(bs, false, parent);
186
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
216
return;
187
return 0;
217
}
188
}
218
assert(bs->quiesce_counter > 0);
189
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
190
+void job_complete(Job *job, Error **errp)
220
221
/* Re-enable things in child-to-parent order */
222
bdrv_drain_invoke(bs, false, false);
223
+ bdrv_parent_drained_end(bs, parent);
224
if (old_quiesce_counter == 1) {
225
- bdrv_parent_drained_end(bs);
226
aio_enable_external(bdrv_get_aio_context(bs));
227
}
228
}
229
230
+void bdrv_drained_end(BlockDriverState *bs)
191
+{
231
+{
192
+ /* Should not be reachable via external interface for internal jobs */
232
+ bdrv_do_drained_end(bs, NULL);
193
+ assert(job->id);
194
+ if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) {
195
+ return;
196
+ }
197
+ if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) {
198
+ error_setg(errp, "The active block job '%s' cannot be completed",
199
+ job->id);
200
+ return;
201
+ }
202
+
203
+ job->driver->complete(job, errp);
204
+}
233
+}
205
+
234
+
206
235
/*
207
typedef struct {
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
208
Job *job;
237
* and suspend block driver's internal I/O until next request arrives.
209
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
210
index XXXXXXX..XXXXXXX 100644
239
/* Stop things in parent-to-child order */
211
--- a/tests/test-bdrv-drain.c
240
aio_context_acquire(aio_context);
212
+++ b/tests/test-bdrv-drain.c
241
aio_disable_external(aio_context);
213
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
242
- bdrv_parent_drained_begin(bs);
214
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
243
+ bdrv_parent_drained_begin(bs, NULL);
215
}
244
bdrv_drain_invoke(bs, true, true);
216
245
aio_context_release(aio_context);
217
-static void test_job_complete(BlockJob *job, Error **errp)
246
218
+static void test_job_complete(Job *job, Error **errp)
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
219
{
248
/* Re-enable things in child-to-parent order */
220
- TestBlockJob *s = container_of(job, TestBlockJob, common);
249
aio_context_acquire(aio_context);
221
+ TestBlockJob *s = container_of(job, TestBlockJob, common.job);
250
bdrv_drain_invoke(bs, false, true);
222
s->should_complete = true;
251
- bdrv_parent_drained_end(bs);
223
}
252
+ bdrv_parent_drained_end(bs, NULL);
224
253
aio_enable_external(aio_context);
225
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
254
aio_context_release(aio_context);
226
.user_resume = block_job_user_resume,
255
}
227
.drain = block_job_drain,
228
.start = test_job_start,
229
+ .complete = test_job_complete,
230
},
231
- .complete = test_job_complete,
232
};
233
234
static void test_blockjob_common(enum drain_type drain_type)
235
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
236
index XXXXXXX..XXXXXXX 100644
237
--- a/tests/test-blockjob.c
238
+++ b/tests/test-blockjob.c
239
@@ -XXX,XX +XXX,XX @@ static void cancel_job_completed(Job *job, void *opaque)
240
block_job_completed(bjob, 0);
241
}
242
243
-static void cancel_job_complete(BlockJob *job, Error **errp)
244
+static void cancel_job_complete(Job *job, Error **errp)
245
{
246
- CancelJob *s = container_of(job, CancelJob, common);
247
+ CancelJob *s = container_of(job, CancelJob, common.job);
248
s->should_complete = true;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
252
.user_resume = block_job_user_resume,
253
.drain = block_job_drain,
254
.start = cancel_job_start,
255
+ .complete = cancel_job_complete,
256
},
257
- .complete = cancel_job_complete,
258
};
259
260
static CancelJob *create_common(BlockJob **pjob)
261
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
262
block_job_enter(job);
263
assert(job->job.status == JOB_STATUS_READY);
264
265
- block_job_complete(job, &error_abort);
266
+ job_complete(&job->job, &error_abort);
267
block_job_enter(job);
268
while (!s->completed) {
269
aio_poll(qemu_get_aio_context(), true);
270
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
271
block_job_enter(job);
272
assert(job->job.status == JOB_STATUS_READY);
273
274
- block_job_complete(job, &error_abort);
275
+ job_complete(&job->job, &error_abort);
276
block_job_enter(job);
277
while (!s->completed) {
278
aio_poll(qemu_get_aio_context(), true);
279
--
256
--
280
2.13.6
257
2.13.6
281
258
282
259
diff view generated by jsdifflib
1
There is nothing block layer specific about block_job_sleep_ns(), so
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
move the function to Job.
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
4
5
Add a version that keeps the whole subtree drained. As of this commit,
6
graph changes cannot be allowed during a subtree drained section, but
7
this will be fixed soon.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: John Snow <jsnow@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
10
---
8
include/block/blockjob_int.h | 11 -----------
11
include/block/block.h | 13 +++++++++++++
9
include/qemu/job.h | 19 ++++++++++++++++++-
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
10
block/backup.c | 2 +-
13
2 files changed, 56 insertions(+), 11 deletions(-)
11
block/commit.c | 2 +-
12
block/mirror.c | 4 ++--
13
block/stream.c | 2 +-
14
blockjob.c | 27 ---------------------------
15
job.c | 32 ++++++++++++++++++++++++++++++++
16
tests/test-bdrv-drain.c | 8 ++++----
17
tests/test-blockjob-txn.c | 2 +-
18
tests/test-blockjob.c | 2 +-
19
11 files changed, 61 insertions(+), 50 deletions(-)
20
14
21
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
15
diff --git a/include/block/block.h b/include/block/block.h
22
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob_int.h
17
--- a/include/block/block.h
24
+++ b/include/block/blockjob_int.h
18
+++ b/include/block/block.h
25
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
26
void block_job_free(Job *job);
20
void bdrv_drained_begin(BlockDriverState *bs);
27
21
28
/**
22
/**
29
- * block_job_sleep_ns:
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
30
- * @job: The job that calls the function.
24
+ * exclusive access to all child nodes as well.
31
- * @ns: How many nanoseconds to stop for.
32
- *
33
- * Put the job to sleep (assuming that it wasn't canceled) for @ns
34
- * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately
35
- * interrupt the wait.
36
- */
37
-void block_job_sleep_ns(BlockJob *job, int64_t ns);
38
-
39
-/**
40
* block_job_yield:
41
* @job: The job that calls the function.
42
*
43
diff --git a/include/qemu/job.h b/include/qemu/job.h
44
index XXXXXXX..XXXXXXX 100644
45
--- a/include/qemu/job.h
46
+++ b/include/qemu/job.h
47
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
48
Coroutine *co;
49
50
/**
51
- * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in
52
+ * Timer that is used by @job_sleep_ns. Accessed under job_mutex (in
53
* job.c).
54
*/
55
QEMUTimer sleep_timer;
56
@@ -XXX,XX +XXX,XX @@ void job_enter_cond(Job *job, bool(*fn)(Job *job));
57
void job_start(Job *job);
58
59
/**
60
+ * @job: The job to enter.
61
+ *
25
+ *
62
+ * Continue the specified job by entering the coroutine.
26
+ * Graph changes are not allowed during a subtree drain section.
63
+ */
27
+ */
64
+void job_enter(Job *job);
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
65
+
29
+
66
+/**
30
+/**
67
* @job: The job that is ready to pause.
31
* bdrv_drained_end:
68
*
32
*
69
* Pause now if job_pause() has been called. Jobs that perform lots of I/O
33
* End a quiescent section started by bdrv_drained_begin().
70
@@ -XXX,XX +XXX,XX @@ void job_start(Job *job);
71
*/
34
*/
72
void coroutine_fn job_pause_point(Job *job);
35
void bdrv_drained_end(BlockDriverState *bs);
73
36
74
+/**
37
+/**
75
+ * @job: The job that calls the function.
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
76
+ * @ns: How many nanoseconds to stop for.
77
+ *
78
+ * Put the job to sleep (assuming that it wasn't canceled) for @ns
79
+ * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately
80
+ * interrupt the wait.
81
+ */
39
+ */
82
+void coroutine_fn job_sleep_ns(Job *job, int64_t ns);
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
83
+
41
+
84
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
85
/** Returns the JobType of a given Job. */
43
Error **errp);
86
JobType job_type(const Job *job);
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
87
diff --git a/block/backup.c b/block/backup.c
45
diff --git a/block/io.c b/block/io.c
88
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
89
--- a/block/backup.c
47
--- a/block/io.c
90
+++ b/block/backup.c
48
+++ b/block/io.c
91
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
92
* return. Without a yield, the VM would not reboot. */
50
BlockDriverState *bs;
93
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
51
bool done;
94
job->bytes_read = 0;
52
bool begin;
95
- block_job_sleep_ns(&job->common, delay_ns);
53
+ bool recursive;
96
+ job_sleep_ns(&job->common.job, delay_ns);
54
BdrvChild *parent;
97
55
} BdrvCoDrainData;
98
if (job_is_cancelled(&job->common.job)) {
56
99
return true;
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
100
diff --git a/block/commit.c b/block/commit.c
58
return waited;
101
index XXXXXXX..XXXXXXX 100644
59
}
102
--- a/block/commit.c
60
103
+++ b/block/commit.c
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
104
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
105
/* Note that even when no rate limit is applied we need to yield
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
* with no pending I/O here so that bdrv_drain_all() returns.
64
+ BdrvChild *parent);
107
*/
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
108
- block_job_sleep_ns(&s->common, delay_ns);
66
+ BdrvChild *parent);
109
+ job_sleep_ns(&s->common.job, delay_ns);
67
110
if (job_is_cancelled(&s->common.job)) {
68
static void bdrv_co_drain_bh_cb(void *opaque)
111
break;
69
{
112
}
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
113
diff --git a/block/mirror.c b/block/mirror.c
71
114
index XXXXXXX..XXXXXXX 100644
72
bdrv_dec_in_flight(bs);
115
--- a/block/mirror.c
73
if (data->begin) {
116
+++ b/block/mirror.c
74
- bdrv_do_drained_begin(bs, data->parent);
117
@@ -XXX,XX +XXX,XX @@ static void mirror_throttle(MirrorBlockJob *s)
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
118
119
if (now - s->last_pause_ns > BLOCK_JOB_SLICE_TIME) {
120
s->last_pause_ns = now;
121
- block_job_sleep_ns(&s->common, 0);
122
+ job_sleep_ns(&s->common.job, 0);
123
} else {
76
} else {
124
job_pause_point(&s->common.job);
77
- bdrv_do_drained_end(bs, data->parent);
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
125
}
79
}
126
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
80
127
cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0);
81
data->done = true;
128
}
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
129
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
130
- block_job_sleep_ns(&s->common, delay_ns);
131
+ job_sleep_ns(&s->common.job, delay_ns);
132
if (job_is_cancelled(&s->common.job) &&
133
(!s->synced || s->common.force))
134
{
135
diff --git a/block/stream.c b/block/stream.c
136
index XXXXXXX..XXXXXXX 100644
137
--- a/block/stream.c
138
+++ b/block/stream.c
139
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
140
/* Note that even when no rate limit is applied we need to yield
141
* with no pending I/O here so that bdrv_drain_all() returns.
142
*/
143
- block_job_sleep_ns(&s->common, delay_ns);
144
+ job_sleep_ns(&s->common.job, delay_ns);
145
if (job_is_cancelled(&s->common.job)) {
146
break;
147
}
148
diff --git a/blockjob.c b/blockjob.c
149
index XXXXXXX..XXXXXXX 100644
150
--- a/blockjob.c
151
+++ b/blockjob.c
152
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
153
block_job_detach_aio_context, bjob);
154
blk_unref(bjob->blk);
155
error_free(bjob->blocker);
156
- assert(!timer_pending(&bjob->job.sleep_timer));
157
}
83
}
158
84
159
static void block_job_attached_aio_context(AioContext *new_context,
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
160
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
86
- bool begin, BdrvChild *parent)
161
return job->driver;
87
+ bool begin, bool recursive,
88
+ BdrvChild *parent)
89
{
90
BdrvCoDrainData data;
91
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
162
}
102
}
163
103
164
-static void block_job_sleep_timer_cb(void *opaque)
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
165
-{
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
166
- BlockJob *job = opaque;
106
+ BdrvChild *parent)
167
-
168
- block_job_enter(job);
169
-}
170
-
171
static void block_job_decommission(BlockJob *job)
172
{
107
{
173
assert(job);
108
+ BdrvChild *child, *next;
174
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
109
+
175
job->opaque = opaque;
110
if (qemu_in_coroutine()) {
176
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
111
- bdrv_co_yield_to_drain(bs, true, parent);
177
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
178
- aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer,
113
return;
179
- QEMU_CLOCK_REALTIME, SCALE_NS,
114
}
180
- block_job_sleep_timer_cb, job);
115
181
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
182
error_setg(&job->blocker, "block device is in use by block job: %s",
117
bdrv_parent_drained_begin(bs, parent);
183
job_type_str(&job->job));
118
bdrv_drain_invoke(bs, true, false);
184
@@ -XXX,XX +XXX,XX @@ void block_job_enter(BlockJob *job)
119
bdrv_drain_recurse(bs);
185
job_enter_cond(&job->job, NULL);
120
+
121
+ if (recursive) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
+ bdrv_do_drained_begin(child->bs, true, child);
124
+ }
125
+ }
186
}
126
}
187
127
188
-void block_job_sleep_ns(BlockJob *job, int64_t ns)
128
void bdrv_drained_begin(BlockDriverState *bs)
189
-{
190
- assert(job->job.busy);
191
-
192
- /* Check cancellation *before* setting busy = false, too! */
193
- if (job_is_cancelled(&job->job)) {
194
- return;
195
- }
196
-
197
- if (!job_should_pause(&job->job)) {
198
- job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
199
- }
200
-
201
- job_pause_point(&job->job);
202
-}
203
-
204
void block_job_yield(BlockJob *job)
205
{
129
{
206
assert(job->job.busy);
130
- bdrv_do_drained_begin(bs, NULL);
207
diff --git a/job.c b/job.c
131
+ bdrv_do_drained_begin(bs, false, NULL);
208
index XXXXXXX..XXXXXXX 100644
209
--- a/job.c
210
+++ b/job.c
211
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id)
212
return NULL;
213
}
214
215
+static void job_sleep_timer_cb(void *opaque)
216
+{
217
+ Job *job = opaque;
218
+
219
+ job_enter(job);
220
+}
132
+}
221
+
133
+
222
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
223
Error **errp)
135
+{
136
+ bdrv_do_drained_begin(bs, true, NULL);
137
}
138
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
224
{
142
{
225
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
143
+ BdrvChild *child, *next;
226
job->pause_count = 1;
144
int old_quiesce_counter;
227
145
228
job_state_transition(job, JOB_STATUS_CREATED);
146
if (qemu_in_coroutine()) {
229
+ aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
147
- bdrv_co_yield_to_drain(bs, false, parent);
230
+ QEMU_CLOCK_REALTIME, SCALE_NS,
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
231
+ job_sleep_timer_cb, job);
149
return;
232
150
}
233
QLIST_INSERT_HEAD(&jobs, job, job_list);
151
assert(bs->quiesce_counter > 0);
234
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
235
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
153
if (old_quiesce_counter == 1) {
154
aio_enable_external(bdrv_get_aio_context(bs));
155
}
156
+
157
+ if (recursive) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
159
+ bdrv_do_drained_end(child->bs, true, child);
160
+ }
161
+ }
162
}
163
164
void bdrv_drained_end(BlockDriverState *bs)
236
{
165
{
237
if (--job->refcnt == 0) {
166
- bdrv_do_drained_end(bs, NULL);
238
assert(job->status == JOB_STATUS_NULL);
167
+ bdrv_do_drained_end(bs, false, NULL);
239
+ assert(!timer_pending(&job->sleep_timer));
240
241
if (job->driver->free) {
242
job->driver->free(job);
243
@@ -XXX,XX +XXX,XX @@ void job_enter_cond(Job *job, bool(*fn)(Job *job))
244
aio_co_wake(job->co);
245
}
246
247
+void job_enter(Job *job)
248
+{
249
+ job_enter_cond(job, NULL);
250
+}
168
+}
251
+
169
+
252
/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
253
* Reentering the job coroutine with block_job_enter() before the timer has
171
+{
254
* expired is allowed and cancels the timer.
172
+ bdrv_do_drained_end(bs, true, NULL);
255
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job)
256
}
257
}
173
}
258
174
259
+void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
175
/*
260
+{
261
+ assert(job->busy);
262
+
263
+ /* Check cancellation *before* setting busy = false, too! */
264
+ if (job_is_cancelled(job)) {
265
+ return;
266
+ }
267
+
268
+ if (!job_should_pause(job)) {
269
+ job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
270
+ }
271
+
272
+ job_pause_point(job);
273
+}
274
+
275
/**
276
* All jobs must allow a pause point before entering their job proper. This
277
* ensures that jobs can be paused prior to being started, then resumed later.
278
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/tests/test-bdrv-drain.c
281
+++ b/tests/test-bdrv-drain.c
282
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
283
284
block_job_event_ready(&s->common);
285
while (!s->should_complete) {
286
- block_job_sleep_ns(&s->common, 100000);
287
+ job_sleep_ns(&s->common.job, 100000);
288
}
289
290
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
291
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
292
293
g_assert_cmpint(job->job.pause_count, ==, 0);
294
g_assert_false(job->job.paused);
295
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
296
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
297
298
do_drain_begin(drain_type, src);
299
300
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
301
302
g_assert_cmpint(job->job.pause_count, ==, 0);
303
g_assert_false(job->job.paused);
304
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
305
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
306
307
do_drain_begin(drain_type, target);
308
309
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
310
311
g_assert_cmpint(job->job.pause_count, ==, 0);
312
g_assert_false(job->job.paused);
313
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
314
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
315
316
ret = block_job_complete_sync(job, &error_abort);
317
g_assert_cmpint(ret, ==, 0);
318
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
319
index XXXXXXX..XXXXXXX 100644
320
--- a/tests/test-blockjob-txn.c
321
+++ b/tests/test-blockjob-txn.c
322
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
323
324
while (s->iterations--) {
325
if (s->use_timer) {
326
- block_job_sleep_ns(job, 0);
327
+ job_sleep_ns(&job->job, 0);
328
} else {
329
block_job_yield(job);
330
}
331
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
332
index XXXXXXX..XXXXXXX 100644
333
--- a/tests/test-blockjob.c
334
+++ b/tests/test-blockjob.c
335
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
336
block_job_event_ready(&s->common);
337
}
338
339
- block_job_sleep_ns(&s->common, 100000);
340
+ job_sleep_ns(&s->common.job, 100000);
341
}
342
343
defer:
344
--
176
--
345
2.13.6
177
2.13.6
346
178
347
179
diff view generated by jsdifflib
1
This moves the finalisation of a single job from BlockJob to Job.
1
Add a subtree drain version to the existing test cases.
2
3
Some part of this code depends on job transactions, and job transactions
4
call this code, we introduce some temporary calls from Job functions to
5
BlockJob ones. This will be fixed once transactions move to Job, too.
6
2
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
4
---
10
include/block/blockjob.h | 9 ---
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
11
include/block/blockjob_int.h | 36 -----------
6
1 file changed, 26 insertions(+), 1 deletion(-)
12
include/qemu/job.h | 53 +++++++++++++++-
13
block/backup.c | 22 +++----
14
block/commit.c | 2 +-
15
block/mirror.c | 2 +-
16
blockjob.c | 142 ++++++++-----------------------------------
17
job.c | 100 +++++++++++++++++++++++++++++-
18
qemu-img.c | 2 +-
19
tests/test-blockjob.c | 10 +--
20
10 files changed, 194 insertions(+), 184 deletions(-)
21
7
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
23
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
10
--- a/tests/test-bdrv-drain.c
25
+++ b/include/block/blockjob.h
11
+++ b/tests/test-bdrv-drain.c
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
27
/** Rate limiting data structure for implementing @speed. */
13
enum drain_type {
28
RateLimit limit;
14
BDRV_DRAIN_ALL,
29
15
BDRV_DRAIN,
30
- /** The completion function that will be called when the job completes. */
16
+ BDRV_SUBTREE_DRAIN,
31
- BlockCompletionFunc *cb;
17
DRAIN_TYPE_MAX,
32
-
33
/** Block other operations when block job is running */
34
Error *blocker;
35
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
37
/** BlockDriverStates that are involved in this block job */
38
GSList *nodes;
39
40
- /** The opaque value that is passed to the completion function. */
41
- void *opaque;
42
-
43
- /** ret code passed to block_job_completed. */
44
- int ret;
45
-
46
BlockJobTxn *txn;
47
QLIST_ENTRY(BlockJob) txn_list;
48
} BlockJob;
49
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
50
index XXXXXXX..XXXXXXX 100644
51
--- a/include/block/blockjob_int.h
52
+++ b/include/block/blockjob_int.h
53
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
54
*/
55
int (*prepare)(BlockJob *job);
56
57
- /**
58
- * If the callback is not NULL, it will be invoked when all the jobs
59
- * belonging to the same transaction complete; or upon this job's
60
- * completion if it is not in a transaction. Skipped if NULL.
61
- *
62
- * All jobs will complete with a call to either .commit() or .abort() but
63
- * never both.
64
- */
65
- void (*commit)(BlockJob *job);
66
-
67
- /**
68
- * If the callback is not NULL, it will be invoked when any job in the
69
- * same transaction fails; or upon this job's failure (due to error or
70
- * cancellation) if it is not in a transaction. Skipped if NULL.
71
- *
72
- * All jobs will complete with a call to either .commit() or .abort() but
73
- * never both.
74
- */
75
- void (*abort)(BlockJob *job);
76
-
77
- /**
78
- * If the callback is not NULL, it will be invoked after a call to either
79
- * .commit() or .abort(). Regardless of which callback is invoked after
80
- * completion, .clean() will always be called, even if the job does not
81
- * belong to a transaction group.
82
- */
83
- void (*clean)(BlockJob *job);
84
-
85
/*
86
* If the callback is not NULL, it will be invoked before the job is
87
* resumed in a new AioContext. This is the place to move any resources
88
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job);
89
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
90
91
/**
92
- * block_job_early_fail:
93
- * @bs: The block device.
94
- *
95
- * The block job could not be started, free it.
96
- */
97
-void block_job_early_fail(BlockJob *job);
98
-
99
-/**
100
* block_job_completed:
101
* @job: The job being completed.
102
* @ret: The status code.
103
diff --git a/include/qemu/job.h b/include/qemu/job.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/qemu/job.h
106
+++ b/include/qemu/job.h
107
@@ -XXX,XX +XXX,XX @@
108
#include "qapi/qapi-types-block-core.h"
109
#include "qemu/queue.h"
110
#include "qemu/coroutine.h"
111
+#include "block/aio.h"
112
113
typedef struct JobDriver JobDriver;
114
115
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
116
/** True if this job should automatically dismiss itself */
117
bool auto_dismiss;
118
119
+ /** ret code passed to block_job_completed. */
120
+ int ret;
121
+
122
+ /** The completion function that will be called when the job completes. */
123
+ BlockCompletionFunc *cb;
124
+
125
+ /** The opaque value that is passed to the completion function. */
126
+ void *opaque;
127
+
128
/** Notifiers called when a cancelled job is finalised */
129
NotifierList on_finalize_cancelled;
130
131
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
132
*/
133
void (*user_resume)(Job *job);
134
135
+ /**
136
+ * If the callback is not NULL, it will be invoked when all the jobs
137
+ * belonging to the same transaction complete; or upon this job's
138
+ * completion if it is not in a transaction. Skipped if NULL.
139
+ *
140
+ * All jobs will complete with a call to either .commit() or .abort() but
141
+ * never both.
142
+ */
143
+ void (*commit)(Job *job);
144
+
145
+ /**
146
+ * If the callback is not NULL, it will be invoked when any job in the
147
+ * same transaction fails; or upon this job's failure (due to error or
148
+ * cancellation) if it is not in a transaction. Skipped if NULL.
149
+ *
150
+ * All jobs will complete with a call to either .commit() or .abort() but
151
+ * never both.
152
+ */
153
+ void (*abort)(Job *job);
154
+
155
+ /**
156
+ * If the callback is not NULL, it will be invoked after a call to either
157
+ * .commit() or .abort(). Regardless of which callback is invoked after
158
+ * completion, .clean() will always be called, even if the job does not
159
+ * belong to a transaction group.
160
+ */
161
+ void (*clean)(Job *job);
162
+
163
+
164
/** Called when the job is freed */
165
void (*free)(Job *job);
166
};
18
};
167
@@ -XXX,XX +XXX,XX @@ typedef enum JobCreateFlags {
19
168
* @driver: The class object for the newly-created job.
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
169
* @ctx: The AioContext to run the job coroutine in.
21
switch (drain_type) {
170
* @flags: Creation flags for the job. See @JobCreateFlags.
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
171
+ * @cb: Completion function for the job.
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
172
+ * @opaque: Opaque pointer value passed to @cb.
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
173
* @errp: Error object.
25
default: g_assert_not_reached();
174
*/
175
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
176
- int flags, Error **errp);
177
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
178
179
/**
180
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
181
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id);
182
*/
183
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
184
185
+/** The @job could not be started, free it. */
186
+void job_early_fail(Job *job);
187
+
188
+
189
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
190
191
/**
192
@@ -XXX,XX +XXX,XX @@ void job_state_transition(Job *job, JobStatus s1);
193
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
194
bool job_should_pause(Job *job);
195
bool job_started(Job *job);
196
+void job_do_dismiss(Job *job);
197
+int job_finalize_single(Job *job);
198
+void job_update_rc(Job *job);
199
+
200
+typedef struct BlockJob BlockJob;
201
+void block_job_txn_del_job(BlockJob *job);
202
203
#endif
204
diff --git a/block/backup.c b/block/backup.c
205
index XXXXXXX..XXXXXXX 100644
206
--- a/block/backup.c
207
+++ b/block/backup.c
208
@@ -XXX,XX +XXX,XX @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
209
}
26
}
210
}
27
}
211
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
212
-static void backup_commit(BlockJob *job)
29
switch (drain_type) {
213
+static void backup_commit(Job *job)
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
214
{
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
215
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
216
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
33
default: g_assert_not_reached();
217
if (s->sync_bitmap) {
218
backup_cleanup_sync_bitmap(s, 0);
219
}
34
}
220
}
35
}
221
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
222
-static void backup_abort(BlockJob *job)
37
test_drv_cb_common(BDRV_DRAIN, false);
223
+static void backup_abort(Job *job)
224
{
225
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
226
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
227
if (s->sync_bitmap) {
228
backup_cleanup_sync_bitmap(s, -1);
229
}
230
}
38
}
231
39
232
-static void backup_clean(BlockJob *job)
40
+static void test_drv_cb_drain_subtree(void)
233
+static void backup_clean(Job *job)
234
{
235
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
236
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
237
assert(s->target);
238
blk_unref(s->target);
239
s->target = NULL;
240
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
241
.free = block_job_free,
242
.user_resume = block_job_user_resume,
243
.start = backup_run,
244
+ .commit = backup_commit,
245
+ .abort = backup_abort,
246
+ .clean = backup_clean,
247
},
248
- .commit = backup_commit,
249
- .abort = backup_abort,
250
- .clean = backup_clean,
251
.attached_aio_context = backup_attached_aio_context,
252
.drain = backup_drain,
253
};
254
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
255
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
256
}
257
if (job) {
258
- backup_clean(&job->common);
259
- block_job_early_fail(&job->common);
260
+ backup_clean(&job->common.job);
261
+ job_early_fail(&job->common.job);
262
}
263
264
return NULL;
265
diff --git a/block/commit.c b/block/commit.c
266
index XXXXXXX..XXXXXXX 100644
267
--- a/block/commit.c
268
+++ b/block/commit.c
269
@@ -XXX,XX +XXX,XX @@ fail:
270
if (commit_top_bs) {
271
bdrv_replace_node(commit_top_bs, top, &error_abort);
272
}
273
- block_job_early_fail(&s->common);
274
+ job_early_fail(&s->common.job);
275
}
276
277
278
diff --git a/block/mirror.c b/block/mirror.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/block/mirror.c
281
+++ b/block/mirror.c
282
@@ -XXX,XX +XXX,XX @@ fail:
283
284
g_free(s->replaces);
285
blk_unref(s->target);
286
- block_job_early_fail(&s->common);
287
+ job_early_fail(&s->common.job);
288
}
289
290
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
291
diff --git a/blockjob.c b/blockjob.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockjob.c
294
+++ b/blockjob.c
295
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
296
block_job_txn_ref(txn);
297
}
298
299
-static void block_job_txn_del_job(BlockJob *job)
300
+void block_job_txn_del_job(BlockJob *job)
301
{
302
if (job->txn) {
303
QLIST_REMOVE(job, txn_list);
304
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
305
return job->driver;
306
}
307
308
-static void block_job_decommission(BlockJob *job)
309
-{
310
- assert(job);
311
- job->job.busy = false;
312
- job->job.paused = false;
313
- job->job.deferred_to_main_loop = true;
314
- block_job_txn_del_job(job);
315
- job_state_transition(&job->job, JOB_STATUS_NULL);
316
- job_unref(&job->job);
317
-}
318
-
319
-static void block_job_do_dismiss(BlockJob *job)
320
-{
321
- block_job_decommission(job);
322
-}
323
-
324
-static void block_job_conclude(BlockJob *job)
325
-{
326
- job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
327
- if (job->job.auto_dismiss || !job_started(&job->job)) {
328
- block_job_do_dismiss(job);
329
- }
330
-}
331
-
332
-static void block_job_update_rc(BlockJob *job)
333
-{
334
- if (!job->ret && job_is_cancelled(&job->job)) {
335
- job->ret = -ECANCELED;
336
- }
337
- if (job->ret) {
338
- job_state_transition(&job->job, JOB_STATUS_ABORTING);
339
- }
340
-}
341
-
342
static int block_job_prepare(BlockJob *job)
343
{
344
- if (job->ret == 0 && job->driver->prepare) {
345
- job->ret = job->driver->prepare(job);
346
- }
347
- return job->ret;
348
-}
349
-
350
-static void block_job_commit(BlockJob *job)
351
-{
352
- assert(!job->ret);
353
- if (job->driver->commit) {
354
- job->driver->commit(job);
355
- }
356
-}
357
-
358
-static void block_job_abort(BlockJob *job)
359
-{
360
- assert(job->ret);
361
- if (job->driver->abort) {
362
- job->driver->abort(job);
363
- }
364
-}
365
-
366
-static void block_job_clean(BlockJob *job)
367
-{
368
- if (job->driver->clean) {
369
- job->driver->clean(job);
370
+ if (job->job.ret == 0 && job->driver->prepare) {
371
+ job->job.ret = job->driver->prepare(job);
372
}
373
-}
374
-
375
-static int block_job_finalize_single(BlockJob *job)
376
-{
377
- assert(job_is_completed(&job->job));
378
-
379
- /* Ensure abort is called for late-transactional failures */
380
- block_job_update_rc(job);
381
-
382
- if (!job->ret) {
383
- block_job_commit(job);
384
- } else {
385
- block_job_abort(job);
386
- }
387
- block_job_clean(job);
388
-
389
- if (job->cb) {
390
- job->cb(job->opaque, job->ret);
391
- }
392
-
393
- /* Emit events only if we actually started */
394
- if (job_started(&job->job)) {
395
- if (job_is_cancelled(&job->job)) {
396
- job_event_cancelled(&job->job);
397
- } else {
398
- job_event_completed(&job->job);
399
- }
400
- }
401
-
402
- block_job_txn_del_job(job);
403
- block_job_conclude(job);
404
- return 0;
405
+ return job->job.ret;
406
}
407
408
static void block_job_cancel_async(BlockJob *job, bool force)
409
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
410
while (!job_is_completed(&job->job)) {
411
aio_poll(qemu_get_aio_context(), true);
412
}
413
- ret = (job_is_cancelled(&job->job) && job->ret == 0)
414
- ? -ECANCELED : job->ret;
415
+ ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
416
+ ? -ECANCELED : job->job.ret;
417
job_unref(&job->job);
418
return ret;
419
}
420
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
421
assert(job_is_cancelled(&other_job->job));
422
block_job_finish_sync(other_job, NULL, NULL);
423
}
424
- block_job_finalize_single(other_job);
425
+ job_finalize_single(&other_job->job);
426
aio_context_release(ctx);
427
}
428
429
@@ -XXX,XX +XXX,XX @@ static int block_job_needs_finalize(BlockJob *job)
430
return !job->job.auto_finalize;
431
}
432
433
+static int block_job_finalize_single(BlockJob *job)
434
+{
41
+{
435
+ return job_finalize_single(&job->job);
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
436
+}
43
+}
437
+
44
+
438
static void block_job_do_finalize(BlockJob *job)
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
439
{
46
{
440
int rc;
47
BlockBackend *blk;
441
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
442
if (!job_is_completed(&other_job->job)) {
49
test_quiesce_common(BDRV_DRAIN, false);
443
return;
444
}
445
- assert(other_job->ret == 0);
446
+ assert(other_job->job.ret == 0);
447
}
448
449
block_job_txn_apply(txn, block_job_transition_to_pending, false);
450
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
451
return;
452
}
453
454
- block_job_do_dismiss(job);
455
+ job_do_dismiss(&job->job);
456
*jobptr = NULL;
457
}
50
}
458
51
459
void block_job_cancel(BlockJob *job, bool force)
52
+static void test_quiesce_drain_subtree(void)
460
{
461
if (job->job.status == JOB_STATUS_CONCLUDED) {
462
- block_job_do_dismiss(job);
463
+ job_do_dismiss(&job->job);
464
return;
465
}
466
block_job_cancel_async(job, force);
467
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
468
info->status = job->job.status;
469
info->auto_finalize = job->job.auto_finalize;
470
info->auto_dismiss = job->job.auto_dismiss;
471
- info->has_error = job->ret != 0;
472
- info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
473
+ info->has_error = job->job.ret != 0;
474
+ info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
475
return info;
476
}
477
478
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
479
return;
480
}
481
482
- if (job->ret < 0) {
483
- msg = strerror(-job->ret);
484
+ if (job->job.ret < 0) {
485
+ msg = strerror(-job->job.ret);
486
}
487
488
qapi_event_send_block_job_completed(job_type(&job->job),
489
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
490
}
491
492
job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
493
- flags, errp);
494
+ flags, cb, opaque, errp);
495
if (job == NULL) {
496
blk_unref(blk);
497
return NULL;
498
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
499
500
job->driver = driver;
501
job->blk = blk;
502
- job->cb = cb;
503
- job->opaque = opaque;
504
505
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
506
job->finalize_completed_notifier.notify = block_job_event_completed;
507
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
508
509
block_job_set_speed(job, speed, &local_err);
510
if (local_err) {
511
- block_job_early_fail(job);
512
+ job_early_fail(&job->job);
513
error_propagate(errp, local_err);
514
return NULL;
515
}
516
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
517
return job;
518
}
519
520
-void block_job_early_fail(BlockJob *job)
521
-{
522
- assert(job->job.status == JOB_STATUS_CREATED);
523
- block_job_decommission(job);
524
-}
525
-
526
void block_job_completed(BlockJob *job, int ret)
527
{
528
assert(job && job->txn && !job_is_completed(&job->job));
529
assert(blk_bs(job->blk)->job == job);
530
- job->ret = ret;
531
- block_job_update_rc(job);
532
- trace_block_job_completed(job, ret, job->ret);
533
- if (job->ret) {
534
+ job->job.ret = ret;
535
+ job_update_rc(&job->job);
536
+ trace_block_job_completed(job, ret, job->job.ret);
537
+ if (job->job.ret) {
538
block_job_completed_txn_abort(job);
539
} else {
540
block_job_completed_txn_success(job);
541
diff --git a/job.c b/job.c
542
index XXXXXXX..XXXXXXX 100644
543
--- a/job.c
544
+++ b/job.c
545
@@ -XXX,XX +XXX,XX @@ void job_state_transition(Job *job, JobStatus s1)
546
{
547
JobStatus s0 = job->status;
548
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
549
- trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
550
+ trace_job_state_transition(job, job->ret,
551
JobSTT[s0][s1] ? "allowed" : "disallowed",
552
JobStatus_str(s0), JobStatus_str(s1));
553
assert(JobSTT[s0][s1]);
554
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
555
}
556
557
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
558
- int flags, Error **errp)
559
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp)
560
{
561
Job *job;
562
563
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
564
job->pause_count = 1;
565
job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
566
job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
567
+ job->cb = cb;
568
+ job->opaque = opaque;
569
570
notifier_list_init(&job->on_finalize_cancelled);
571
notifier_list_init(&job->on_finalize_completed);
572
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
573
job_resume(job);
574
}
575
576
+void job_do_dismiss(Job *job)
577
+{
53
+{
578
+ assert(job);
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
579
+ job->busy = false;
580
+ job->paused = false;
581
+ job->deferred_to_main_loop = true;
582
+
583
+ /* TODO Don't assume it's a BlockJob */
584
+ block_job_txn_del_job((BlockJob*) job);
585
+
586
+ job_state_transition(job, JOB_STATUS_NULL);
587
+ job_unref(job);
588
+}
55
+}
589
+
56
+
590
+void job_early_fail(Job *job)
57
static void test_nested(void)
58
{
59
BlockBackend *blk;
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
61
/* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
62
int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
63
(inner != BDRV_DRAIN_ALL);
64
- int backing_quiesce = 0;
65
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
66
+ (inner == BDRV_SUBTREE_DRAIN);
67
int backing_cb_cnt = (outer != BDRV_DRAIN) +
68
(inner != BDRV_DRAIN);
69
70
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
71
test_blockjob_common(BDRV_DRAIN);
72
}
73
74
+static void test_blockjob_drain_subtree(void)
591
+{
75
+{
592
+ assert(job->status == JOB_STATUS_CREATED);
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
593
+ job_do_dismiss(job);
594
+}
77
+}
595
+
78
+
596
+static void job_conclude(Job *job)
79
int main(int argc, char **argv)
597
+{
80
{
598
+ job_state_transition(job, JOB_STATUS_CONCLUDED);
81
bdrv_init();
599
+ if (job->auto_dismiss || !job_started(job)) {
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
600
+ job_do_dismiss(job);
83
601
+ }
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
602
+}
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
603
+
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
604
+void job_update_rc(Job *job)
87
+ test_drv_cb_drain_subtree);
605
+{
88
606
+ if (!job->ret && job_is_cancelled(job)) {
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
607
+ job->ret = -ECANCELED;
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
608
+ }
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
609
+ if (job->ret) {
92
+ test_quiesce_drain_subtree);
610
+ job_state_transition(job, JOB_STATUS_ABORTING);
93
611
+ }
94
g_test_add_func("/bdrv-drain/nested", test_nested);
612
+}
95
613
+
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
614
+static void job_commit(Job *job)
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
615
+{
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
616
+ assert(!job->ret);
99
+ test_blockjob_drain_subtree);
617
+ if (job->driver->commit) {
100
618
+ job->driver->commit(job);
101
return g_test_run();
619
+ }
102
}
620
+}
621
+
622
+static void job_abort(Job *job)
623
+{
624
+ assert(job->ret);
625
+ if (job->driver->abort) {
626
+ job->driver->abort(job);
627
+ }
628
+}
629
+
630
+static void job_clean(Job *job)
631
+{
632
+ if (job->driver->clean) {
633
+ job->driver->clean(job);
634
+ }
635
+}
636
+
637
+int job_finalize_single(Job *job)
638
+{
639
+ assert(job_is_completed(job));
640
+
641
+ /* Ensure abort is called for late-transactional failures */
642
+ job_update_rc(job);
643
+
644
+ if (!job->ret) {
645
+ job_commit(job);
646
+ } else {
647
+ job_abort(job);
648
+ }
649
+ job_clean(job);
650
+
651
+ if (job->cb) {
652
+ job->cb(job->opaque, job->ret);
653
+ }
654
+
655
+ /* Emit events only if we actually started */
656
+ if (job_started(job)) {
657
+ if (job_is_cancelled(job)) {
658
+ job_event_cancelled(job);
659
+ } else {
660
+ job_event_completed(job);
661
+ }
662
+ }
663
+
664
+ /* TODO Don't assume it's a BlockJob */
665
+ block_job_txn_del_job((BlockJob*) job);
666
+ job_conclude(job);
667
+ return 0;
668
+}
669
+
670
671
typedef struct {
672
Job *job;
673
diff --git a/qemu-img.c b/qemu-img.c
674
index XXXXXXX..XXXXXXX 100644
675
--- a/qemu-img.c
676
+++ b/qemu-img.c
677
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
678
if (!job_is_completed(&job->job)) {
679
ret = block_job_complete_sync(job, errp);
680
} else {
681
- ret = job->ret;
682
+ ret = job->job.ret;
683
}
684
job_unref(&job->job);
685
aio_context_release(aio_context);
686
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
687
index XXXXXXX..XXXXXXX 100644
688
--- a/tests/test-blockjob.c
689
+++ b/tests/test-blockjob.c
690
@@ -XXX,XX +XXX,XX @@ static void test_job_ids(void)
691
job[1] = do_test_id(blk[1], "id0", false);
692
693
/* But once job[0] finishes we can reuse its ID */
694
- block_job_early_fail(job[0]);
695
+ job_early_fail(&job[0]->job);
696
job[1] = do_test_id(blk[1], "id0", true);
697
698
/* No job ID specified, defaults to the backend name ('drive1') */
699
- block_job_early_fail(job[1]);
700
+ job_early_fail(&job[1]->job);
701
job[1] = do_test_id(blk[1], NULL, true);
702
703
/* Duplicate job ID */
704
@@ -XXX,XX +XXX,XX @@ static void test_job_ids(void)
705
/* This one is valid */
706
job[2] = do_test_id(blk[2], "id_2", true);
707
708
- block_job_early_fail(job[0]);
709
- block_job_early_fail(job[1]);
710
- block_job_early_fail(job[2]);
711
+ job_early_fail(&job[0]->job);
712
+ job_early_fail(&job[1]->job);
713
+ job_early_fail(&job[2]->job);
714
715
destroy_blk(blk[0]);
716
destroy_blk(blk[1]);
717
--
103
--
718
2.13.6
104
2.13.6
719
105
720
106
diff view generated by jsdifflib
1
This is the first step towards creating an infrastructure for generic
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
background jobs that aren't tied to a block device. For now, Job only
2
first use a BH to get out of the coroutine context. Call some existing
3
stores its ID and JobDriver, the rest stays in BlockJob.
3
tests again from a coroutine to cover this code path.
4
5
The following patches will move over more parts of BlockJob to Job if
6
they are meaningful outside the context of a block job.
7
8
BlockJob.driver is now redundant, but this patch leaves it around to
9
avoid unnecessary churn. The next patches will get rid of almost all of
10
its uses anyway so that it can be removed later with much less churn.
11
4
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: John Snow <jsnow@redhat.com>
15
---
6
---
16
include/block/blockjob.h | 9 +++----
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
17
include/block/blockjob_int.h | 4 +--
8
1 file changed, 59 insertions(+)
18
include/qemu/job.h | 60 ++++++++++++++++++++++++++++++++++++++++++++
19
block/backup.c | 4 ++-
20
block/commit.c | 4 ++-
21
block/mirror.c | 10 +++++---
22
block/stream.c | 4 ++-
23
blockjob.c | 46 ++++++++++++++++-----------------
24
job.c | 48 +++++++++++++++++++++++++++++++++++
25
tests/test-bdrv-drain.c | 4 ++-
26
tests/test-blockjob-txn.c | 4 ++-
27
tests/test-blockjob.c | 12 ++++++---
28
MAINTAINERS | 2 ++
29
Makefile.objs | 2 +-
30
14 files changed, 169 insertions(+), 44 deletions(-)
31
create mode 100644 include/qemu/job.h
32
create mode 100644 job.c
33
9
34
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/blockjob.h
37
+++ b/include/block/blockjob.h
38
@@ -XXX,XX +XXX,XX @@
39
#ifndef BLOCKJOB_H
40
#define BLOCKJOB_H
41
42
+#include "qemu/job.h"
43
#include "block/block.h"
44
#include "qemu/ratelimit.h"
45
46
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJobTxn BlockJobTxn;
47
* Long-running operation on a BlockDriverState.
48
*/
49
typedef struct BlockJob {
50
+ /** Data belonging to the generic Job infrastructure */
51
+ Job job;
52
+
53
/** The job type, including the job vtable. */
54
const BlockJobDriver *driver;
55
56
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
57
BlockBackend *blk;
58
59
/**
60
- * The ID of the block job. May be NULL for internal jobs.
61
- */
62
- char *id;
63
-
64
- /**
65
* The coroutine that executes the job. If not NULL, it is
66
* reentered when busy is false and the job is cancelled.
67
*/
68
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
69
index XXXXXXX..XXXXXXX 100644
70
--- a/include/block/blockjob_int.h
71
+++ b/include/block/blockjob_int.h
72
@@ -XXX,XX +XXX,XX @@
73
* A class type for block job driver.
74
*/
75
struct BlockJobDriver {
76
- /** Derived BlockJob struct size */
77
- size_t instance_size;
78
+ /** Generic JobDriver callbacks and settings */
79
+ JobDriver job_driver;
80
81
/** String describing the operation, part of query-block-jobs QMP API */
82
BlockJobType job_type;
83
diff --git a/include/qemu/job.h b/include/qemu/job.h
84
new file mode 100644
85
index XXXXXXX..XXXXXXX
86
--- /dev/null
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@
89
+/*
90
+ * Declarations for background jobs
91
+ *
92
+ * Copyright (c) 2011 IBM Corp.
93
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
94
+ *
95
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
96
+ * of this software and associated documentation files (the "Software"), to deal
97
+ * in the Software without restriction, including without limitation the rights
98
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
+ * copies of the Software, and to permit persons to whom the Software is
100
+ * furnished to do so, subject to the following conditions:
101
+ *
102
+ * The above copyright notice and this permission notice shall be included in
103
+ * all copies or substantial portions of the Software.
104
+ *
105
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
108
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
110
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
111
+ * THE SOFTWARE.
112
+ */
113
+
114
+#ifndef JOB_H
115
+#define JOB_H
116
+
117
+typedef struct JobDriver JobDriver;
118
+
119
+/**
120
+ * Long-running operation.
121
+ */
122
+typedef struct Job {
123
+ /** The ID of the job. May be NULL for internal jobs. */
124
+ char *id;
125
+
126
+ /** The type of this job. */
127
+ const JobDriver *driver;
128
+} Job;
129
+
130
+/**
131
+ * Callbacks and other information about a Job driver.
132
+ */
133
+struct JobDriver {
134
+ /** Derived Job struct size */
135
+ size_t instance_size;
136
+};
137
+
138
+
139
+/**
140
+ * Create a new long-running job and return it.
141
+ *
142
+ * @job_id: The id of the newly-created job, or %NULL for internal jobs
143
+ * @driver: The class object for the newly-created job.
144
+ * @errp: Error object.
145
+ */
146
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
147
+
148
+#endif
149
diff --git a/block/backup.c b/block/backup.c
150
index XXXXXXX..XXXXXXX 100644
151
--- a/block/backup.c
152
+++ b/block/backup.c
153
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
154
}
155
156
static const BlockJobDriver backup_job_driver = {
157
- .instance_size = sizeof(BackupBlockJob),
158
+ .job_driver = {
159
+ .instance_size = sizeof(BackupBlockJob),
160
+ },
161
.job_type = BLOCK_JOB_TYPE_BACKUP,
162
.start = backup_run,
163
.commit = backup_commit,
164
diff --git a/block/commit.c b/block/commit.c
165
index XXXXXXX..XXXXXXX 100644
166
--- a/block/commit.c
167
+++ b/block/commit.c
168
@@ -XXX,XX +XXX,XX @@ out:
169
}
170
171
static const BlockJobDriver commit_job_driver = {
172
- .instance_size = sizeof(CommitBlockJob),
173
+ .job_driver = {
174
+ .instance_size = sizeof(CommitBlockJob),
175
+ },
176
.job_type = BLOCK_JOB_TYPE_COMMIT,
177
.start = commit_run,
178
};
179
diff --git a/block/mirror.c b/block/mirror.c
180
index XXXXXXX..XXXXXXX 100644
181
--- a/block/mirror.c
182
+++ b/block/mirror.c
183
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
184
185
if (!s->synced) {
186
error_setg(errp, "The active block job '%s' cannot be completed",
187
- job->id);
188
+ job->job.id);
189
return;
190
}
191
192
@@ -XXX,XX +XXX,XX @@ static void mirror_drain(BlockJob *job)
193
}
194
195
static const BlockJobDriver mirror_job_driver = {
196
- .instance_size = sizeof(MirrorBlockJob),
197
+ .job_driver = {
198
+ .instance_size = sizeof(MirrorBlockJob),
199
+ },
200
.job_type = BLOCK_JOB_TYPE_MIRROR,
201
.start = mirror_run,
202
.complete = mirror_complete,
203
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
204
};
205
206
static const BlockJobDriver commit_active_job_driver = {
207
- .instance_size = sizeof(MirrorBlockJob),
208
+ .job_driver = {
209
+ .instance_size = sizeof(MirrorBlockJob),
210
+ },
211
.job_type = BLOCK_JOB_TYPE_COMMIT,
212
.start = mirror_run,
213
.complete = mirror_complete,
214
diff --git a/block/stream.c b/block/stream.c
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/stream.c
217
+++ b/block/stream.c
218
@@ -XXX,XX +XXX,XX @@ out:
219
}
220
221
static const BlockJobDriver stream_job_driver = {
222
- .instance_size = sizeof(StreamBlockJob),
223
+ .job_driver = {
224
+ .instance_size = sizeof(StreamBlockJob),
225
+ },
226
.job_type = BLOCK_JOB_TYPE_STREAM,
227
.start = stream_run,
228
};
229
diff --git a/blockjob.c b/blockjob.c
230
index XXXXXXX..XXXXXXX 100644
231
--- a/blockjob.c
232
+++ b/blockjob.c
233
@@ -XXX,XX +XXX,XX @@
234
#include "qapi/qapi-events-block-core.h"
235
#include "qapi/qmp/qerror.h"
236
#include "qemu/coroutine.h"
237
-#include "qemu/id.h"
238
#include "qemu/timer.h"
239
240
/* Right now, this mutex is only needed to synchronize accesses to job->busy
241
@@ -XXX,XX +XXX,XX @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
242
return 0;
243
}
244
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
245
- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv));
246
+ job->job.id, BlockJobStatus_str(job->status),
247
+ BlockJobVerb_str(bv));
248
return -EPERM;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
252
BlockJob *job;
253
254
QLIST_FOREACH(job, &block_jobs, job_list) {
255
- if (job->id && !strcmp(id, job->id)) {
256
+ if (job->job.id && !strcmp(id, job->job.id)) {
257
return job;
258
}
259
}
260
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
261
block_job_detach_aio_context, job);
262
blk_unref(job->blk);
263
error_free(job->blocker);
264
- g_free(job->id);
265
+ g_free(job->job.id);
266
assert(!timer_pending(&job->sleep_timer));
267
g_free(job);
268
}
269
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
270
BlockJob *job = c->opaque;
271
return g_strdup_printf("%s job '%s'",
272
BlockJobType_str(job->driver->job_type),
273
- job->id);
274
+ job->job.id);
275
}
276
277
static void child_job_drained_begin(BdrvChild *c)
278
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
279
280
bool block_job_is_internal(BlockJob *job)
281
{
282
- return (job->id == NULL);
283
+ return (job->job.id == NULL);
284
}
285
286
static bool block_job_started(BlockJob *job)
287
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
288
void block_job_complete(BlockJob *job, Error **errp)
289
{
290
/* Should not be reachable via external interface for internal jobs */
291
- assert(job->id);
292
+ assert(job->job.id);
293
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
294
return;
295
}
296
if (job->pause_count || job->cancelled || !job->driver->complete) {
297
error_setg(errp, "The active block job '%s' cannot be completed",
298
- job->id);
299
+ job->job.id);
300
return;
301
}
302
303
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
304
305
void block_job_finalize(BlockJob *job, Error **errp)
306
{
307
- assert(job && job->id);
308
+ assert(job && job->job.id);
309
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
310
return;
311
}
312
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
313
{
314
BlockJob *job = *jobptr;
315
/* similarly to _complete, this is QMP-interface only. */
316
- assert(job->id);
317
+ assert(job->job.id);
318
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
319
return;
320
}
321
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
322
}
323
info = g_new0(BlockJobInfo, 1);
324
info->type = g_strdup(BlockJobType_str(job->driver->job_type));
325
- info->device = g_strdup(job->id);
326
+ info->device = g_strdup(job->job.id);
327
info->len = job->len;
328
info->busy = atomic_read(&job->busy);
329
info->paused = job->pause_count > 0;
330
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
331
}
332
333
qapi_event_send_block_job_cancelled(job->driver->job_type,
334
- job->id,
335
+ job->job.id,
336
job->len,
337
job->offset,
338
job->speed,
339
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
340
}
341
342
qapi_event_send_block_job_completed(job->driver->job_type,
343
- job->id,
344
+ job->job.id,
345
job->len,
346
job->offset,
347
job->speed,
348
@@ -XXX,XX +XXX,XX @@ static int block_job_event_pending(BlockJob *job)
349
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
350
if (!job->auto_finalize && !block_job_is_internal(job)) {
351
qapi_event_send_block_job_pending(job->driver->job_type,
352
- job->id,
353
+ job->job.id,
354
&error_abort);
355
}
356
return 0;
357
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
358
error_setg(errp, "Cannot specify job ID for internal block job");
359
return NULL;
360
}
361
-
362
- if (!id_wellformed(job_id)) {
363
- error_setg(errp, "Invalid job ID '%s'", job_id);
364
- return NULL;
365
- }
366
-
367
if (block_job_get(job_id)) {
368
error_setg(errp, "Job ID '%s' already in use", job_id);
369
return NULL;
370
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
371
return NULL;
372
}
373
374
- job = g_malloc0(driver->instance_size);
375
+ job = job_create(job_id, &driver->job_driver, errp);
376
+ if (job == NULL) {
377
+ blk_unref(blk);
378
+ return NULL;
379
+ }
380
+
381
job->driver = driver;
382
- job->id = g_strdup(job_id);
383
job->blk = blk;
384
job->cb = cb;
385
job->opaque = opaque;
386
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job)
387
}
388
389
qapi_event_send_block_job_ready(job->driver->job_type,
390
- job->id,
391
+ job->job.id,
392
job->len,
393
job->offset,
394
job->speed, &error_abort);
395
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
396
abort();
397
}
398
if (!block_job_is_internal(job)) {
399
- qapi_event_send_block_job_error(job->id,
400
+ qapi_event_send_block_job_error(job->job.id,
401
is_read ? IO_OPERATION_TYPE_READ :
402
IO_OPERATION_TYPE_WRITE,
403
action, &error_abort);
404
diff --git a/job.c b/job.c
405
new file mode 100644
406
index XXXXXXX..XXXXXXX
407
--- /dev/null
408
+++ b/job.c
409
@@ -XXX,XX +XXX,XX @@
410
+/*
411
+ * Background jobs (long-running operations)
412
+ *
413
+ * Copyright (c) 2011 IBM Corp.
414
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
415
+ *
416
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
417
+ * of this software and associated documentation files (the "Software"), to deal
418
+ * in the Software without restriction, including without limitation the rights
419
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
420
+ * copies of the Software, and to permit persons to whom the Software is
421
+ * furnished to do so, subject to the following conditions:
422
+ *
423
+ * The above copyright notice and this permission notice shall be included in
424
+ * all copies or substantial portions of the Software.
425
+ *
426
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
427
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
428
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
429
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
430
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
431
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
432
+ * THE SOFTWARE.
433
+ */
434
+
435
+#include "qemu/osdep.h"
436
+#include "qemu-common.h"
437
+#include "qapi/error.h"
438
+#include "qemu/job.h"
439
+#include "qemu/id.h"
440
+
441
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
442
+{
443
+ Job *job;
444
+
445
+ if (job_id) {
446
+ if (!id_wellformed(job_id)) {
447
+ error_setg(errp, "Invalid job ID '%s'", job_id);
448
+ return NULL;
449
+ }
450
+ }
451
+
452
+ job = g_malloc0(driver->instance_size);
453
+ job->driver = driver;
454
+ job->id = g_strdup(job_id);
455
+
456
+ return job;
457
+}
458
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
459
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
460
--- a/tests/test-bdrv-drain.c
12
--- a/tests/test-bdrv-drain.c
461
+++ b/tests/test-bdrv-drain.c
13
+++ b/tests/test-bdrv-drain.c
462
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
15
*aio_ret = ret;
463
}
16
}
464
17
465
BlockJobDriver test_job_driver = {
18
+typedef struct CallInCoroutineData {
466
- .instance_size = sizeof(TestBlockJob),
19
+ void (*entry)(void);
467
+ .job_driver = {
20
+ bool done;
468
+ .instance_size = sizeof(TestBlockJob),
21
+} CallInCoroutineData;
469
+ },
22
+
470
.start = test_job_start,
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
471
.complete = test_job_complete,
24
+{
472
};
25
+ CallInCoroutineData *data = opaque;
473
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
26
+
474
index XXXXXXX..XXXXXXX 100644
27
+ data->entry();
475
--- a/tests/test-blockjob-txn.c
28
+ data->done = true;
476
+++ b/tests/test-blockjob-txn.c
29
+}
477
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
30
+
31
+static void call_in_coroutine(void (*entry)(void))
32
+{
33
+ Coroutine *co;
34
+ CallInCoroutineData data = {
35
+ .entry = entry,
36
+ .done = false,
37
+ };
38
+
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
40
+ qemu_coroutine_enter(co);
41
+ while (!data.done) {
42
+ aio_poll(qemu_get_aio_context(), true);
43
+ }
44
+}
45
+
46
enum drain_type {
47
BDRV_DRAIN_ALL,
48
BDRV_DRAIN,
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
478
}
51
}
479
52
480
static const BlockJobDriver test_block_job_driver = {
53
+static void test_drv_cb_co_drain(void)
481
- .instance_size = sizeof(TestBlockJob),
54
+{
482
+ .job_driver = {
55
+ call_in_coroutine(test_drv_cb_drain);
483
+ .instance_size = sizeof(TestBlockJob),
56
+}
484
+ },
57
+
485
.start = test_block_job_run,
58
+static void test_drv_cb_co_drain_subtree(void)
486
};
59
+{
487
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
61
+}
489
index XXXXXXX..XXXXXXX 100644
62
+
490
--- a/tests/test-blockjob.c
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
491
+++ b/tests/test-blockjob.c
64
{
492
@@ -XXX,XX +XXX,XX @@
65
BlockBackend *blk;
493
#include "sysemu/block-backend.h"
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
494
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
495
static const BlockJobDriver test_block_job_driver = {
496
- .instance_size = sizeof(BlockJob),
497
+ .job_driver = {
498
+ .instance_size = sizeof(BlockJob),
499
+ },
500
};
501
502
static void block_job_cb(void *opaque, int ret)
503
@@ -XXX,XX +XXX,XX @@ static BlockJob *mk_job(BlockBackend *blk, const char *id,
504
g_assert_null(errp);
505
g_assert_nonnull(job);
506
if (id) {
507
- g_assert_cmpstr(job->id, ==, id);
508
+ g_assert_cmpstr(job->job.id, ==, id);
509
} else {
510
- g_assert_cmpstr(job->id, ==, blk_name(blk));
511
+ g_assert_cmpstr(job->job.id, ==, blk_name(blk));
512
}
513
} else {
514
g_assert_nonnull(errp);
515
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
516
}
68
}
517
69
518
static const BlockJobDriver test_cancel_driver = {
70
+static void test_quiesce_co_drain(void)
519
- .instance_size = sizeof(CancelJob),
71
+{
520
+ .job_driver = {
72
+ call_in_coroutine(test_quiesce_drain);
521
+ .instance_size = sizeof(CancelJob),
73
+}
522
+ },
74
+
523
.start = cancel_job_start,
75
+static void test_quiesce_co_drain_subtree(void)
524
.complete = cancel_job_complete,
76
+{
525
};
77
+ call_in_coroutine(test_quiesce_drain_subtree);
526
diff --git a/MAINTAINERS b/MAINTAINERS
78
+}
527
index XXXXXXX..XXXXXXX 100644
79
+
528
--- a/MAINTAINERS
80
static void test_nested(void)
529
+++ b/MAINTAINERS
81
{
530
@@ -XXX,XX +XXX,XX @@ L: qemu-block@nongnu.org
82
BlockBackend *blk;
531
S: Supported
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
532
F: blockjob.c
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
533
F: include/block/blockjob.h
85
test_drv_cb_drain_subtree);
534
+F: job.c
86
535
+F: include/block/job.h
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
536
F: block/backup.c
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
537
F: block/commit.c
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
538
F: block/stream.c
90
+ test_drv_cb_co_drain_subtree);
539
diff --git a/Makefile.objs b/Makefile.objs
91
+
540
index XXXXXXX..XXXXXXX 100644
92
+
541
--- a/Makefile.objs
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
542
+++ b/Makefile.objs
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
543
@@ -XXX,XX +XXX,XX @@ chardev-obj-y = chardev/
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
544
# block-obj-y is code used by both qemu system emulation and qemu-img
96
test_quiesce_drain_subtree);
545
97
546
block-obj-y += nbd/
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
547
-block-obj-y += block.o blockjob.o
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
548
+block-obj-y += block.o blockjob.o job.o
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
549
block-obj-y += block/ scsi/
101
+ test_quiesce_co_drain_subtree);
550
block-obj-y += qemu-io-cmds.o
102
+
551
block-obj-$(CONFIG_REPLICATION) += replication.o
103
g_test_add_func("/bdrv-drain/nested", test_nested);
104
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
552
--
106
--
553
2.13.6
107
2.13.6
554
108
555
109
diff view generated by jsdifflib
Deleted patch
1
This moves freeing the Job object and its fields from block_job_unref()
2
to job_delete().
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
---
8
include/qemu/job.h | 3 +++
9
blockjob.c | 3 +--
10
job.c | 6 ++++++
11
3 files changed, 10 insertions(+), 2 deletions(-)
12
13
diff --git a/include/qemu/job.h b/include/qemu/job.h
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qemu/job.h
16
+++ b/include/qemu/job.h
17
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
18
*/
19
void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
20
21
+/** Frees the @job object. */
22
+void job_delete(Job *job);
23
+
24
/** Returns the JobType of a given Job. */
25
JobType job_type(const Job *job);
26
27
diff --git a/blockjob.c b/blockjob.c
28
index XXXXXXX..XXXXXXX 100644
29
--- a/blockjob.c
30
+++ b/blockjob.c
31
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
32
block_job_detach_aio_context, job);
33
blk_unref(job->blk);
34
error_free(job->blocker);
35
- g_free(job->job.id);
36
assert(!timer_pending(&job->sleep_timer));
37
- g_free(job);
38
+ job_delete(&job->job);
39
}
40
}
41
42
diff --git a/job.c b/job.c
43
index XXXXXXX..XXXXXXX 100644
44
--- a/job.c
45
+++ b/job.c
46
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
47
48
return job;
49
}
50
+
51
+void job_delete(Job *job)
52
+{
53
+ g_free(job->id);
54
+ g_free(job);
55
+}
56
--
57
2.13.6
58
59
diff view generated by jsdifflib
Deleted patch
1
This moves BlockJob.status and the closely related functions
2
(block_)job_state_transition() and (block_)job_apply_verb to Job. The
3
two QAPI enums are renamed to JobStatus and JobVerb.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
---
10
qapi/block-core.json | 16 ++++----
11
include/block/blockjob.h | 3 --
12
include/qemu/job.h | 13 ++++++
13
blockjob.c | 102 +++++++++++------------------------------------
14
job.c | 56 ++++++++++++++++++++++++++
15
tests/test-blockjob.c | 39 +++++++++---------
16
block/trace-events | 2 -
17
trace-events | 4 ++
18
8 files changed, 123 insertions(+), 112 deletions(-)
19
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
25
'data': ['commit', 'stream', 'mirror', 'backup'] }
26
27
##
28
-# @BlockJobVerb:
29
+# @JobVerb:
30
#
31
-# Represents command verbs that can be applied to a blockjob.
32
+# Represents command verbs that can be applied to a job.
33
#
34
# @cancel: see @block-job-cancel
35
#
36
@@ -XXX,XX +XXX,XX @@
37
#
38
# Since: 2.12
39
##
40
-{ 'enum': 'BlockJobVerb',
41
+{ 'enum': 'JobVerb',
42
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
43
'finalize' ] }
44
45
##
46
-# @BlockJobStatus:
47
+# @JobStatus:
48
#
49
-# Indicates the present state of a given blockjob in its lifetime.
50
+# Indicates the present state of a given job in its lifetime.
51
#
52
# @undefined: Erroneous, default state. Should not ever be visible.
53
#
54
@@ -XXX,XX +XXX,XX @@
55
#
56
# Since: 2.12
57
##
58
-{ 'enum': 'BlockJobStatus',
59
+{ 'enum': 'JobStatus',
60
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
61
'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
62
63
@@ -XXX,XX +XXX,XX @@
64
'data': {'type': 'str', 'device': 'str', 'len': 'int',
65
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
66
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
67
- 'status': 'BlockJobStatus',
68
+ 'status': 'JobStatus',
69
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
70
'*error': 'str' } }
71
72
@@ -XXX,XX +XXX,XX @@
73
# QEMU 2.12+ job lifetime management semantics.
74
#
75
# This command will refuse to operate on any job that has not yet reached
76
-# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
77
+# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the
78
# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
79
# to be used as appropriate.
80
#
81
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob.h
84
+++ b/include/block/blockjob.h
85
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
86
*/
87
QEMUTimer sleep_timer;
88
89
- /** Current state; See @BlockJobStatus for details. */
90
- BlockJobStatus status;
91
-
92
/** True if this job should automatically finalize itself */
93
bool auto_finalize;
94
95
diff --git a/include/qemu/job.h b/include/qemu/job.h
96
index XXXXXXX..XXXXXXX 100644
97
--- a/include/qemu/job.h
98
+++ b/include/qemu/job.h
99
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
100
/** The type of this job. */
101
const JobDriver *driver;
102
103
+ /** Current state; See @JobStatus for details. */
104
+ JobStatus status;
105
+
106
/** Element of the list of jobs */
107
QLIST_ENTRY(Job) job_list;
108
} Job;
109
@@ -XXX,XX +XXX,XX @@ Job *job_next(Job *job);
110
*/
111
Job *job_get(const char *id);
112
113
+/**
114
+ * Check whether the verb @verb can be applied to @job in its current state.
115
+ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM
116
+ * returned.
117
+ */
118
+int job_apply_verb(Job *job, JobVerb verb, Error **errp);
119
+
120
+/* TODO To be removed from the public interface */
121
+void job_state_transition(Job *job, JobStatus s1);
122
+
123
#endif
124
diff --git a/blockjob.c b/blockjob.c
125
index XXXXXXX..XXXXXXX 100644
126
--- a/blockjob.c
127
+++ b/blockjob.c
128
@@ -XXX,XX +XXX,XX @@
129
* block_job_enter. */
130
static QemuMutex block_job_mutex;
131
132
-/* BlockJob State Transition Table */
133
-bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
134
- /* U, C, R, P, Y, S, W, D, X, E, N */
135
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
136
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
137
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
138
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
139
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
140
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
141
- /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
142
- /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
143
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
144
- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
145
- /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
146
-};
147
-
148
-bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
149
- /* U, C, R, P, Y, S, W, D, X, E, N */
150
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
151
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
152
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
153
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
154
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
155
- [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
156
- [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
157
-};
158
-
159
-static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
160
-{
161
- BlockJobStatus s0 = job->status;
162
- assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
163
- trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
164
- "allowed" : "disallowed",
165
- BlockJobStatus_str(s0),
166
- BlockJobStatus_str(s1));
167
- assert(BlockJobSTT[s0][s1]);
168
- job->status = s1;
169
-}
170
-
171
-static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
172
-{
173
- assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
174
- trace_block_job_apply_verb(job, BlockJobStatus_str(job->status),
175
- BlockJobVerb_str(bv),
176
- BlockJobVerbTable[bv][job->status] ?
177
- "allowed" : "prohibited");
178
- if (BlockJobVerbTable[bv][job->status]) {
179
- return 0;
180
- }
181
- error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
182
- job->job.id, BlockJobStatus_str(job->status),
183
- BlockJobVerb_str(bv));
184
- return -EPERM;
185
-}
186
-
187
static void block_job_lock(void)
188
{
189
qemu_mutex_lock(&block_job_mutex);
190
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque);
191
void block_job_unref(BlockJob *job)
192
{
193
if (--job->refcnt == 0) {
194
- assert(job->status == BLOCK_JOB_STATUS_NULL);
195
+ assert(job->job.status == JOB_STATUS_NULL);
196
assert(!job->txn);
197
BlockDriverState *bs = blk_bs(job->blk);
198
bs->job = NULL;
199
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
200
job->pause_count--;
201
job->busy = true;
202
job->paused = false;
203
- block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
204
+ job_state_transition(&job->job, JOB_STATUS_RUNNING);
205
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
206
}
207
208
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
209
job->paused = false;
210
job->deferred_to_main_loop = true;
211
block_job_txn_del_job(job);
212
- block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
213
+ job_state_transition(&job->job, JOB_STATUS_NULL);
214
block_job_unref(job);
215
}
216
217
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
218
219
static void block_job_conclude(BlockJob *job)
220
{
221
- block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
222
+ job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
223
if (job->auto_dismiss || !block_job_started(job)) {
224
block_job_do_dismiss(job);
225
}
226
@@ -XXX,XX +XXX,XX @@ static void block_job_update_rc(BlockJob *job)
227
job->ret = -ECANCELED;
228
}
229
if (job->ret) {
230
- block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
231
+ job_state_transition(&job->job, JOB_STATUS_ABORTING);
232
}
233
}
234
235
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
236
BlockJobTxn *txn = job->txn;
237
BlockJob *other_job;
238
239
- block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
240
+ job_state_transition(&job->job, JOB_STATUS_WAITING);
241
242
/*
243
* Successful completion, see if there are other running jobs in this
244
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
245
{
246
int64_t old_speed = job->speed;
247
248
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
249
+ if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
250
return;
251
}
252
if (speed < 0) {
253
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
254
{
255
/* Should not be reachable via external interface for internal jobs */
256
assert(job->job.id);
257
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
258
+ if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
259
return;
260
}
261
if (job->pause_count || job->cancelled || !job->driver->complete) {
262
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
263
void block_job_finalize(BlockJob *job, Error **errp)
264
{
265
assert(job && job->job.id);
266
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
267
+ if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) {
268
return;
269
}
270
block_job_do_finalize(job);
271
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
272
BlockJob *job = *jobptr;
273
/* similarly to _complete, this is QMP-interface only. */
274
assert(job->job.id);
275
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
276
+ if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
277
return;
278
}
279
280
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
281
282
void block_job_user_pause(BlockJob *job, Error **errp)
283
{
284
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
285
+ if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) {
286
return;
287
}
288
if (job->user_paused) {
289
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
290
error_setg(errp, "Can't resume a job that was not paused");
291
return;
292
}
293
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
294
+ if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) {
295
return;
296
}
297
block_job_iostatus_reset(job);
298
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
299
300
void block_job_cancel(BlockJob *job, bool force)
301
{
302
- if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
303
+ if (job->job.status == JOB_STATUS_CONCLUDED) {
304
block_job_do_dismiss(job);
305
return;
306
}
307
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
308
309
void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
310
{
311
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
312
+ if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
313
return;
314
}
315
block_job_cancel(job, force);
316
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
317
info->speed = job->speed;
318
info->io_status = job->iostatus;
319
info->ready = job->ready;
320
- info->status = job->status;
321
+ info->status = job->job.status;
322
info->auto_finalize = job->auto_finalize;
323
info->auto_dismiss = job->auto_dismiss;
324
info->has_error = job->ret != 0;
325
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
326
327
static int block_job_event_pending(BlockJob *job)
328
{
329
- block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
330
+ job_state_transition(&job->job, JOB_STATUS_PENDING);
331
if (!job->auto_finalize && !block_job_is_internal(job)) {
332
qapi_event_send_block_job_pending(job_type(&job->job),
333
job->job.id,
334
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
335
job->refcnt = 1;
336
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
337
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
338
- block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
339
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
340
QEMU_CLOCK_REALTIME, SCALE_NS,
341
block_job_sleep_timer_cb, job);
342
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
343
344
void block_job_early_fail(BlockJob *job)
345
{
346
- assert(job->status == BLOCK_JOB_STATUS_CREATED);
347
+ assert(job->job.status == JOB_STATUS_CREATED);
348
block_job_decommission(job);
349
}
350
351
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
352
}
353
354
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
355
- BlockJobStatus status = job->status;
356
- block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
357
- BLOCK_JOB_STATUS_STANDBY : \
358
- BLOCK_JOB_STATUS_PAUSED);
359
+ JobStatus status = job->job.status;
360
+ job_state_transition(&job->job, status == JOB_STATUS_READY
361
+ ? JOB_STATUS_STANDBY
362
+ : JOB_STATUS_PAUSED);
363
job->paused = true;
364
block_job_do_yield(job, -1);
365
job->paused = false;
366
- block_job_state_transition(job, status);
367
+ job_state_transition(&job->job, status);
368
}
369
370
if (job->driver->resume) {
371
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
372
373
void block_job_event_ready(BlockJob *job)
374
{
375
- block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
376
+ job_state_transition(&job->job, JOB_STATUS_READY);
377
job->ready = true;
378
379
if (block_job_is_internal(job)) {
380
diff --git a/job.c b/job.c
381
index XXXXXXX..XXXXXXX 100644
382
--- a/job.c
383
+++ b/job.c
384
@@ -XXX,XX +XXX,XX @@
385
#include "qapi/error.h"
386
#include "qemu/job.h"
387
#include "qemu/id.h"
388
+#include "trace-root.h"
389
390
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
391
392
+/* Job State Transition Table */
393
+bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = {
394
+ /* U, C, R, P, Y, S, W, D, X, E, N */
395
+ /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
396
+ /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
397
+ /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
398
+ /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
399
+ /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
400
+ /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
401
+ /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
402
+ /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
403
+ /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
404
+ /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
405
+ /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
406
+};
407
+
408
+bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
409
+ /* U, C, R, P, Y, S, W, D, X, E, N */
410
+ [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
411
+ [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
412
+ [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
413
+ [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
414
+ [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
415
+ [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
416
+ [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
417
+};
418
+
419
+/* TODO Make static once the whole state machine is in job.c */
420
+void job_state_transition(Job *job, JobStatus s1)
421
+{
422
+ JobStatus s0 = job->status;
423
+ assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
424
+ trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
425
+ JobSTT[s0][s1] ? "allowed" : "disallowed",
426
+ JobStatus_str(s0), JobStatus_str(s1));
427
+ assert(JobSTT[s0][s1]);
428
+ job->status = s1;
429
+}
430
+
431
+int job_apply_verb(Job *job, JobVerb verb, Error **errp)
432
+{
433
+ JobStatus s0 = job->status;
434
+ assert(verb >= 0 && verb <= JOB_VERB__MAX);
435
+ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
436
+ JobVerbTable[verb][s0] ? "allowed" : "prohibited");
437
+ if (JobVerbTable[verb][s0]) {
438
+ return 0;
439
+ }
440
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
441
+ job->id, JobStatus_str(s0), JobVerb_str(verb));
442
+ return -EPERM;
443
+}
444
+
445
JobType job_type(const Job *job)
446
{
447
return job->driver->job_type;
448
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
449
job->driver = driver;
450
job->id = g_strdup(job_id);
451
452
+ job_state_transition(job, JOB_STATUS_CREATED);
453
+
454
QLIST_INSERT_HEAD(&jobs, job, job_list);
455
456
return job;
457
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
458
index XXXXXXX..XXXXXXX 100644
459
--- a/tests/test-blockjob.c
460
+++ b/tests/test-blockjob.c
461
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
462
job = mk_job(blk, "Steve", &test_cancel_driver, true,
463
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
464
block_job_ref(job);
465
- assert(job->status == BLOCK_JOB_STATUS_CREATED);
466
+ assert(job->job.status == JOB_STATUS_CREATED);
467
s = container_of(job, CancelJob, common);
468
s->blk = blk;
469
470
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
471
{
472
BlockJob *job = &s->common;
473
BlockBackend *blk = s->blk;
474
- BlockJobStatus sts = job->status;
475
+ JobStatus sts = job->job.status;
476
477
block_job_cancel_sync(job);
478
- if ((sts != BLOCK_JOB_STATUS_CREATED) &&
479
- (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
480
+ if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
481
BlockJob *dummy = job;
482
block_job_dismiss(&dummy, &error_abort);
483
}
484
- assert(job->status == BLOCK_JOB_STATUS_NULL);
485
+ assert(job->job.status == JOB_STATUS_NULL);
486
block_job_unref(job);
487
destroy_blk(blk);
488
}
489
@@ -XXX,XX +XXX,XX @@ static void test_cancel_running(void)
490
s = create_common(&job);
491
492
block_job_start(job);
493
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
494
+ assert(job->job.status == JOB_STATUS_RUNNING);
495
496
cancel_common(s);
497
}
498
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
499
s = create_common(&job);
500
501
block_job_start(job);
502
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
503
+ assert(job->job.status == JOB_STATUS_RUNNING);
504
505
block_job_user_pause(job, &error_abort);
506
block_job_enter(job);
507
- assert(job->status == BLOCK_JOB_STATUS_PAUSED);
508
+ assert(job->job.status == JOB_STATUS_PAUSED);
509
510
cancel_common(s);
511
}
512
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
513
s = create_common(&job);
514
515
block_job_start(job);
516
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
517
+ assert(job->job.status == JOB_STATUS_RUNNING);
518
519
s->should_converge = true;
520
block_job_enter(job);
521
- assert(job->status == BLOCK_JOB_STATUS_READY);
522
+ assert(job->job.status == JOB_STATUS_READY);
523
524
cancel_common(s);
525
}
526
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
527
s = create_common(&job);
528
529
block_job_start(job);
530
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
531
+ assert(job->job.status == JOB_STATUS_RUNNING);
532
533
s->should_converge = true;
534
block_job_enter(job);
535
- assert(job->status == BLOCK_JOB_STATUS_READY);
536
+ assert(job->job.status == JOB_STATUS_READY);
537
538
block_job_user_pause(job, &error_abort);
539
block_job_enter(job);
540
- assert(job->status == BLOCK_JOB_STATUS_STANDBY);
541
+ assert(job->job.status == JOB_STATUS_STANDBY);
542
543
cancel_common(s);
544
}
545
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
546
s = create_common(&job);
547
548
block_job_start(job);
549
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
550
+ assert(job->job.status == JOB_STATUS_RUNNING);
551
552
s->should_converge = true;
553
block_job_enter(job);
554
- assert(job->status == BLOCK_JOB_STATUS_READY);
555
+ assert(job->job.status == JOB_STATUS_READY);
556
557
block_job_complete(job, &error_abort);
558
block_job_enter(job);
559
while (!s->completed) {
560
aio_poll(qemu_get_aio_context(), true);
561
}
562
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
563
+ assert(job->job.status == JOB_STATUS_PENDING);
564
565
cancel_common(s);
566
}
567
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
568
s = create_common(&job);
569
570
block_job_start(job);
571
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
572
+ assert(job->job.status == JOB_STATUS_RUNNING);
573
574
s->should_converge = true;
575
block_job_enter(job);
576
- assert(job->status == BLOCK_JOB_STATUS_READY);
577
+ assert(job->job.status == JOB_STATUS_READY);
578
579
block_job_complete(job, &error_abort);
580
block_job_enter(job);
581
while (!s->completed) {
582
aio_poll(qemu_get_aio_context(), true);
583
}
584
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
585
+ assert(job->job.status == JOB_STATUS_PENDING);
586
587
block_job_finalize(job, &error_abort);
588
- assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
589
+ assert(job->job.status == JOB_STATUS_CONCLUDED);
590
591
cancel_common(s);
592
}
593
diff --git a/block/trace-events b/block/trace-events
594
index XXXXXXX..XXXXXXX 100644
595
--- a/block/trace-events
596
+++ b/block/trace-events
597
@@ -XXX,XX +XXX,XX @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
598
599
# blockjob.c
600
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
601
-block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
602
-block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
603
604
# block/block-backend.c
605
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
606
diff --git a/trace-events b/trace-events
607
index XXXXXXX..XXXXXXX 100644
608
--- a/trace-events
609
+++ b/trace-events
610
@@ -XXX,XX +XXX,XX @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
611
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
612
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
613
614
+# job.c
615
+job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
616
+job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
617
+
618
### Guest events, keep at bottom
619
620
621
--
622
2.13.6
623
624
diff view generated by jsdifflib
1
Move the defer_to_main_loop functionality from BlockJob to Job.
1
Test that drain sections are correctly propagated through the graph.
2
3
The code can be simplified because we can use job->aio_context in
4
job_defer_to_main_loop_bh() now, instead of having to access the
5
BlockDriverState.
6
7
Probably taking the data->aio_context lock in addition was already
8
unnecessary in the old code because we didn't actually make use of
9
anything protected by the old AioContext except getting the new
10
AioContext, in case it changed between scheduling the BH and running it.
11
But it's certainly unnecessary now that the BDS isn't accessed at all
12
any more.
13
2
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Reviewed-by: John Snow <jsnow@redhat.com>
17
---
4
---
18
include/block/blockjob.h | 5 ----
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
19
include/block/blockjob_int.h | 19 ---------------
6
1 file changed, 74 insertions(+)
20
include/qemu/job.h | 20 ++++++++++++++++
21
block/backup.c | 7 +++---
22
block/commit.c | 11 +++++----
23
block/mirror.c | 15 ++++++------
24
block/stream.c | 14 +++++------
25
blockjob.c | 57 ++++----------------------------------------
26
job.c | 32 +++++++++++++++++++++++++
27
tests/test-bdrv-drain.c | 7 +++---
28
tests/test-blockjob-txn.c | 13 +++++-----
29
tests/test-blockjob.c | 7 +++---
30
12 files changed, 97 insertions(+), 110 deletions(-)
31
7
32
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
33
index XXXXXXX..XXXXXXX 100644
34
--- a/include/block/blockjob.h
35
+++ b/include/block/blockjob.h
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
37
*/
38
bool ready;
39
40
- /**
41
- * Set to true when the job has deferred work to the main loop.
42
- */
43
- bool deferred_to_main_loop;
44
-
45
/** Status that is published by the query-block-jobs QMP API */
46
BlockDeviceIoStatus iostatus;
47
48
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
49
index XXXXXXX..XXXXXXX 100644
50
--- a/include/block/blockjob_int.h
51
+++ b/include/block/blockjob_int.h
52
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job);
53
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
54
int is_read, int error);
55
56
-typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
57
-
58
-/**
59
- * block_job_defer_to_main_loop:
60
- * @job: The job
61
- * @fn: The function to run in the main loop
62
- * @opaque: The opaque value that is passed to @fn
63
- *
64
- * This function must be called by the main job coroutine just before it
65
- * returns. @fn is executed in the main loop with the BlockDriverState
66
- * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
67
- * anything that uses bdrv_drain_all() in the main loop.
68
- *
69
- * The @job AioContext is held while @fn executes.
70
- */
71
-void block_job_defer_to_main_loop(BlockJob *job,
72
- BlockJobDeferToMainLoopFn *fn,
73
- void *opaque);
74
-
75
#endif
76
diff --git a/include/qemu/job.h b/include/qemu/job.h
77
index XXXXXXX..XXXXXXX 100644
78
--- a/include/qemu/job.h
79
+++ b/include/qemu/job.h
80
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
81
*/
82
bool cancelled;
83
84
+ /** Set to true when the job has deferred work to the main loop. */
85
+ bool deferred_to_main_loop;
86
+
87
/** Element of the list of jobs */
88
QLIST_ENTRY(Job) job_list;
89
} Job;
90
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id);
91
*/
92
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
93
94
+typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
95
+
96
+/**
97
+ * @job: The job
98
+ * @fn: The function to run in the main loop
99
+ * @opaque: The opaque value that is passed to @fn
100
+ *
101
+ * This function must be called by the main job coroutine just before it
102
+ * returns. @fn is executed in the main loop with the job AioContext acquired.
103
+ *
104
+ * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses
105
+ * bdrv_drain_all() in the main loop.
106
+ *
107
+ * The @job AioContext is held while @fn executes.
108
+ */
109
+void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
110
+
111
/* TODO To be removed from the public interface */
112
void job_state_transition(Job *job, JobStatus s1);
113
114
diff --git a/block/backup.c b/block/backup.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/block/backup.c
117
+++ b/block/backup.c
118
@@ -XXX,XX +XXX,XX @@ typedef struct {
119
int ret;
120
} BackupCompleteData;
121
122
-static void backup_complete(BlockJob *job, void *opaque)
123
+static void backup_complete(Job *job, void *opaque)
124
{
125
+ BlockJob *bjob = container_of(job, BlockJob, job);
126
BackupCompleteData *data = opaque;
127
128
- block_job_completed(job, data->ret);
129
+ block_job_completed(bjob, data->ret);
130
g_free(data);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
134
135
data = g_malloc(sizeof(*data));
136
data->ret = ret;
137
- block_job_defer_to_main_loop(&job->common, backup_complete, data);
138
+ job_defer_to_main_loop(&job->common.job, backup_complete, data);
139
}
140
141
static const BlockJobDriver backup_job_driver = {
142
diff --git a/block/commit.c b/block/commit.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/commit.c
145
+++ b/block/commit.c
146
@@ -XXX,XX +XXX,XX @@ typedef struct {
147
int ret;
148
} CommitCompleteData;
149
150
-static void commit_complete(BlockJob *job, void *opaque)
151
+static void commit_complete(Job *job, void *opaque)
152
{
153
- CommitBlockJob *s = container_of(job, CommitBlockJob, common);
154
+ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
155
+ BlockJob *bjob = &s->common;
156
CommitCompleteData *data = opaque;
157
BlockDriverState *top = blk_bs(s->top);
158
BlockDriverState *base = blk_bs(s->base);
159
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
160
* the normal backing chain can be restored. */
161
blk_unref(s->base);
162
163
- if (!job_is_cancelled(&s->common.job) && ret == 0) {
164
+ if (!job_is_cancelled(job) && ret == 0) {
165
/* success */
166
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
167
s->backing_file_str);
168
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
169
* block_job_finish_sync()), block_job_completed() won't free it and
170
* therefore the blockers on the intermediate nodes remain. This would
171
* cause bdrv_set_backing_hd() to fail. */
172
- block_job_remove_all_bdrv(job);
173
+ block_job_remove_all_bdrv(bjob);
174
175
block_job_completed(&s->common, ret);
176
g_free(data);
177
@@ -XXX,XX +XXX,XX @@ out:
178
179
data = g_malloc(sizeof(*data));
180
data->ret = ret;
181
- block_job_defer_to_main_loop(&s->common, commit_complete, data);
182
+ job_defer_to_main_loop(&s->common.job, commit_complete, data);
183
}
184
185
static const BlockJobDriver commit_job_driver = {
186
diff --git a/block/mirror.c b/block/mirror.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/block/mirror.c
189
+++ b/block/mirror.c
190
@@ -XXX,XX +XXX,XX @@ typedef struct {
191
int ret;
192
} MirrorExitData;
193
194
-static void mirror_exit(BlockJob *job, void *opaque)
195
+static void mirror_exit(Job *job, void *opaque)
196
{
197
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
198
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
199
+ BlockJob *bjob = &s->common;
200
MirrorExitData *data = opaque;
201
AioContext *replace_aio_context = NULL;
202
BlockDriverState *src = s->source;
203
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
204
* the blockers on the intermediate nodes so that the resulting state is
205
* valid. Also give up permissions on mirror_top_bs->backing, which might
206
* block the removal. */
207
- block_job_remove_all_bdrv(job);
208
+ block_job_remove_all_bdrv(bjob);
209
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
210
&error_abort);
211
bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
212
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
213
/* We just changed the BDS the job BB refers to (with either or both of the
214
* bdrv_replace_node() calls), so switch the BB back so the cleanup does
215
* the right thing. We don't need any permissions any more now. */
216
- blk_remove_bs(job->blk);
217
- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
218
- blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
219
+ blk_remove_bs(bjob->blk);
220
+ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
221
+ blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
222
223
block_job_completed(&s->common, data->ret);
224
225
@@ -XXX,XX +XXX,XX @@ immediate_exit:
226
if (need_drain) {
227
bdrv_drained_begin(bs);
228
}
229
- block_job_defer_to_main_loop(&s->common, mirror_exit, data);
230
+ job_defer_to_main_loop(&s->common.job, mirror_exit, data);
231
}
232
233
static void mirror_complete(BlockJob *job, Error **errp)
234
diff --git a/block/stream.c b/block/stream.c
235
index XXXXXXX..XXXXXXX 100644
236
--- a/block/stream.c
237
+++ b/block/stream.c
238
@@ -XXX,XX +XXX,XX @@ typedef struct {
239
int ret;
240
} StreamCompleteData;
241
242
-static void stream_complete(BlockJob *job, void *opaque)
243
+static void stream_complete(Job *job, void *opaque)
244
{
245
- StreamBlockJob *s = container_of(job, StreamBlockJob, common);
246
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
247
+ BlockJob *bjob = &s->common;
248
StreamCompleteData *data = opaque;
249
- BlockDriverState *bs = blk_bs(job->blk);
250
+ BlockDriverState *bs = blk_bs(bjob->blk);
251
BlockDriverState *base = s->base;
252
Error *local_err = NULL;
253
254
- if (!job_is_cancelled(&s->common.job) && bs->backing &&
255
- data->ret == 0) {
256
+ if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
257
const char *base_id = NULL, *base_fmt = NULL;
258
if (base) {
259
base_id = s->backing_file_str;
260
@@ -XXX,XX +XXX,XX @@ out:
261
/* Reopen the image back in read-only mode if necessary */
262
if (s->bs_flags != bdrv_get_flags(bs)) {
263
/* Give up write permissions before making it read-only */
264
- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
265
+ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
266
bdrv_reopen(bs, s->bs_flags, NULL);
267
}
268
269
@@ -XXX,XX +XXX,XX @@ out:
270
/* Modify backing chain and close BDSes in main loop */
271
data = g_malloc(sizeof(*data));
272
data->ret = ret;
273
- block_job_defer_to_main_loop(&s->common, stream_complete, data);
274
+ job_defer_to_main_loop(&s->common.job, stream_complete, data);
275
}
276
277
static const BlockJobDriver stream_job_driver = {
278
diff --git a/blockjob.c b/blockjob.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/blockjob.c
281
+++ b/blockjob.c
282
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
283
job->completed = true;
284
job->busy = false;
285
job->paused = false;
286
- job->deferred_to_main_loop = true;
287
+ job->job.deferred_to_main_loop = true;
288
block_job_txn_del_job(job);
289
job_state_transition(&job->job, JOB_STATUS_NULL);
290
job_unref(&job->job);
291
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
292
/* block_job_drain calls block_job_enter, and it should be enough to
293
* induce progress until the job completes or moves to the main thread.
294
*/
295
- while (!job->deferred_to_main_loop && !job->completed) {
296
+ while (!job->job.deferred_to_main_loop && !job->completed) {
297
block_job_drain(job);
298
}
299
while (!job->completed) {
300
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
301
block_job_cancel_async(job, force);
302
if (!block_job_started(job)) {
303
block_job_completed(job, -ECANCELED);
304
- } else if (job->deferred_to_main_loop) {
305
+ } else if (job->job.deferred_to_main_loop) {
306
block_job_completed_txn_abort(job);
307
} else {
308
block_job_enter(job);
309
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
310
if (!block_job_started(job)) {
311
return;
312
}
313
- if (job->deferred_to_main_loop) {
314
+ if (job->job.deferred_to_main_loop) {
315
return;
316
}
317
318
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
319
return;
320
}
321
322
- assert(!job->deferred_to_main_loop);
323
+ assert(!job->job.deferred_to_main_loop);
324
timer_del(&job->sleep_timer);
325
job->busy = true;
326
block_job_unlock();
327
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
328
}
329
return action;
330
}
331
-
332
-typedef struct {
333
- BlockJob *job;
334
- AioContext *aio_context;
335
- BlockJobDeferToMainLoopFn *fn;
336
- void *opaque;
337
-} BlockJobDeferToMainLoopData;
338
-
339
-static void block_job_defer_to_main_loop_bh(void *opaque)
340
-{
341
- BlockJobDeferToMainLoopData *data = opaque;
342
- AioContext *aio_context;
343
-
344
- /* Prevent race with block_job_defer_to_main_loop() */
345
- aio_context_acquire(data->aio_context);
346
-
347
- /* Fetch BDS AioContext again, in case it has changed */
348
- aio_context = blk_get_aio_context(data->job->blk);
349
- if (aio_context != data->aio_context) {
350
- aio_context_acquire(aio_context);
351
- }
352
-
353
- data->fn(data->job, data->opaque);
354
-
355
- if (aio_context != data->aio_context) {
356
- aio_context_release(aio_context);
357
- }
358
-
359
- aio_context_release(data->aio_context);
360
-
361
- g_free(data);
362
-}
363
-
364
-void block_job_defer_to_main_loop(BlockJob *job,
365
- BlockJobDeferToMainLoopFn *fn,
366
- void *opaque)
367
-{
368
- BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data));
369
- data->job = job;
370
- data->aio_context = blk_get_aio_context(job->blk);
371
- data->fn = fn;
372
- data->opaque = opaque;
373
- job->deferred_to_main_loop = true;
374
-
375
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
376
- block_job_defer_to_main_loop_bh, data);
377
-}
378
diff --git a/job.c b/job.c
379
index XXXXXXX..XXXXXXX 100644
380
--- a/job.c
381
+++ b/job.c
382
@@ -XXX,XX +XXX,XX @@
383
#include "qapi/error.h"
384
#include "qemu/job.h"
385
#include "qemu/id.h"
386
+#include "qemu/main-loop.h"
387
#include "trace-root.h"
388
389
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
390
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
391
g_free(job);
392
}
393
}
394
+
395
+typedef struct {
396
+ Job *job;
397
+ JobDeferToMainLoopFn *fn;
398
+ void *opaque;
399
+} JobDeferToMainLoopData;
400
+
401
+static void job_defer_to_main_loop_bh(void *opaque)
402
+{
403
+ JobDeferToMainLoopData *data = opaque;
404
+ Job *job = data->job;
405
+ AioContext *aio_context = job->aio_context;
406
+
407
+ aio_context_acquire(aio_context);
408
+ data->fn(data->job, data->opaque);
409
+ aio_context_release(aio_context);
410
+
411
+ g_free(data);
412
+}
413
+
414
+void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque)
415
+{
416
+ JobDeferToMainLoopData *data = g_malloc(sizeof(*data));
417
+ data->job = job;
418
+ data->fn = fn;
419
+ data->opaque = opaque;
420
+ job->deferred_to_main_loop = true;
421
+
422
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
423
+ job_defer_to_main_loop_bh, data);
424
+}
425
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
426
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
427
--- a/tests/test-bdrv-drain.c
10
--- a/tests/test-bdrv-drain.c
428
+++ b/tests/test-bdrv-drain.c
11
+++ b/tests/test-bdrv-drain.c
429
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
430
bool should_complete;
13
blk_unref(blk);
431
} TestBlockJob;
432
433
-static void test_job_completed(BlockJob *job, void *opaque)
434
+static void test_job_completed(Job *job, void *opaque)
435
{
436
- block_job_completed(job, 0);
437
+ BlockJob *bjob = container_of(job, BlockJob, job);
438
+ block_job_completed(bjob, 0);
439
}
14
}
440
15
441
static void coroutine_fn test_job_start(void *opaque)
16
+static void test_multiparent(void)
442
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
17
+{
443
block_job_sleep_ns(&s->common, 100000);
18
+ BlockBackend *blk_a, *blk_b;
444
}
19
+ BlockDriverState *bs_a, *bs_b, *backing;
445
20
+ BDRVTestState *a_s, *b_s, *backing_s;
446
- block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
21
+
447
+ job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
448
}
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
449
24
+ &error_abort);
450
static void test_job_complete(BlockJob *job, Error **errp)
25
+ a_s = bs_a->opaque;
451
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
452
index XXXXXXX..XXXXXXX 100644
27
+
453
--- a/tests/test-blockjob-txn.c
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
454
+++ b/tests/test-blockjob-txn.c
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
455
@@ -XXX,XX +XXX,XX @@ typedef struct {
30
+ &error_abort);
456
int *result;
31
+ b_s = bs_b->opaque;
457
} TestBlockJob;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
458
33
+
459
-static void test_block_job_complete(BlockJob *job, void *opaque)
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
460
+static void test_block_job_complete(Job *job, void *opaque)
35
+ backing_s = backing->opaque;
461
{
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
462
- BlockDriverState *bs = blk_bs(job->blk);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
463
+ BlockJob *bjob = container_of(job, BlockJob, job);
38
+
464
+ BlockDriverState *bs = blk_bs(bjob->blk);
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
465
int rc = (intptr_t)opaque;
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
466
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
467
- if (job_is_cancelled(&job->job)) {
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
468
+ if (job_is_cancelled(job)) {
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
469
rc = -ECANCELED;
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
470
}
45
+
471
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
472
- block_job_completed(job, rc);
47
+
473
+ block_job_completed(bjob, rc);
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
474
bdrv_unref(bs);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
475
}
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
476
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
477
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
478
}
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
479
}
54
+
480
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
481
- block_job_defer_to_main_loop(job, test_block_job_complete,
56
+
482
- (void *)(intptr_t)s->rc);
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
483
+ job_defer_to_main_loop(&job->job, test_block_job_complete,
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
484
+ (void *)(intptr_t)s->rc);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
485
}
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
486
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
487
typedef struct {
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
63
+
489
index XXXXXXX..XXXXXXX 100644
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
490
--- a/tests/test-blockjob.c
65
+
491
+++ b/tests/test-blockjob.c
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
492
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
493
bool completed;
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
494
} CancelJob;
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
495
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
496
-static void cancel_job_completed(BlockJob *job, void *opaque)
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
497
+static void cancel_job_completed(Job *job, void *opaque)
72
+
498
{
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
499
+ BlockJob *bjob = container_of(job, BlockJob, job);
74
+
500
CancelJob *s = opaque;
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
501
s->completed = true;
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
502
- block_job_completed(job, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
503
+ block_job_completed(bjob, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
504
}
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
505
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
506
static void cancel_job_complete(BlockJob *job, Error **errp)
81
+
507
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
82
+ bdrv_unref(backing);
508
}
83
+ bdrv_unref(bs_a);
509
84
+ bdrv_unref(bs_b);
510
defer:
85
+ blk_unref(blk_a);
511
- block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
86
+ blk_unref(blk_b);
512
+ job_defer_to_main_loop(&s->common.job, cancel_job_completed, s);
87
+}
513
}
88
+
514
89
515
static const BlockJobDriver test_cancel_driver = {
90
typedef struct TestBlockJob {
91
BlockJob common;
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
test_quiesce_co_drain_subtree);
94
95
g_test_add_func("/bdrv-drain/nested", test_nested);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
97
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
516
--
100
--
517
2.13.6
101
2.13.6
518
102
519
103
diff view generated by jsdifflib
1
This moves the top-level job completion and cancellation functions from
1
We need to remember how many of the drain sections in which a node is
2
BlockJob to Job.
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
can be correctly applied when children are added or removed during the
4
drained section.
5
6
With this change, it is safe to modify the graph even inside a
7
bdrv_subtree_drained_begin/end() section.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
include/block/blockjob.h | 55 -----------------------------
11
include/block/block.h | 2 --
7
include/block/blockjob_int.h | 18 ----------
12
include/block/block_int.h | 5 +++++
8
include/qemu/job.h | 68 +++++++++++++++++++++++++++++------
13
block.c | 32 +++++++++++++++++++++++++++++---
9
block.c | 4 ++-
14
block/io.c | 28 ++++++++++++++++++++++++----
10
block/backup.c | 3 +-
15
4 files changed, 58 insertions(+), 9 deletions(-)
11
block/commit.c | 6 ++--
16
12
block/mirror.c | 6 ++--
17
diff --git a/include/block/block.h b/include/block/block.h
13
block/replication.c | 4 +--
18
index XXXXXXX..XXXXXXX 100644
14
block/stream.c | 2 +-
19
--- a/include/block/block.h
15
blockdev.c | 8 ++---
20
+++ b/include/block/block.h
16
blockjob.c | 76 ---------------------------------------
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
17
job.c | 84 +++++++++++++++++++++++++++++++++++++++-----
18
qemu-img.c | 2 +-
19
tests/test-bdrv-drain.c | 5 ++-
20
tests/test-blockjob-txn.c | 14 ++++----
21
tests/test-blockjob.c | 21 ++++++-----
22
block/trace-events | 3 --
23
trace-events | 1 +
24
18 files changed, 171 insertions(+), 209 deletions(-)
25
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
27
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
29
+++ b/include/block/blockjob.h
30
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
31
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
32
33
/**
22
/**
34
- * block_job_cancel:
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
35
- * @job: The job to be canceled.
24
* exclusive access to all child nodes as well.
36
- * @force: Quit a job without waiting for data to be in sync.
37
- *
25
- *
38
- * Asynchronously cancel the specified job.
26
- * Graph changes are not allowed during a subtree drain section.
39
- */
40
-void block_job_cancel(BlockJob *job, bool force);
41
-
42
-/**
43
* block_job_dismiss:
44
* @job: The job to be dismissed.
45
* @errp: Error object.
46
@@ -XXX,XX +XXX,XX @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
47
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
48
49
/**
50
- * block_job_user_cancel:
51
- * @job: The job to be cancelled.
52
- * @force: Quit a job without waiting for data to be in sync.
53
- *
54
- * Cancels the specified job, but may refuse to do so if the
55
- * operation isn't currently meaningful.
56
- */
57
-void block_job_user_cancel(BlockJob *job, bool force, Error **errp);
58
-
59
-/**
60
- * block_job_cancel_sync:
61
- * @job: The job to be canceled.
62
- *
63
- * Synchronously cancel the job. The completion callback is called
64
- * before the function returns. The job may actually complete
65
- * instead of canceling itself; the circumstances under which this
66
- * happens depend on the kind of job that is active.
67
- *
68
- * Returns the return value from the job if the job actually completed
69
- * during the call, or -ECANCELED if it was canceled.
70
- */
71
-int block_job_cancel_sync(BlockJob *job);
72
-
73
-/**
74
- * block_job_cancel_sync_all:
75
- *
76
- * Synchronously cancels all jobs using block_job_cancel_sync().
77
- */
78
-void block_job_cancel_sync_all(void);
79
-
80
-/**
81
- * block_job_complete_sync:
82
- * @job: The job to be completed.
83
- * @errp: Error object which may be set by block_job_complete(); this is not
84
- * necessarily set on every error, the job return value has to be
85
- * checked as well.
86
- *
87
- * Synchronously complete the job. The completion callback is called before the
88
- * function returns, unless it is NULL (which is permissible when using this
89
- * function).
90
- *
91
- * Returns the return value from the job.
92
- */
93
-int block_job_complete_sync(BlockJob *job, Error **errp);
94
-
95
-/**
96
* block_job_iostatus_reset:
97
* @job: The job whose I/O status should be reset.
98
*
99
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
100
index XXXXXXX..XXXXXXX 100644
101
--- a/include/block/blockjob_int.h
102
+++ b/include/block/blockjob_int.h
103
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job);
104
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
105
106
/**
107
- * block_job_completed:
108
- * @job: The job being completed.
109
- * @ret: The status code.
110
- *
111
- * Call the completion function that was registered at creation time, and
112
- * free @job.
113
- */
114
-void block_job_completed(BlockJob *job, int ret);
115
-
116
-/**
117
- * block_job_enter:
118
- * @job: The job to enter.
119
- *
120
- * Continue the specified job by entering the coroutine.
121
- */
122
-void block_job_enter(BlockJob *job);
123
-
124
-/**
125
* block_job_event_ready:
126
* @job: The job which is now ready to be completed.
127
*
128
diff --git a/include/qemu/job.h b/include/qemu/job.h
129
index XXXXXXX..XXXXXXX 100644
130
--- a/include/qemu/job.h
131
+++ b/include/qemu/job.h
132
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
133
134
/**
135
* Set to false by the job while the coroutine has yielded and may be
136
- * re-entered by block_job_enter(). There may still be I/O or event loop
137
- * activity pending. Accessed under block_job_mutex (in blockjob.c).
138
+ * re-entered by job_enter(). There may still be I/O or event loop activity
139
+ * pending. Accessed under block_job_mutex (in blockjob.c).
140
*/
141
bool busy;
142
143
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
144
/** True if this job should automatically dismiss itself */
145
bool auto_dismiss;
146
147
- /** ret code passed to block_job_completed. */
148
+ /** ret code passed to job_completed. */
149
int ret;
150
151
/** The completion function that will be called when the job completes. */
152
@@ -XXX,XX +XXX,XX @@ void job_txn_unref(JobTxn *txn);
153
* @job: Job to add to the transaction
154
*
155
* Add @job to the transaction. The @job must not already be in a transaction.
156
- * The caller must call either job_txn_unref() or block_job_completed() to
157
- * release the reference that is automatically grabbed here.
158
+ * The caller must call either job_txn_unref() or job_completed() to release
159
+ * the reference that is automatically grabbed here.
160
*
161
* If @txn is NULL, the function does nothing.
162
*/
27
*/
163
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
164
/** The @job could not be started, free it. */
29
165
void job_early_fail(Job *job);
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
166
31
index XXXXXXX..XXXXXXX 100644
167
+/**
32
--- a/include/block/block_int.h
168
+ * @job: The job being completed.
33
+++ b/include/block/block_int.h
169
+ * @ret: The status code.
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
170
+ *
35
171
+ * Marks @job as completed. If @ret is non-zero, the job transaction it is part
36
/* Accessed with atomic ops. */
172
+ * of is aborted. If @ret is zero, the job moves into the WAITING state. If it
37
int quiesce_counter;
173
+ * is the last job to complete in its transaction, all jobs in the transaction
38
+ int recursive_quiesce_counter;
174
+ * move from WAITING to PENDING.
39
+
175
+ */
40
unsigned int write_gen; /* Current data generation */
176
+void job_completed(Job *job, int ret);
41
177
+
42
/* Protected by reqs_lock. */
178
/** Asynchronously complete the specified @job. */
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
179
-void job_complete(Job *job, Error **errp);;
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
180
+void job_complete(Job *job, Error **errp);
45
BdrvRequestFlags flags);
181
+
46
182
+/**
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
183
+ * Asynchronously cancel the specified @job. If @force is true, the job should
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
184
+ * be cancelled immediately without waiting for a consistent state.
49
+
185
+ */
50
int get_tmp_filename(char *filename, int size);
186
+void job_cancel(Job *job, bool force);
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
187
+
52
const char *filename);
188
+/**
189
+ * Cancels the specified job like job_cancel(), but may refuse to do so if the
190
+ * operation isn't meaningful in the current state of the job.
191
+ */
192
+void job_user_cancel(Job *job, bool force, Error **errp);
193
+
194
+/**
195
+ * Synchronously cancel the @job. The completion callback is called
196
+ * before the function returns. The job may actually complete
197
+ * instead of canceling itself; the circumstances under which this
198
+ * happens depend on the kind of job that is active.
199
+ *
200
+ * Returns the return value from the job if the job actually completed
201
+ * during the call, or -ECANCELED if it was canceled.
202
+ */
203
+int job_cancel_sync(Job *job);
204
+
205
+/** Synchronously cancels all jobs using job_cancel_sync(). */
206
+void job_cancel_sync_all(void);
207
+
208
+/**
209
+ * @job: The job to be completed.
210
+ * @errp: Error object which may be set by job_complete(); this is not
211
+ * necessarily set on every error, the job return value has to be
212
+ * checked as well.
213
+ *
214
+ * Synchronously complete the job. The completion callback is called before the
215
+ * function returns, unless it is NULL (which is permissible when using this
216
+ * function).
217
+ *
218
+ * Returns the return value from the job.
219
+ */
220
+int job_complete_sync(Job *job, Error **errp);
221
222
/**
223
* For a @job that has finished its work and is pending awaiting explicit
224
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
225
void job_state_transition(Job *job, JobStatus s1);
226
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
227
bool job_should_pause(Job *job);
228
-bool job_started(Job *job);
229
void job_do_dismiss(Job *job);
230
-void job_update_rc(Job *job);
231
-void job_cancel_async(Job *job, bool force);
232
-void job_completed_txn_abort(Job *job);
233
-void job_completed_txn_success(Job *job);
234
235
#endif
236
diff --git a/block.c b/block.c
53
diff --git a/block.c b/block.c
237
index XXXXXXX..XXXXXXX 100644
54
index XXXXXXX..XXXXXXX 100644
238
--- a/block.c
55
--- a/block.c
239
+++ b/block.c
56
+++ b/block.c
240
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
241
58
bdrv_drained_end(bs);
242
void bdrv_close_all(void)
59
}
60
61
+static void bdrv_child_cb_attach(BdrvChild *child)
62
+{
63
+ BlockDriverState *bs = child->opaque;
64
+ bdrv_apply_subtree_drain(child, bs);
65
+}
66
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
68
+{
69
+ BlockDriverState *bs = child->opaque;
70
+ bdrv_unapply_subtree_drain(child, bs);
71
+}
72
+
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
243
{
74
{
244
- block_job_cancel_sync_all();
75
BlockDriverState *bs = child->opaque;
245
+ /* TODO We do want to cancel all jobs instead of just block jobs on
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
246
+ * shutdown, but bdrv_close_all() isn't the right place any more. */
77
.inherit_options = bdrv_inherited_options,
247
+ job_cancel_sync_all();
78
.drained_begin = bdrv_child_cb_drained_begin,
248
nbd_export_close_all();
79
.drained_end = bdrv_child_cb_drained_end,
249
80
+ .attach = bdrv_child_cb_attach,
250
/* Drop references from requests still in flight, such as canceled block
81
+ .detach = bdrv_child_cb_detach,
251
diff --git a/block/backup.c b/block/backup.c
82
.inactivate = bdrv_child_cb_inactivate,
252
index XXXXXXX..XXXXXXX 100644
83
};
253
--- a/block/backup.c
84
254
+++ b/block/backup.c
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
255
@@ -XXX,XX +XXX,XX @@ typedef struct {
86
.inherit_options = bdrv_inherited_fmt_options,
256
87
.drained_begin = bdrv_child_cb_drained_begin,
257
static void backup_complete(Job *job, void *opaque)
88
.drained_end = bdrv_child_cb_drained_end,
89
+ .attach = bdrv_child_cb_attach,
90
+ .detach = bdrv_child_cb_detach,
91
.inactivate = bdrv_child_cb_inactivate,
92
};
93
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
95
parent->backing_blocker);
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
97
parent->backing_blocker);
98
+
99
+ bdrv_child_cb_attach(c);
100
}
101
102
static void bdrv_backing_detach(BdrvChild *c)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
105
error_free(parent->backing_blocker);
106
parent->backing_blocker = NULL;
107
+
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
125
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
}
135
}
136
137
+ /* Attach only after starting new drained sections, so that recursive
138
+ * drain sections coming from @child don't get an extra .drained_begin
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
258
{
155
{
259
- BlockJob *bjob = container_of(job, BlockJob, job);
156
BdrvChild *child, *next;
260
BackupCompleteData *data = opaque;
157
261
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
262
- block_job_completed(bjob, data->ret);
159
bdrv_drain_recurse(bs);
263
+ job_completed(job, data->ret);
160
264
g_free(data);
161
if (recursive) {
265
}
162
+ bs->recursive_quiesce_counter++;
266
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
267
diff --git a/block/commit.c b/block/commit.c
164
bdrv_do_drained_begin(child->bs, true, child);
268
index XXXXXXX..XXXXXXX 100644
165
}
269
--- a/block/commit.c
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
270
+++ b/block/commit.c
167
bdrv_do_drained_begin(bs, true, NULL);
271
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
168
}
272
blk_unref(s->top);
169
273
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
274
/* If there is more than one reference to the job (e.g. if called from
171
- BdrvChild *parent)
275
- * job_finish_sync()), block_job_completed() won't free it and therefore
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
276
- * the blockers on the intermediate nodes remain. This would cause
173
+ BdrvChild *parent)
277
+ * job_finish_sync()), job_completed() won't free it and therefore the
174
{
278
+ * blockers on the intermediate nodes remain. This would cause
175
BdrvChild *child, *next;
279
* bdrv_set_backing_hd() to fail. */
176
int old_quiesce_counter;
280
block_job_remove_all_bdrv(bjob);
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
281
282
- block_job_completed(&s->common, ret);
283
+ job_completed(job, ret);
284
g_free(data);
285
286
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
287
diff --git a/block/mirror.c b/block/mirror.c
288
index XXXXXXX..XXXXXXX 100644
289
--- a/block/mirror.c
290
+++ b/block/mirror.c
291
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
292
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
293
294
/* Make sure that the source BDS doesn't go away before we called
295
- * block_job_completed(). */
296
+ * job_completed(). */
297
bdrv_ref(src);
298
bdrv_ref(mirror_top_bs);
299
bdrv_ref(target_bs);
300
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
301
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
302
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
303
304
- block_job_completed(&s->common, data->ret);
305
+ job_completed(job, data->ret);
306
307
g_free(data);
308
bdrv_drained_end(src);
309
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(Job *job, Error **errp)
310
}
178
}
311
179
312
s->should_complete = true;
180
if (recursive) {
313
- block_job_enter(&s->common);
181
+ bs->recursive_quiesce_counter--;
314
+ job_enter(job);
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
315
}
183
bdrv_do_drained_end(child->bs, true, child);
316
184
}
317
static void mirror_pause(Job *job)
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
318
diff --git a/block/replication.c b/block/replication.c
186
bdrv_do_drained_end(bs, true, NULL);
319
index XXXXXXX..XXXXXXX 100644
187
}
320
--- a/block/replication.c
188
321
+++ b/block/replication.c
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
322
@@ -XXX,XX +XXX,XX @@ static void replication_close(BlockDriverState *bs)
190
+{
323
replication_stop(s->rs, false, NULL);
191
+ int i;
324
}
192
+
325
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
326
- block_job_cancel_sync(s->active_disk->bs->job);
194
+ bdrv_do_drained_begin(child->bs, true, child);
327
+ job_cancel_sync(&s->active_disk->bs->job->job);
328
}
329
330
if (s->mode == REPLICATION_MODE_SECONDARY) {
331
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
332
* disk, secondary disk in backup_job_completed().
333
*/
334
if (s->secondary_disk->bs->job) {
335
- block_job_cancel_sync(s->secondary_disk->bs->job);
336
+ job_cancel_sync(&s->secondary_disk->bs->job->job);
337
}
338
339
if (!failover) {
340
diff --git a/block/stream.c b/block/stream.c
341
index XXXXXXX..XXXXXXX 100644
342
--- a/block/stream.c
343
+++ b/block/stream.c
344
@@ -XXX,XX +XXX,XX @@ out:
345
}
346
347
g_free(s->backing_file_str);
348
- block_job_completed(&s->common, data->ret);
349
+ job_completed(job, data->ret);
350
g_free(data);
351
}
352
353
diff --git a/blockdev.c b/blockdev.c
354
index XXXXXXX..XXXXXXX 100644
355
--- a/blockdev.c
356
+++ b/blockdev.c
357
@@ -XXX,XX +XXX,XX @@ void blockdev_mark_auto_del(BlockBackend *blk)
358
aio_context_acquire(aio_context);
359
360
if (bs->job) {
361
- block_job_cancel(bs->job, false);
362
+ job_cancel(&bs->job->job, false);
363
}
364
365
aio_context_release(aio_context);
366
@@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(BlkActionState *common)
367
aio_context = bdrv_get_aio_context(state->bs);
368
aio_context_acquire(aio_context);
369
370
- block_job_cancel_sync(state->job);
371
+ job_cancel_sync(&state->job->job);
372
373
aio_context_release(aio_context);
374
}
375
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(BlkActionState *common)
376
aio_context = bdrv_get_aio_context(state->bs);
377
aio_context_acquire(aio_context);
378
379
- block_job_cancel_sync(state->job);
380
+ job_cancel_sync(&state->job->job);
381
382
aio_context_release(aio_context);
383
}
384
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
385
}
386
387
trace_qmp_block_job_cancel(job);
388
- block_job_user_cancel(job, force, errp);
389
+ job_user_cancel(&job->job, force, errp);
390
out:
391
aio_context_release(aio_context);
392
}
393
diff --git a/blockjob.c b/blockjob.c
394
index XXXXXXX..XXXXXXX 100644
395
--- a/blockjob.c
396
+++ b/blockjob.c
397
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
398
*jobptr = NULL;
399
}
400
401
-void block_job_cancel(BlockJob *job, bool force)
402
-{
403
- if (job->job.status == JOB_STATUS_CONCLUDED) {
404
- job_do_dismiss(&job->job);
405
- return;
406
- }
407
- job_cancel_async(&job->job, force);
408
- if (!job_started(&job->job)) {
409
- block_job_completed(job, -ECANCELED);
410
- } else if (job->job.deferred_to_main_loop) {
411
- job_completed_txn_abort(&job->job);
412
- } else {
413
- block_job_enter(job);
414
- }
415
-}
416
-
417
-void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
418
-{
419
- if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
420
- return;
421
- }
422
- block_job_cancel(job, force);
423
-}
424
-
425
-/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
426
- * used with job_finish_sync() without the need for (rather nasty) function
427
- * pointer casts there. */
428
-static void block_job_cancel_err(Job *job, Error **errp)
429
-{
430
- BlockJob *bjob = container_of(job, BlockJob, job);
431
- assert(is_block_job(job));
432
- block_job_cancel(bjob, false);
433
-}
434
-
435
-int block_job_cancel_sync(BlockJob *job)
436
-{
437
- return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
438
-}
439
-
440
-void block_job_cancel_sync_all(void)
441
-{
442
- BlockJob *job;
443
- AioContext *aio_context;
444
-
445
- while ((job = block_job_next(NULL))) {
446
- aio_context = blk_get_aio_context(job->blk);
447
- aio_context_acquire(aio_context);
448
- block_job_cancel_sync(job);
449
- aio_context_release(aio_context);
450
- }
451
-}
452
-
453
-int block_job_complete_sync(BlockJob *job, Error **errp)
454
-{
455
- return job_finish_sync(&job->job, job_complete, errp);
456
-}
457
-
458
void block_job_progress_update(BlockJob *job, uint64_t done)
459
{
460
job->offset += done;
461
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
462
return job;
463
}
464
465
-void block_job_completed(BlockJob *job, int ret)
466
-{
467
- assert(job && job->job.txn && !job_is_completed(&job->job));
468
- assert(blk_bs(job->blk)->job == job);
469
- job->job.ret = ret;
470
- job_update_rc(&job->job);
471
- trace_block_job_completed(job, ret, job->job.ret);
472
- if (job->job.ret) {
473
- job_completed_txn_abort(&job->job);
474
- } else {
475
- job_completed_txn_success(&job->job);
476
- }
477
-}
478
-
479
-void block_job_enter(BlockJob *job)
480
-{
481
- job_enter_cond(&job->job, NULL);
482
-}
483
-
484
void block_job_yield(BlockJob *job)
485
{
486
assert(job->job.busy);
487
diff --git a/job.c b/job.c
488
index XXXXXXX..XXXXXXX 100644
489
--- a/job.c
490
+++ b/job.c
491
@@ -XXX,XX +XXX,XX @@ bool job_is_completed(Job *job)
492
return false;
493
}
494
495
-bool job_started(Job *job)
496
+static bool job_started(Job *job)
497
{
498
return job->co;
499
}
500
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
501
}
502
503
/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
504
- * Reentering the job coroutine with block_job_enter() before the timer has
505
- * expired is allowed and cancels the timer.
506
+ * Reentering the job coroutine with job_enter() before the timer has expired
507
+ * is allowed and cancels the timer.
508
*
509
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
510
+ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
511
* called explicitly. */
512
void coroutine_fn job_do_yield(Job *job, uint64_t ns)
513
{
514
@@ -XXX,XX +XXX,XX @@ static void job_conclude(Job *job)
515
}
516
}
517
518
-void job_update_rc(Job *job)
519
+static void job_update_rc(Job *job)
520
{
521
if (!job->ret && job_is_cancelled(job)) {
522
job->ret = -ECANCELED;
523
@@ -XXX,XX +XXX,XX @@ static int job_finalize_single(Job *job)
524
return 0;
525
}
526
527
-void job_cancel_async(Job *job, bool force)
528
+static void job_cancel_async(Job *job, bool force)
529
{
530
if (job->user_paused) {
531
/* Do not call job_enter here, the caller will handle it. */
532
@@ -XXX,XX +XXX,XX @@ void job_cancel_async(Job *job, bool force)
533
job->force_cancel |= force;
534
}
535
536
-void job_completed_txn_abort(Job *job)
537
+static void job_completed_txn_abort(Job *job)
538
{
539
AioContext *ctx;
540
JobTxn *txn = job->txn;
541
@@ -XXX,XX +XXX,XX @@ static int job_transition_to_pending(Job *job)
542
return 0;
543
}
544
545
-void job_completed_txn_success(Job *job)
546
+static void job_completed_txn_success(Job *job)
547
{
548
JobTxn *txn = job->txn;
549
Job *other_job;
550
@@ -XXX,XX +XXX,XX @@ void job_completed_txn_success(Job *job)
551
}
552
}
553
554
+void job_completed(Job *job, int ret)
555
+{
556
+ assert(job && job->txn && !job_is_completed(job));
557
+ job->ret = ret;
558
+ job_update_rc(job);
559
+ trace_job_completed(job, ret, job->ret);
560
+ if (job->ret) {
561
+ job_completed_txn_abort(job);
562
+ } else {
563
+ job_completed_txn_success(job);
564
+ }
195
+ }
565
+}
196
+}
566
+
197
+
567
+void job_cancel(Job *job, bool force)
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
568
+{
199
+{
569
+ if (job->status == JOB_STATUS_CONCLUDED) {
200
+ int i;
570
+ job_do_dismiss(job);
201
+
571
+ return;
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
572
+ }
204
+ }
573
+ job_cancel_async(job, force);
205
+}
574
+ if (!job_started(job)) {
206
+
575
+ job_completed(job, -ECANCELED);
207
/*
576
+ } else if (job->deferred_to_main_loop) {
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
577
+ job_completed_txn_abort(job);
209
* and suspend block driver's internal I/O until next request arrives.
578
+ } else {
579
+ job_enter(job);
580
+ }
581
+}
582
+
583
+void job_user_cancel(Job *job, bool force, Error **errp)
584
+{
585
+ if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) {
586
+ return;
587
+ }
588
+ job_cancel(job, force);
589
+}
590
+
591
+/* A wrapper around job_cancel() taking an Error ** parameter so it may be
592
+ * used with job_finish_sync() without the need for (rather nasty) function
593
+ * pointer casts there. */
594
+static void job_cancel_err(Job *job, Error **errp)
595
+{
596
+ job_cancel(job, false);
597
+}
598
+
599
+int job_cancel_sync(Job *job)
600
+{
601
+ return job_finish_sync(job, &job_cancel_err, NULL);
602
+}
603
+
604
+void job_cancel_sync_all(void)
605
+{
606
+ Job *job;
607
+ AioContext *aio_context;
608
+
609
+ while ((job = job_next(NULL))) {
610
+ aio_context = job->aio_context;
611
+ aio_context_acquire(aio_context);
612
+ job_cancel_sync(job);
613
+ aio_context_release(aio_context);
614
+ }
615
+}
616
+
617
+int job_complete_sync(Job *job, Error **errp)
618
+{
619
+ return job_finish_sync(job, job_complete, errp);
620
+}
621
+
622
void job_complete(Job *job, Error **errp)
623
{
624
/* Should not be reachable via external interface for internal jobs */
625
diff --git a/qemu-img.c b/qemu-img.c
626
index XXXXXXX..XXXXXXX 100644
627
--- a/qemu-img.c
628
+++ b/qemu-img.c
629
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
630
} while (!job->ready && !job_is_completed(&job->job));
631
632
if (!job_is_completed(&job->job)) {
633
- ret = block_job_complete_sync(job, errp);
634
+ ret = job_complete_sync(&job->job, errp);
635
} else {
636
ret = job->job.ret;
637
}
638
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
639
index XXXXXXX..XXXXXXX 100644
640
--- a/tests/test-bdrv-drain.c
641
+++ b/tests/test-bdrv-drain.c
642
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
643
644
static void test_job_completed(Job *job, void *opaque)
645
{
646
- BlockJob *bjob = container_of(job, BlockJob, job);
647
- block_job_completed(bjob, 0);
648
+ job_completed(job, 0);
649
}
650
651
static void coroutine_fn test_job_start(void *opaque)
652
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
653
g_assert_false(job->job.paused);
654
g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
655
656
- ret = block_job_complete_sync(job, &error_abort);
657
+ ret = job_complete_sync(&job->job, &error_abort);
658
g_assert_cmpint(ret, ==, 0);
659
660
blk_unref(blk_src);
661
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
662
index XXXXXXX..XXXXXXX 100644
663
--- a/tests/test-blockjob-txn.c
664
+++ b/tests/test-blockjob-txn.c
665
@@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque)
666
rc = -ECANCELED;
667
}
668
669
- block_job_completed(bjob, rc);
670
+ job_completed(job, rc);
671
bdrv_unref(bs);
672
}
673
674
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
675
job_start(&job->job);
676
677
if (expected == -ECANCELED) {
678
- block_job_cancel(job, false);
679
+ job_cancel(&job->job, false);
680
}
681
682
while (result == -EINPROGRESS) {
683
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
684
job_txn_unref(txn);
685
686
if (expected1 == -ECANCELED) {
687
- block_job_cancel(job1, false);
688
+ job_cancel(&job1->job, false);
689
}
690
if (expected2 == -ECANCELED) {
691
- block_job_cancel(job2, false);
692
+ job_cancel(&job2->job, false);
693
}
694
695
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
696
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
697
job_start(&job1->job);
698
job_start(&job2->job);
699
700
- block_job_cancel(job1, false);
701
+ job_cancel(&job1->job, false);
702
703
/* Now make job2 finish before the main loop kicks jobs. This simulates
704
* the race between a pending kick and another job completing.
705
*/
706
- block_job_enter(job2);
707
- block_job_enter(job2);
708
+ job_enter(&job2->job);
709
+ job_enter(&job2->job);
710
711
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
712
aio_poll(qemu_get_aio_context(), true);
713
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
714
index XXXXXXX..XXXXXXX 100644
715
--- a/tests/test-blockjob.c
716
+++ b/tests/test-blockjob.c
717
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
718
719
static void cancel_job_completed(Job *job, void *opaque)
720
{
721
- BlockJob *bjob = container_of(job, BlockJob, job);
722
CancelJob *s = opaque;
723
s->completed = true;
724
- block_job_completed(bjob, 0);
725
+ job_completed(job, 0);
726
}
727
728
static void cancel_job_complete(Job *job, Error **errp)
729
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
730
BlockBackend *blk = s->blk;
731
JobStatus sts = job->job.status;
732
733
- block_job_cancel_sync(job);
734
+ job_cancel_sync(&job->job);
735
if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
736
BlockJob *dummy = job;
737
block_job_dismiss(&dummy, &error_abort);
738
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
739
assert(job->job.status == JOB_STATUS_RUNNING);
740
741
job_user_pause(&job->job, &error_abort);
742
- block_job_enter(job);
743
+ job_enter(&job->job);
744
assert(job->job.status == JOB_STATUS_PAUSED);
745
746
cancel_common(s);
747
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
748
assert(job->job.status == JOB_STATUS_RUNNING);
749
750
s->should_converge = true;
751
- block_job_enter(job);
752
+ job_enter(&job->job);
753
assert(job->job.status == JOB_STATUS_READY);
754
755
cancel_common(s);
756
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
757
assert(job->job.status == JOB_STATUS_RUNNING);
758
759
s->should_converge = true;
760
- block_job_enter(job);
761
+ job_enter(&job->job);
762
assert(job->job.status == JOB_STATUS_READY);
763
764
job_user_pause(&job->job, &error_abort);
765
- block_job_enter(job);
766
+ job_enter(&job->job);
767
assert(job->job.status == JOB_STATUS_STANDBY);
768
769
cancel_common(s);
770
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
771
assert(job->job.status == JOB_STATUS_RUNNING);
772
773
s->should_converge = true;
774
- block_job_enter(job);
775
+ job_enter(&job->job);
776
assert(job->job.status == JOB_STATUS_READY);
777
778
job_complete(&job->job, &error_abort);
779
- block_job_enter(job);
780
+ job_enter(&job->job);
781
while (!s->completed) {
782
aio_poll(qemu_get_aio_context(), true);
783
}
784
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
785
assert(job->job.status == JOB_STATUS_RUNNING);
786
787
s->should_converge = true;
788
- block_job_enter(job);
789
+ job_enter(&job->job);
790
assert(job->job.status == JOB_STATUS_READY);
791
792
job_complete(&job->job, &error_abort);
793
- block_job_enter(job);
794
+ job_enter(&job->job);
795
while (!s->completed) {
796
aio_poll(qemu_get_aio_context(), true);
797
}
798
diff --git a/block/trace-events b/block/trace-events
799
index XXXXXXX..XXXXXXX 100644
800
--- a/block/trace-events
801
+++ b/block/trace-events
802
@@ -XXX,XX +XXX,XX @@
803
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
804
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
805
806
-# blockjob.c
807
-block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
808
-
809
# block/block-backend.c
810
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
811
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
812
diff --git a/trace-events b/trace-events
813
index XXXXXXX..XXXXXXX 100644
814
--- a/trace-events
815
+++ b/trace-events
816
@@ -XXX,XX +XXX,XX @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe
817
# job.c
818
job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
819
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
820
+job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
821
822
### Guest events, keep at bottom
823
824
--
210
--
825
2.13.6
211
2.13.6
826
212
827
213
diff view generated by jsdifflib
1
This moves reference counting from BlockJob to Job.
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
---
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
4
1 file changed, 80 insertions(+)
2
5
3
In order to keep calling the BlockJob cleanup code when the job is
4
deleted via job_unref(), introduce a new JobDriver.free callback. Every
5
block job must use block_job_free() for this callback, this is asserted
6
in block_job_create().
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: John Snow <jsnow@redhat.com>
11
---
12
include/block/blockjob.h | 21 -------------------
13
include/block/blockjob_int.h | 7 +++++++
14
include/qemu/job.h | 19 ++++++++++++++++--
15
block/backup.c | 1 +
16
block/commit.c | 1 +
17
block/mirror.c | 2 ++
18
block/stream.c | 1 +
19
blockjob.c | 48 +++++++++++++++++++-------------------------
20
job.c | 22 ++++++++++++++++----
21
qemu-img.c | 4 ++--
22
tests/test-bdrv-drain.c | 1 +
23
tests/test-blockjob-txn.c | 1 +
24
tests/test-blockjob.c | 6 ++++--
25
13 files changed, 76 insertions(+), 58 deletions(-)
26
27
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
28
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/blockjob.h
30
+++ b/include/block/blockjob.h
31
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
32
/** The opaque value that is passed to the completion function. */
33
void *opaque;
34
35
- /** Reference count of the block job */
36
- int refcnt;
37
-
38
/** True when job has reported completion by calling block_job_completed. */
39
bool completed;
40
41
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
42
BlockJobTxn *block_job_txn_new(void);
43
44
/**
45
- * block_job_ref:
46
- *
47
- * Add a reference to BlockJob refcnt, it will be decreased with
48
- * block_job_unref, and then be freed if it comes to be the last
49
- * reference.
50
- */
51
-void block_job_ref(BlockJob *job);
52
-
53
-/**
54
- * block_job_unref:
55
- *
56
- * Release a reference that was previously acquired with block_job_ref
57
- * or block_job_create. If it's the last reference to the object, it will be
58
- * freed.
59
- */
60
-void block_job_unref(BlockJob *job);
61
-
62
-/**
63
* block_job_txn_unref:
64
*
65
* Release a reference that was previously acquired with block_job_txn_add_job
66
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
67
index XXXXXXX..XXXXXXX 100644
68
--- a/include/block/blockjob_int.h
69
+++ b/include/block/blockjob_int.h
70
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
71
BlockCompletionFunc *cb, void *opaque, Error **errp);
72
73
/**
74
+ * block_job_free:
75
+ * Callback to be used for JobDriver.free in all block jobs. Frees block job
76
+ * specific resources in @job.
77
+ */
78
+void block_job_free(Job *job);
79
+
80
+/**
81
* block_job_sleep_ns:
82
* @job: The job that calls the function.
83
* @ns: How many nanoseconds to stop for.
84
diff --git a/include/qemu/job.h b/include/qemu/job.h
85
index XXXXXXX..XXXXXXX 100644
86
--- a/include/qemu/job.h
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
89
/** The type of this job. */
90
const JobDriver *driver;
91
92
+ /** Reference count of the block job */
93
+ int refcnt;
94
+
95
/** Current state; See @JobStatus for details. */
96
JobStatus status;
97
98
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
99
100
/** Enum describing the operation */
101
JobType job_type;
102
+
103
+ /** Called when the job is freed */
104
+ void (*free)(Job *job);
105
};
106
107
108
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
109
*/
110
void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
111
112
-/** Frees the @job object. */
113
-void job_delete(Job *job);
114
+/**
115
+ * Add a reference to Job refcnt, it will be decreased with job_unref, and then
116
+ * be freed if it comes to be the last reference.
117
+ */
118
+void job_ref(Job *job);
119
+
120
+/**
121
+ * Release a reference that was previously acquired with job_ref() or
122
+ * job_create(). If it's the last reference to the object, it will be freed.
123
+ */
124
+void job_unref(Job *job);
125
126
/** Returns the JobType of a given Job. */
127
JobType job_type(const Job *job);
128
diff --git a/block/backup.c b/block/backup.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/block/backup.c
131
+++ b/block/backup.c
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
133
.job_driver = {
134
.instance_size = sizeof(BackupBlockJob),
135
.job_type = JOB_TYPE_BACKUP,
136
+ .free = block_job_free,
137
},
138
.start = backup_run,
139
.commit = backup_commit,
140
diff --git a/block/commit.c b/block/commit.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/block/commit.c
143
+++ b/block/commit.c
144
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
145
.job_driver = {
146
.instance_size = sizeof(CommitBlockJob),
147
.job_type = JOB_TYPE_COMMIT,
148
+ .free = block_job_free,
149
},
150
.start = commit_run,
151
};
152
diff --git a/block/mirror.c b/block/mirror.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/mirror.c
155
+++ b/block/mirror.c
156
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
157
.job_driver = {
158
.instance_size = sizeof(MirrorBlockJob),
159
.job_type = JOB_TYPE_MIRROR,
160
+ .free = block_job_free,
161
},
162
.start = mirror_run,
163
.complete = mirror_complete,
164
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
165
.job_driver = {
166
.instance_size = sizeof(MirrorBlockJob),
167
.job_type = JOB_TYPE_COMMIT,
168
+ .free = block_job_free,
169
},
170
.start = mirror_run,
171
.complete = mirror_complete,
172
diff --git a/block/stream.c b/block/stream.c
173
index XXXXXXX..XXXXXXX 100644
174
--- a/block/stream.c
175
+++ b/block/stream.c
176
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
177
.job_driver = {
178
.instance_size = sizeof(StreamBlockJob),
179
.job_type = JOB_TYPE_STREAM,
180
+ .free = block_job_free,
181
},
182
.start = stream_run,
183
};
184
diff --git a/blockjob.c b/blockjob.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/blockjob.c
187
+++ b/blockjob.c
188
@@ -XXX,XX +XXX,XX @@ static void block_job_resume(BlockJob *job)
189
block_job_enter_cond(job, block_job_timer_not_pending);
190
}
191
192
-void block_job_ref(BlockJob *job)
193
-{
194
- ++job->refcnt;
195
-}
196
-
197
static void block_job_attached_aio_context(AioContext *new_context,
198
void *opaque);
199
static void block_job_detach_aio_context(void *opaque);
200
201
-void block_job_unref(BlockJob *job)
202
+void block_job_free(Job *job)
203
{
204
- if (--job->refcnt == 0) {
205
- assert(job->job.status == JOB_STATUS_NULL);
206
- assert(!job->txn);
207
- BlockDriverState *bs = blk_bs(job->blk);
208
- bs->job = NULL;
209
- block_job_remove_all_bdrv(job);
210
- blk_remove_aio_context_notifier(job->blk,
211
- block_job_attached_aio_context,
212
- block_job_detach_aio_context, job);
213
- blk_unref(job->blk);
214
- error_free(job->blocker);
215
- assert(!timer_pending(&job->sleep_timer));
216
- job_delete(&job->job);
217
- }
218
+ BlockJob *bjob = container_of(job, BlockJob, job);
219
+ BlockDriverState *bs = blk_bs(bjob->blk);
220
+
221
+ assert(!bjob->txn);
222
+
223
+ bs->job = NULL;
224
+ block_job_remove_all_bdrv(bjob);
225
+ blk_remove_aio_context_notifier(bjob->blk,
226
+ block_job_attached_aio_context,
227
+ block_job_detach_aio_context, bjob);
228
+ blk_unref(bjob->blk);
229
+ error_free(bjob->blocker);
230
+ assert(!timer_pending(&bjob->sleep_timer));
231
}
232
233
static void block_job_attached_aio_context(AioContext *new_context,
234
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
235
BlockJob *job = opaque;
236
237
/* In case the job terminates during aio_poll()... */
238
- block_job_ref(job);
239
+ job_ref(&job->job);
240
241
block_job_pause(job);
242
243
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
244
block_job_drain(job);
245
}
246
247
- block_job_unref(job);
248
+ job_unref(&job->job);
249
}
250
251
static char *child_job_get_parent_desc(BdrvChild *c)
252
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
253
job->deferred_to_main_loop = true;
254
block_job_txn_del_job(job);
255
job_state_transition(&job->job, JOB_STATUS_NULL);
256
- block_job_unref(job);
257
+ job_unref(&job->job);
258
}
259
260
static void block_job_do_dismiss(BlockJob *job)
261
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
262
263
assert(blk_bs(job->blk)->job == job);
264
265
- block_job_ref(job);
266
+ job_ref(&job->job);
267
268
if (finish) {
269
finish(job, &local_err);
270
}
271
if (local_err) {
272
error_propagate(errp, local_err);
273
- block_job_unref(job);
274
+ job_unref(&job->job);
275
return -EBUSY;
276
}
277
/* block_job_drain calls block_job_enter, and it should be enough to
278
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
279
aio_poll(qemu_get_aio_context(), true);
280
}
281
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
282
- block_job_unref(job);
283
+ job_unref(&job->job);
284
return ret;
285
}
286
287
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
288
}
289
290
assert(is_block_job(&job->job));
291
+ assert(job->job.driver->free == &block_job_free);
292
293
job->driver = driver;
294
job->blk = blk;
295
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
296
job->busy = false;
297
job->paused = true;
298
job->pause_count = 1;
299
- job->refcnt = 1;
300
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
301
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
302
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
303
diff --git a/job.c b/job.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/job.c
306
+++ b/job.c
307
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
308
job = g_malloc0(driver->instance_size);
309
job->driver = driver;
310
job->id = g_strdup(job_id);
311
+ job->refcnt = 1;
312
313
job_state_transition(job, JOB_STATUS_CREATED);
314
315
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
316
return job;
317
}
318
319
-void job_delete(Job *job)
320
+void job_ref(Job *job)
321
{
322
- QLIST_REMOVE(job, job_list);
323
+ ++job->refcnt;
324
+}
325
+
326
+void job_unref(Job *job)
327
+{
328
+ if (--job->refcnt == 0) {
329
+ assert(job->status == JOB_STATUS_NULL);
330
331
- g_free(job->id);
332
- g_free(job);
333
+ if (job->driver->free) {
334
+ job->driver->free(job);
335
+ }
336
+
337
+ QLIST_REMOVE(job, job_list);
338
+
339
+ g_free(job->id);
340
+ g_free(job);
341
+ }
342
}
343
diff --git a/qemu-img.c b/qemu-img.c
344
index XXXXXXX..XXXXXXX 100644
345
--- a/qemu-img.c
346
+++ b/qemu-img.c
347
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
348
int ret = 0;
349
350
aio_context_acquire(aio_context);
351
- block_job_ref(job);
352
+ job_ref(&job->job);
353
do {
354
aio_poll(aio_context, true);
355
qemu_progress_print(job->len ?
356
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
357
} else {
358
ret = job->ret;
359
}
360
- block_job_unref(job);
361
+ job_unref(&job->job);
362
aio_context_release(aio_context);
363
364
/* publish completion progress only when success */
365
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
366
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
367
--- a/tests/test-bdrv-drain.c
8
--- a/tests/test-bdrv-drain.c
368
+++ b/tests/test-bdrv-drain.c
9
+++ b/tests/test-bdrv-drain.c
369
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
370
BlockJobDriver test_job_driver = {
11
blk_unref(blk_b);
371
.job_driver = {
372
.instance_size = sizeof(TestBlockJob),
373
+ .free = block_job_free,
374
},
375
.start = test_job_start,
376
.complete = test_job_complete,
377
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
378
index XXXXXXX..XXXXXXX 100644
379
--- a/tests/test-blockjob-txn.c
380
+++ b/tests/test-blockjob-txn.c
381
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
382
static const BlockJobDriver test_block_job_driver = {
383
.job_driver = {
384
.instance_size = sizeof(TestBlockJob),
385
+ .free = block_job_free,
386
},
387
.start = test_block_job_run,
388
};
389
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
390
index XXXXXXX..XXXXXXX 100644
391
--- a/tests/test-blockjob.c
392
+++ b/tests/test-blockjob.c
393
@@ -XXX,XX +XXX,XX @@
394
static const BlockJobDriver test_block_job_driver = {
395
.job_driver = {
396
.instance_size = sizeof(BlockJob),
397
+ .free = block_job_free,
398
},
399
};
400
401
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
402
static const BlockJobDriver test_cancel_driver = {
403
.job_driver = {
404
.instance_size = sizeof(CancelJob),
405
+ .free = block_job_free,
406
},
407
.start = cancel_job_start,
408
.complete = cancel_job_complete,
409
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
410
blk = create_blk(NULL);
411
job = mk_job(blk, "Steve", &test_cancel_driver, true,
412
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
413
- block_job_ref(job);
414
+ job_ref(&job->job);
415
assert(job->job.status == JOB_STATUS_CREATED);
416
s = container_of(job, CancelJob, common);
417
s->blk = blk;
418
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
419
block_job_dismiss(&dummy, &error_abort);
420
}
421
assert(job->job.status == JOB_STATUS_NULL);
422
- block_job_unref(job);
423
+ job_unref(&job->job);
424
destroy_blk(blk);
425
}
12
}
426
13
14
+static void test_graph_change(void)
15
+{
16
+ BlockBackend *blk_a, *blk_b;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
19
+
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
22
+ &error_abort);
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
91
+}
92
+
93
94
typedef struct TestBlockJob {
95
BlockJob common;
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
97
98
g_test_add_func("/bdrv-drain/nested", test_nested);
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
101
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
427
--
104
--
428
2.13.6
105
2.13.6
429
106
430
107
diff view generated by jsdifflib
Deleted patch
1
When block jobs need an AioContext, they just take it from their main
2
block node. Generic jobs don't have a main block node, so we need to
3
assign them an AioContext explicitly.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
9
include/qemu/job.h | 7 ++++++-
10
blockjob.c | 5 ++++-
11
job.c | 4 +++-
12
3 files changed, 13 insertions(+), 3 deletions(-)
13
14
diff --git a/include/qemu/job.h b/include/qemu/job.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/qemu/job.h
17
+++ b/include/qemu/job.h
18
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
19
/** Current state; See @JobStatus for details. */
20
JobStatus status;
21
22
+ /** AioContext to run the job coroutine in */
23
+ AioContext *aio_context;
24
+
25
/**
26
* Set to true if the job should cancel itself. The flag must
27
* always be tested just before toggling the busy flag from false
28
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
29
*
30
* @job_id: The id of the newly-created job, or %NULL for internal jobs
31
* @driver: The class object for the newly-created job.
32
+ * @ctx: The AioContext to run the job coroutine in.
33
* @errp: Error object.
34
*/
35
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
36
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
37
+ Error **errp);
38
39
/**
40
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
41
diff --git a/blockjob.c b/blockjob.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/blockjob.c
44
+++ b/blockjob.c
45
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
46
{
47
BlockJob *job = opaque;
48
49
+ job->job.aio_context = new_context;
50
if (job->driver->attached_aio_context) {
51
job->driver->attached_aio_context(job, new_context);
52
}
53
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
54
block_job_drain(job);
55
}
56
57
+ job->job.aio_context = NULL;
58
job_unref(&job->job);
59
}
60
61
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
62
return NULL;
63
}
64
65
- job = job_create(job_id, &driver->job_driver, errp);
66
+ job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
67
+ errp);
68
if (job == NULL) {
69
blk_unref(blk);
70
return NULL;
71
diff --git a/job.c b/job.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/job.c
74
+++ b/job.c
75
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id)
76
return NULL;
77
}
78
79
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
80
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
81
+ Error **errp)
82
{
83
Job *job;
84
85
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
86
job->driver = driver;
87
job->id = g_strdup(job_id);
88
job->refcnt = 1;
89
+ job->aio_context = ctx;
90
91
job_state_transition(job, JOB_STATUS_CREATED);
92
93
--
94
2.13.6
95
96
diff view generated by jsdifflib
Deleted patch
1
Since we introduced an explicit status to block job, BlockJob.completed
2
is redundant because it can be derived from the status. Remove the field
3
from BlockJob and add a function to derive it from the status at the Job
4
level.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
---
10
include/block/blockjob.h | 3 ---
11
include/qemu/job.h | 3 +++
12
blockjob.c | 16 +++++++---------
13
job.c | 22 ++++++++++++++++++++++
14
qemu-img.c | 4 ++--
15
5 files changed, 34 insertions(+), 14 deletions(-)
16
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
20
+++ b/include/block/blockjob.h
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
22
/** The opaque value that is passed to the completion function. */
23
void *opaque;
24
25
- /** True when job has reported completion by calling block_job_completed. */
26
- bool completed;
27
-
28
/** ret code passed to block_job_completed. */
29
int ret;
30
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/qemu/job.h
34
+++ b/include/qemu/job.h
35
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
36
/** Returns whether the job is scheduled for cancellation. */
37
bool job_is_cancelled(Job *job);
38
39
+/** Returns whether the job is in a completed state. */
40
+bool job_is_completed(Job *job);
41
+
42
/**
43
* Request @job to pause at the next pause point. Must be paired with
44
* job_resume(). If the job is supposed to be resumed by user action, call
45
diff --git a/blockjob.c b/blockjob.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/blockjob.c
48
+++ b/blockjob.c
49
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
50
51
job_pause(&job->job);
52
53
- while (!job->job.paused && !job->completed) {
54
+ while (!job->job.paused && !job_is_completed(&job->job)) {
55
block_job_drain(job);
56
}
57
58
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
59
static void block_job_decommission(BlockJob *job)
60
{
61
assert(job);
62
- job->completed = true;
63
job->job.busy = false;
64
job->job.paused = false;
65
job->job.deferred_to_main_loop = true;
66
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
67
68
static int block_job_finalize_single(BlockJob *job)
69
{
70
- assert(job->completed);
71
+ assert(job_is_completed(&job->job));
72
73
/* Ensure abort is called for late-transactional failures */
74
block_job_update_rc(job);
75
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
76
/* block_job_drain calls block_job_enter, and it should be enough to
77
* induce progress until the job completes or moves to the main thread.
78
*/
79
- while (!job->job.deferred_to_main_loop && !job->completed) {
80
+ while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
81
block_job_drain(job);
82
}
83
- while (!job->completed) {
84
+ while (!job_is_completed(&job->job)) {
85
aio_poll(qemu_get_aio_context(), true);
86
}
87
ret = (job_is_cancelled(&job->job) && job->ret == 0)
88
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
89
while (!QLIST_EMPTY(&txn->jobs)) {
90
other_job = QLIST_FIRST(&txn->jobs);
91
ctx = blk_get_aio_context(other_job->blk);
92
- if (!other_job->completed) {
93
+ if (!job_is_completed(&other_job->job)) {
94
assert(job_is_cancelled(&other_job->job));
95
block_job_finish_sync(other_job, NULL, NULL);
96
}
97
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
98
* txn.
99
*/
100
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
101
- if (!other_job->completed) {
102
+ if (!job_is_completed(&other_job->job)) {
103
return;
104
}
105
assert(other_job->ret == 0);
106
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job)
107
108
void block_job_completed(BlockJob *job, int ret)
109
{
110
- assert(job && job->txn && !job->completed);
111
+ assert(job && job->txn && !job_is_completed(&job->job));
112
assert(blk_bs(job->blk)->job == job);
113
- job->completed = true;
114
job->ret = ret;
115
block_job_update_rc(job);
116
trace_block_job_completed(job, ret, job->ret);
117
diff --git a/job.c b/job.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/job.c
120
+++ b/job.c
121
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
122
return job->cancelled;
123
}
124
125
+bool job_is_completed(Job *job)
126
+{
127
+ switch (job->status) {
128
+ case JOB_STATUS_UNDEFINED:
129
+ case JOB_STATUS_CREATED:
130
+ case JOB_STATUS_RUNNING:
131
+ case JOB_STATUS_PAUSED:
132
+ case JOB_STATUS_READY:
133
+ case JOB_STATUS_STANDBY:
134
+ return false;
135
+ case JOB_STATUS_WAITING:
136
+ case JOB_STATUS_PENDING:
137
+ case JOB_STATUS_ABORTING:
138
+ case JOB_STATUS_CONCLUDED:
139
+ case JOB_STATUS_NULL:
140
+ return true;
141
+ default:
142
+ g_assert_not_reached();
143
+ }
144
+ return false;
145
+}
146
+
147
bool job_started(Job *job)
148
{
149
return job->co;
150
diff --git a/qemu-img.c b/qemu-img.c
151
index XXXXXXX..XXXXXXX 100644
152
--- a/qemu-img.c
153
+++ b/qemu-img.c
154
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
155
aio_poll(aio_context, true);
156
qemu_progress_print(job->len ?
157
((float)job->offset / job->len * 100.f) : 0.0f, 0);
158
- } while (!job->ready && !job->completed);
159
+ } while (!job->ready && !job_is_completed(&job->job));
160
161
- if (!job->completed) {
162
+ if (!job_is_completed(&job->job)) {
163
ret = block_job_complete_sync(job, errp);
164
} else {
165
ret = job->ret;
166
--
167
2.13.6
168
169
diff view generated by jsdifflib
1
This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL
1
Since commit bde70715, base is the only node that is reopened in
2
checks to job_create() and the auto_{finalize,dismiss} fields from
2
commit_start(). This means that the code, which still involves an
3
BlockJob to Job.
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
4
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
7
---
8
include/block/blockjob.h | 17 -----------------
8
block/commit.c | 8 +-------
9
include/block/blockjob_int.h | 3 +--
9
1 file changed, 1 insertion(+), 7 deletions(-)
10
include/qemu/job.h | 20 +++++++++++++++++++-
11
block/commit.c | 2 +-
12
block/mirror.c | 2 +-
13
block/replication.c | 4 ++--
14
block/stream.c | 2 +-
15
blockdev.c | 14 +++++++-------
16
blockjob.c | 27 +++++++--------------------
17
job.c | 11 ++++++++++-
18
qemu-img.c | 2 +-
19
tests/test-blockjob-txn.c | 2 +-
20
tests/test-blockjob.c | 4 ++--
21
13 files changed, 53 insertions(+), 57 deletions(-)
22
10
23
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob.h
26
+++ b/include/block/blockjob.h
27
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
28
/** ret code passed to block_job_completed. */
29
int ret;
30
31
- /** True if this job should automatically finalize itself */
32
- bool auto_finalize;
33
-
34
- /** True if this job should automatically dismiss itself */
35
- bool auto_dismiss;
36
-
37
BlockJobTxn *txn;
38
QLIST_ENTRY(BlockJob) txn_list;
39
} BlockJob;
40
41
-typedef enum BlockJobCreateFlags {
42
- /* Default behavior */
43
- BLOCK_JOB_DEFAULT = 0x00,
44
- /* BlockJob is not QMP-created and should not send QMP events */
45
- BLOCK_JOB_INTERNAL = 0x01,
46
- /* BlockJob requires manual finalize step */
47
- BLOCK_JOB_MANUAL_FINALIZE = 0x02,
48
- /* BlockJob requires manual dismiss step */
49
- BLOCK_JOB_MANUAL_DISMISS = 0x04,
50
-} BlockJobCreateFlags;
51
-
52
/**
53
* block_job_next:
54
* @job: A block job, or %NULL.
55
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
56
index XXXXXXX..XXXXXXX 100644
57
--- a/include/block/blockjob_int.h
58
+++ b/include/block/blockjob_int.h
59
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
60
* @bs: The block
61
* @perm, @shared_perm: Permissions to request for @bs
62
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
63
- * @flags: Creation flags for the Block Job.
64
- * See @BlockJobCreateFlags
65
+ * @flags: Creation flags for the Block Job. See @JobCreateFlags.
66
* @cb: Completion function for the job.
67
* @opaque: Opaque pointer value passed to @cb.
68
* @errp: Error object.
69
diff --git a/include/qemu/job.h b/include/qemu/job.h
70
index XXXXXXX..XXXXXXX 100644
71
--- a/include/qemu/job.h
72
+++ b/include/qemu/job.h
73
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
74
/** Set to true when the job has deferred work to the main loop. */
75
bool deferred_to_main_loop;
76
77
+ /** True if this job should automatically finalize itself */
78
+ bool auto_finalize;
79
+
80
+ /** True if this job should automatically dismiss itself */
81
+ bool auto_dismiss;
82
+
83
/** Element of the list of jobs */
84
QLIST_ENTRY(Job) job_list;
85
} Job;
86
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
87
void (*free)(Job *job);
88
};
89
90
+typedef enum JobCreateFlags {
91
+ /* Default behavior */
92
+ JOB_DEFAULT = 0x00,
93
+ /* Job is not QMP-created and should not send QMP events */
94
+ JOB_INTERNAL = 0x01,
95
+ /* Job requires manual finalize step */
96
+ JOB_MANUAL_FINALIZE = 0x02,
97
+ /* Job requires manual dismiss step */
98
+ JOB_MANUAL_DISMISS = 0x04,
99
+} JobCreateFlags;
100
+
101
102
/**
103
* Create a new long-running job and return it.
104
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
105
* @job_id: The id of the newly-created job, or %NULL for internal jobs
106
* @driver: The class object for the newly-created job.
107
* @ctx: The AioContext to run the job coroutine in.
108
+ * @flags: Creation flags for the job. See @JobCreateFlags.
109
* @errp: Error object.
110
*/
111
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
112
- Error **errp);
113
+ int flags, Error **errp);
114
115
/**
116
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
117
diff --git a/block/commit.c b/block/commit.c
11
diff --git a/block/commit.c b/block/commit.c
118
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
119
--- a/block/commit.c
13
--- a/block/commit.c
120
+++ b/block/commit.c
14
+++ b/block/commit.c
121
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
122
}
16
const char *filter_node_name, Error **errp)
123
124
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
125
- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
126
+ speed, JOB_DEFAULT, NULL, NULL, errp);
127
if (!s) {
128
return;
129
}
130
diff --git a/block/mirror.c b/block/mirror.c
131
index XXXXXXX..XXXXXXX 100644
132
--- a/block/mirror.c
133
+++ b/block/mirror.c
134
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
135
}
136
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
137
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
138
- mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
139
+ mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces,
140
speed, granularity, buf_size, backing_mode,
141
on_source_error, on_target_error, unmap, NULL, NULL,
142
&mirror_job_driver, is_none_mode, base, false,
143
diff --git a/block/replication.c b/block/replication.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/replication.c
146
+++ b/block/replication.c
147
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
148
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
149
0, MIRROR_SYNC_MODE_NONE, NULL, false,
150
BLOCKDEV_ON_ERROR_REPORT,
151
- BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
152
+ BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
153
backup_job_completed, bs, NULL, &local_err);
154
if (local_err) {
155
error_propagate(errp, local_err);
156
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
157
158
s->stage = BLOCK_REPLICATION_FAILOVER;
159
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
160
- BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
161
+ JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
162
NULL, replication_done, bs, true, errp);
163
break;
164
default:
165
diff --git a/block/stream.c b/block/stream.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/stream.c
168
+++ b/block/stream.c
169
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
170
BLK_PERM_GRAPH_MOD,
171
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
172
BLK_PERM_WRITE,
173
- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
174
+ speed, JOB_DEFAULT, NULL, NULL, errp);
175
if (!s) {
176
goto fail;
177
}
178
diff --git a/blockdev.c b/blockdev.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/blockdev.c
181
+++ b/blockdev.c
182
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
183
goto out;
184
}
185
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
186
- BLOCK_JOB_DEFAULT, speed, on_error,
187
+ JOB_DEFAULT, speed, on_error,
188
filter_node_name, NULL, NULL, false, &local_err);
189
} else {
190
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
191
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
192
AioContext *aio_context;
193
QDict *options = NULL;
194
Error *local_err = NULL;
195
- int flags, job_flags = BLOCK_JOB_DEFAULT;
196
+ int flags, job_flags = JOB_DEFAULT;
197
int64_t size;
198
bool set_backing_hd = false;
199
200
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
201
}
202
}
203
if (!backup->auto_finalize) {
204
- job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
205
+ job_flags |= JOB_MANUAL_FINALIZE;
206
}
207
if (!backup->auto_dismiss) {
208
- job_flags |= BLOCK_JOB_MANUAL_DISMISS;
209
+ job_flags |= JOB_MANUAL_DISMISS;
210
}
211
212
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
213
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
214
Error *local_err = NULL;
215
AioContext *aio_context;
216
BlockJob *job = NULL;
217
- int job_flags = BLOCK_JOB_DEFAULT;
218
+ int job_flags = JOB_DEFAULT;
219
220
if (!backup->has_speed) {
221
backup->speed = 0;
222
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
223
}
224
}
225
if (!backup->auto_finalize) {
226
- job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
227
+ job_flags |= JOB_MANUAL_FINALIZE;
228
}
229
if (!backup->auto_dismiss) {
230
- job_flags |= BLOCK_JOB_MANUAL_DISMISS;
231
+ job_flags |= JOB_MANUAL_DISMISS;
232
}
233
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
234
backup->sync, NULL, backup->compress,
235
diff --git a/blockjob.c b/blockjob.c
236
index XXXXXXX..XXXXXXX 100644
237
--- a/blockjob.c
238
+++ b/blockjob.c
239
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
240
static void block_job_conclude(BlockJob *job)
241
{
17
{
242
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
18
CommitBlockJob *s;
243
- if (job->auto_dismiss || !job_started(&job->job)) {
19
- BlockReopenQueue *reopen_queue = NULL;
244
+ if (job->job.auto_dismiss || !job_started(&job->job)) {
20
int orig_base_flags;
245
block_job_do_dismiss(job);
21
BlockDriverState *iter;
246
}
22
BlockDriverState *commit_top_bs = NULL;
247
}
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
248
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
24
/* convert base to r/w, if necessary */
249
25
orig_base_flags = bdrv_get_flags(base);
250
static int block_job_needs_finalize(BlockJob *job)
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
251
{
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
252
- return !job->auto_finalize;
28
- orig_base_flags | BDRV_O_RDWR);
253
+ return !job->job.auto_finalize;
254
}
255
256
static void block_job_do_finalize(BlockJob *job)
257
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
258
info->io_status = job->iostatus;
259
info->ready = job->ready;
260
info->status = job->job.status;
261
- info->auto_finalize = job->auto_finalize;
262
- info->auto_dismiss = job->auto_dismiss;
263
+ info->auto_finalize = job->job.auto_finalize;
264
+ info->auto_dismiss = job->job.auto_dismiss;
265
info->has_error = job->ret != 0;
266
info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
267
return info;
268
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
269
static int block_job_event_pending(BlockJob *job)
270
{
271
job_state_transition(&job->job, JOB_STATUS_PENDING);
272
- if (!job->auto_finalize && !block_job_is_internal(job)) {
273
+ if (!job->job.auto_finalize && !block_job_is_internal(job)) {
274
qapi_event_send_block_job_pending(job_type(&job->job),
275
job->job.id,
276
&error_abort);
277
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
278
return NULL;
279
}
280
281
- if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) {
282
+ if (job_id == NULL && !(flags & JOB_INTERNAL)) {
283
job_id = bdrv_get_device_name(bs);
284
- if (!*job_id) {
285
- error_setg(errp, "An explicit job ID is required for this node");
286
- return NULL;
287
- }
288
- }
29
- }
289
-
30
-
290
- if (job_id) {
31
- if (reopen_queue) {
291
- if (flags & BLOCK_JOB_INTERNAL) {
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
292
- error_setg(errp, "Cannot specify job ID for internal block job");
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
293
- return NULL;
34
if (local_err != NULL) {
294
- }
35
error_propagate(errp, local_err);
295
}
36
goto fail;
296
297
blk = blk_new(perm, shared_perm);
298
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
299
}
300
301
job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
302
- errp);
303
+ flags, errp);
304
if (job == NULL) {
305
blk_unref(blk);
306
return NULL;
307
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
308
job->blk = blk;
309
job->cb = cb;
310
job->opaque = opaque;
311
- job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
312
- job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
313
314
error_setg(&job->blocker, "block device is in use by block job: %s",
315
job_type_str(&job->job));
316
diff --git a/job.c b/job.c
317
index XXXXXXX..XXXXXXX 100644
318
--- a/job.c
319
+++ b/job.c
320
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
321
}
322
323
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
324
- Error **errp)
325
+ int flags, Error **errp)
326
{
327
Job *job;
328
329
if (job_id) {
330
+ if (flags & JOB_INTERNAL) {
331
+ error_setg(errp, "Cannot specify job ID for internal job");
332
+ return NULL;
333
+ }
334
if (!id_wellformed(job_id)) {
335
error_setg(errp, "Invalid job ID '%s'", job_id);
336
return NULL;
337
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
338
error_setg(errp, "Job ID '%s' already in use", job_id);
339
return NULL;
340
}
341
+ } else if (!(flags & JOB_INTERNAL)) {
342
+ error_setg(errp, "An explicit job ID is required");
343
+ return NULL;
344
}
345
346
job = g_malloc0(driver->instance_size);
347
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
348
job->busy = false;
349
job->paused = true;
350
job->pause_count = 1;
351
+ job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
352
+ job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
353
354
job_state_transition(job, JOB_STATUS_CREATED);
355
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
356
diff --git a/qemu-img.c b/qemu-img.c
357
index XXXXXXX..XXXXXXX 100644
358
--- a/qemu-img.c
359
+++ b/qemu-img.c
360
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
361
362
aio_context = bdrv_get_aio_context(bs);
363
aio_context_acquire(aio_context);
364
- commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
365
+ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
366
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
367
&cbi, false, &local_err);
368
aio_context_release(aio_context);
369
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
370
index XXXXXXX..XXXXXXX 100644
371
--- a/tests/test-blockjob-txn.c
372
+++ b/tests/test-blockjob-txn.c
373
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
374
375
snprintf(job_id, sizeof(job_id), "job%u", counter++);
376
s = block_job_create(job_id, &test_block_job_driver, txn, bs,
377
- 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
378
+ 0, BLK_PERM_ALL, 0, JOB_DEFAULT,
379
test_block_job_cb, data, &error_abort);
380
s->iterations = iterations;
381
s->use_timer = use_timer;
382
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
383
index XXXXXXX..XXXXXXX 100644
384
--- a/tests/test-blockjob.c
385
+++ b/tests/test-blockjob.c
386
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
387
bool should_succeed)
388
{
389
return mk_job(blk, id, &test_block_job_driver,
390
- should_succeed, BLOCK_JOB_DEFAULT);
391
+ should_succeed, JOB_DEFAULT);
392
}
393
394
/* This creates a BlockBackend (optionally with a name) with a
395
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
396
397
blk = create_blk(NULL);
398
job = mk_job(blk, "Steve", &test_cancel_driver, true,
399
- BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
400
+ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
401
job_ref(&job->job);
402
assert(job->job.status == JOB_STATUS_CREATED);
403
s = container_of(job, CancelJob, common);
404
--
37
--
405
2.13.6
38
2.13.6
406
39
407
40
diff view generated by jsdifflib
1
block_job_event_pending() doesn't only send a QMP event, but it also
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
transitions to the PENDING state. Split the function so that we get one
2
changed between queuing nodes for reopen and actually reopening them
3
part only sending the event (like other block_job_event_* functions) and
3
(one of the reasons is that queuing can be recursive).
4
another part that does the state transition.
4
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
5
8
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
8
---
11
---
9
blockjob.c | 27 ++++++++++++++++++---------
12
block.c | 23 ++++++++++++++++-------
10
1 file changed, 18 insertions(+), 9 deletions(-)
13
block/replication.c | 6 ++++++
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
11
16
12
diff --git a/blockjob.c b/blockjob.c
17
diff --git a/block.c b/block.c
13
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
14
--- a/blockjob.c
19
--- a/block.c
15
+++ b/blockjob.c
20
+++ b/block.c
16
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
17
22
* returns a pointer to bs_queue, which is either the newly allocated
18
static void block_job_event_cancelled(BlockJob *job);
23
* bs_queue, or the existing bs_queue being used.
19
static void block_job_event_completed(BlockJob *job, const char *msg);
24
*
20
-static int block_job_event_pending(BlockJob *job);
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
21
+static void block_job_event_pending(BlockJob *job);
26
*/
22
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
23
/* Transactional group of block jobs */
28
BlockDriverState *bs,
24
struct BlockJobTxn {
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
25
@@ -XXX,XX +XXX,XX @@ static void block_job_do_finalize(BlockJob *job)
30
BdrvChild *child;
31
QDict *old_options, *explicit_options;
32
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
37
+
38
if (bs_queue == NULL) {
39
bs_queue = g_new0(BlockReopenQueue, 1);
40
QSIMPLEQ_INIT(bs_queue);
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
42
* If all devices prepare successfully, then the changes are committed
43
* to all devices.
44
*
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
46
+ * bdrv_reopen_multiple().
47
*/
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
49
{
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
51
52
assert(bs_queue != NULL);
53
54
- aio_context_release(ctx);
55
- bdrv_drain_all_begin();
56
- aio_context_acquire(ctx);
57
-
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
61
error_propagate(errp, local_err);
62
goto cleanup;
63
@@ -XXX,XX +XXX,XX @@ cleanup:
26
}
64
}
65
g_free(bs_queue);
66
67
- bdrv_drain_all_end();
68
-
69
return ret;
27
}
70
}
28
71
29
+static int block_job_transition_to_pending(BlockJob *job)
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
30
+{
73
{
31
+ job_state_transition(&job->job, JOB_STATUS_PENDING);
74
int ret = -1;
32
+ if (!job->job.auto_finalize) {
75
Error *local_err = NULL;
33
+ block_job_event_pending(job);
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
34
+ }
77
+ BlockReopenQueue *queue;
35
+ return 0;
78
36
+}
79
+ bdrv_subtree_drained_begin(bs);
37
+
80
+
38
static void block_job_completed_txn_success(BlockJob *job)
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
39
{
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
40
BlockJobTxn *txn = job->txn;
83
if (local_err != NULL) {
41
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
84
error_propagate(errp, local_err);
42
assert(other_job->ret == 0);
43
}
85
}
44
86
+
45
- block_job_txn_apply(txn, block_job_event_pending, false);
87
+ bdrv_subtree_drained_end(bs);
46
+ block_job_txn_apply(txn, block_job_transition_to_pending, false);
88
+
47
89
return ret;
48
/* If no jobs need manual finalization, automatically do so */
49
if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
50
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
51
&error_abort);
52
}
90
}
53
91
54
-static int block_job_event_pending(BlockJob *job)
92
diff --git a/block/replication.c b/block/replication.c
55
+static void block_job_event_pending(BlockJob *job)
93
index XXXXXXX..XXXXXXX 100644
56
{
94
--- a/block/replication.c
57
- job_state_transition(&job->job, JOB_STATUS_PENDING);
95
+++ b/block/replication.c
58
- if (!job->job.auto_finalize && !block_job_is_internal(job)) {
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
59
- qapi_event_send_block_job_pending(job_type(&job->job),
97
new_secondary_flags = s->orig_secondary_flags;
60
- job->job.id,
61
- &error_abort);
62
+ if (block_job_is_internal(job)) {
63
+ return;
64
}
98
}
65
- return 0;
99
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
66
+
102
+
67
+ qapi_event_send_block_job_pending(job_type(&job->job),
103
if (orig_hidden_flags != new_hidden_flags) {
68
+ job->job.id,
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
69
+ &error_abort);
105
new_hidden_flags);
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
107
reopen_queue, &local_err);
108
error_propagate(errp, local_err);
109
}
110
+
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
70
}
113
}
71
114
72
/*
115
static void backup_job_cleanup(BlockDriverState *bs)
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
117
index XXXXXXX..XXXXXXX 100644
118
--- a/qemu-io-cmds.c
119
+++ b/qemu-io-cmds.c
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
122
qemu_opts_reset(&reopen_opts);
123
124
+ bdrv_subtree_drained_begin(bs);
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
127
+ bdrv_subtree_drained_end(bs);
128
+
129
if (local_err) {
130
error_report_err(local_err);
131
} else {
73
--
132
--
74
2.13.6
133
2.13.6
75
134
76
135
diff view generated by jsdifflib
Deleted patch
1
block_job_cancel_async() did two things that were still block job
2
specific:
3
1
4
* Setting job->force. This field makes sense on the Job level, so we can
5
just move it. While at it, rename it to job->force_cancel to make its
6
purpose more obvious.
7
8
* Resetting the I/O status. This can't be moved because generic Jobs
9
don't have an I/O status. What the function really implements is a
10
user resume, except without entering the coroutine. Consequently, it
11
makes sense to call the .user_resume driver callback here which
12
already resets the I/O status.
13
14
The old block_job_cancel_async() has two separate if statements that
15
check job->iostatus != BLOCK_DEVICE_IO_STATUS_OK and job->user_paused.
16
However, the former condition always implies the latter (as is
17
asserted in block_job_iostatus_reset()), so changing the explicit call
18
of block_job_iostatus_reset() on the former condition with the
19
.user_resume callback on the latter condition is equivalent and
20
doesn't need to access any BlockJob specific state.
21
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Reviewed-by: Max Reitz <mreitz@redhat.com>
24
---
25
include/block/blockjob.h | 6 ------
26
include/qemu/job.h | 6 ++++++
27
block/mirror.c | 4 ++--
28
blockjob.c | 25 +++++++++++++------------
29
4 files changed, 21 insertions(+), 20 deletions(-)
30
31
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/block/blockjob.h
34
+++ b/include/block/blockjob.h
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
36
BlockBackend *blk;
37
38
/**
39
- * Set to true if the job should abort immediately without waiting
40
- * for data to be in sync.
41
- */
42
- bool force;
43
-
44
- /**
45
* Set to true when the job is ready to be completed.
46
*/
47
bool ready;
48
diff --git a/include/qemu/job.h b/include/qemu/job.h
49
index XXXXXXX..XXXXXXX 100644
50
--- a/include/qemu/job.h
51
+++ b/include/qemu/job.h
52
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
53
*/
54
bool cancelled;
55
56
+ /**
57
+ * Set to true if the job should abort immediately without waiting
58
+ * for data to be in sync.
59
+ */
60
+ bool force_cancel;
61
+
62
/** Set to true when the job has deferred work to the main loop. */
63
bool deferred_to_main_loop;
64
65
diff --git a/block/mirror.c b/block/mirror.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/block/mirror.c
68
+++ b/block/mirror.c
69
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
70
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
71
job_sleep_ns(&s->common.job, delay_ns);
72
if (job_is_cancelled(&s->common.job) &&
73
- (!s->synced || s->common.force))
74
+ (!s->synced || s->common.job.force_cancel))
75
{
76
break;
77
}
78
@@ -XXX,XX +XXX,XX @@ immediate_exit:
79
* or it was cancelled prematurely so that we do not guarantee that
80
* the target is a copy of the source.
81
*/
82
- assert(ret < 0 || ((s->common.force || !s->synced) &&
83
+ assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) &&
84
job_is_cancelled(&s->common.job)));
85
assert(need_drain);
86
mirror_wait_for_all_io(s);
87
diff --git a/blockjob.c b/blockjob.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/blockjob.c
90
+++ b/blockjob.c
91
@@ -XXX,XX +XXX,XX @@ static int block_job_prepare(BlockJob *job)
92
return job->job.ret;
93
}
94
95
-static void block_job_cancel_async(BlockJob *job, bool force)
96
+static void job_cancel_async(Job *job, bool force)
97
{
98
- if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
99
- block_job_iostatus_reset(job);
100
- }
101
- if (job->job.user_paused) {
102
- /* Do not call block_job_enter here, the caller will handle it. */
103
- job->job.user_paused = false;
104
- job->job.pause_count--;
105
+ if (job->user_paused) {
106
+ /* Do not call job_enter here, the caller will handle it. */
107
+ job->user_paused = false;
108
+ if (job->driver->user_resume) {
109
+ job->driver->user_resume(job);
110
+ }
111
+ assert(job->pause_count > 0);
112
+ job->pause_count--;
113
}
114
- job->job.cancelled = true;
115
+ job->cancelled = true;
116
/* To prevent 'force == false' overriding a previous 'force == true' */
117
- job->force |= force;
118
+ job->force_cancel |= force;
119
}
120
121
static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
122
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
123
* on the caller, so leave it. */
124
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
125
if (other_job != job) {
126
- block_job_cancel_async(other_job, false);
127
+ job_cancel_async(&other_job->job, false);
128
}
129
}
130
while (!QLIST_EMPTY(&txn->jobs)) {
131
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
132
job_do_dismiss(&job->job);
133
return;
134
}
135
- block_job_cancel_async(job, force);
136
+ job_cancel_async(&job->job, force);
137
if (!job_started(&job->job)) {
138
block_job_completed(job, -ECANCELED);
139
} else if (job->job.deferred_to_main_loop) {
140
--
141
2.13.6
142
143
diff view generated by jsdifflib
Deleted patch
1
BlockJob has fields .offset and .len, which are actually misnomers today
2
because they are no longer tied to block device sizes, but just progress
3
counters. As such they make a lot of sense in generic Jobs.
4
1
5
This patch moves the fields to Job and renames them to .progress_current
6
and .progress_total to describe their function better.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/blockjob.h | 25 -------------------------
12
include/qemu/job.h | 28 ++++++++++++++++++++++++++++
13
block/backup.c | 8 ++++----
14
block/commit.c | 4 ++--
15
block/mirror.c | 4 ++--
16
block/stream.c | 4 ++--
17
blockjob.c | 26 ++++++++------------------
18
job.c | 10 ++++++++++
19
qemu-img.c | 8 ++++++--
20
9 files changed, 62 insertions(+), 55 deletions(-)
21
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
25
+++ b/include/block/blockjob.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
27
/** Status that is published by the query-block-jobs QMP API */
28
BlockDeviceIoStatus iostatus;
29
30
- /** Offset that is published by the query-block-jobs QMP API */
31
- int64_t offset;
32
-
33
- /** Length that is published by the query-block-jobs QMP API */
34
- int64_t len;
35
-
36
/** Speed that was set with @block_job_set_speed. */
37
int64_t speed;
38
39
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
40
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
41
42
/**
43
- * block_job_progress_update:
44
- * @job: The job that has made progress
45
- * @done: How much progress the job made
46
- *
47
- * Updates the progress counter of the job.
48
- */
49
-void block_job_progress_update(BlockJob *job, uint64_t done);
50
-
51
-/**
52
- * block_job_progress_set_remaining:
53
- * @job: The job whose expected progress end value is set
54
- * @remaining: Expected end value of the progress counter of the job
55
- *
56
- * Sets the expected end value of the progress counter of a job so that a
57
- * completion percentage can be calculated when the progress is updated.
58
- */
59
-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
60
-
61
-/**
62
* block_job_query:
63
* @job: The job to get information about.
64
*
65
diff --git a/include/qemu/job.h b/include/qemu/job.h
66
index XXXXXXX..XXXXXXX 100644
67
--- a/include/qemu/job.h
68
+++ b/include/qemu/job.h
69
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
70
/** True if this job should automatically dismiss itself */
71
bool auto_dismiss;
72
73
+ /**
74
+ * Current progress. The unit is arbitrary as long as the ratio between
75
+ * progress_current and progress_total represents the estimated percentage
76
+ * of work already done.
77
+ */
78
+ int64_t progress_current;
79
+
80
+ /** Estimated progress_current value at the completion of the job */
81
+ int64_t progress_total;
82
+
83
/** ret code passed to job_completed. */
84
int ret;
85
86
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
87
*/
88
void job_unref(Job *job);
89
90
+/**
91
+ * @job: The job that has made progress
92
+ * @done: How much progress the job made since the last call
93
+ *
94
+ * Updates the progress counter of the job.
95
+ */
96
+void job_progress_update(Job *job, uint64_t done);
97
+
98
+/**
99
+ * @job: The job whose expected progress end value is set
100
+ * @remaining: Missing progress (on top of the current progress counter value)
101
+ * until the new expected end value is reached
102
+ *
103
+ * Sets the expected end value of the progress counter of a job so that a
104
+ * completion percentage can be calculated when the progress is updated.
105
+ */
106
+void job_progress_set_remaining(Job *job, uint64_t remaining);
107
+
108
/** To be called when a cancelled job is finalised. */
109
void job_event_cancelled(Job *job);
110
111
diff --git a/block/backup.c b/block/backup.c
112
index XXXXXXX..XXXXXXX 100644
113
--- a/block/backup.c
114
+++ b/block/backup.c
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
116
* offset field is an opaque progress value, it is not a disk offset.
117
*/
118
job->bytes_read += n;
119
- block_job_progress_update(&job->common, n);
120
+ job_progress_update(&job->common.job, n);
121
}
122
123
out:
124
@@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
125
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
126
}
127
128
- /* TODO block_job_progress_set_remaining() would make more sense */
129
- block_job_progress_update(&job->common,
130
+ /* TODO job_progress_set_remaining() would make more sense */
131
+ job_progress_update(&job->common.job,
132
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
133
134
bdrv_dirty_iter_free(dbi);
135
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
136
qemu_co_rwlock_init(&job->flush_rwlock);
137
138
nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
139
- block_job_progress_set_remaining(&job->common, job->len);
140
+ job_progress_set_remaining(&job->common.job, job->len);
141
142
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
143
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
144
diff --git a/block/commit.c b/block/commit.c
145
index XXXXXXX..XXXXXXX 100644
146
--- a/block/commit.c
147
+++ b/block/commit.c
148
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
149
if (len < 0) {
150
goto out;
151
}
152
- block_job_progress_set_remaining(&s->common, len);
153
+ job_progress_set_remaining(&s->common.job, len);
154
155
ret = base_len = blk_getlength(s->base);
156
if (base_len < 0) {
157
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
158
}
159
}
160
/* Publish progress */
161
- block_job_progress_update(&s->common, n);
162
+ job_progress_update(&s->common.job, n);
163
164
if (copy) {
165
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
166
diff --git a/block/mirror.c b/block/mirror.c
167
index XXXXXXX..XXXXXXX 100644
168
--- a/block/mirror.c
169
+++ b/block/mirror.c
170
@@ -XXX,XX +XXX,XX @@ static void mirror_iteration_done(MirrorOp *op, int ret)
171
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
172
}
173
if (!s->initial_zeroing_ongoing) {
174
- block_job_progress_update(&s->common, op->bytes);
175
+ job_progress_update(&s->common.job, op->bytes);
176
}
177
}
178
qemu_iovec_destroy(&op->qiov);
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
180
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
181
* the number of bytes currently being processed; together those are
182
* the current remaining operation length */
183
- block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt);
184
+ job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt);
185
186
/* Note that even when no rate limit is applied we need to yield
187
* periodically with no pending I/O so that bdrv_drain_all() returns.
188
diff --git a/block/stream.c b/block/stream.c
189
index XXXXXXX..XXXXXXX 100644
190
--- a/block/stream.c
191
+++ b/block/stream.c
192
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
193
ret = len;
194
goto out;
195
}
196
- block_job_progress_set_remaining(&s->common, len);
197
+ job_progress_set_remaining(&s->common.job, len);
198
199
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
200
201
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
202
ret = 0;
203
204
/* Publish progress */
205
- block_job_progress_update(&s->common, n);
206
+ job_progress_update(&s->common.job, n);
207
if (copy) {
208
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
209
} else {
210
diff --git a/blockjob.c b/blockjob.c
211
index XXXXXXX..XXXXXXX 100644
212
--- a/blockjob.c
213
+++ b/blockjob.c
214
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
215
return ratelimit_calculate_delay(&job->limit, n);
216
}
217
218
-void block_job_progress_update(BlockJob *job, uint64_t done)
219
-{
220
- job->offset += done;
221
-}
222
-
223
-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining)
224
-{
225
- job->len = job->offset + remaining;
226
-}
227
-
228
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
229
{
230
BlockJobInfo *info;
231
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
232
info = g_new0(BlockJobInfo, 1);
233
info->type = g_strdup(job_type_str(&job->job));
234
info->device = g_strdup(job->job.id);
235
- info->len = job->len;
236
info->busy = atomic_read(&job->job.busy);
237
info->paused = job->job.pause_count > 0;
238
- info->offset = job->offset;
239
+ info->offset = job->job.progress_current;
240
+ info->len = job->job.progress_total;
241
info->speed = job->speed;
242
info->io_status = job->iostatus;
243
info->ready = job_is_ready(&job->job),
244
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(Notifier *n, void *opaque)
245
246
qapi_event_send_block_job_cancelled(job_type(&job->job),
247
job->job.id,
248
- job->len,
249
- job->offset,
250
+ job->job.progress_total,
251
+ job->job.progress_current,
252
job->speed,
253
&error_abort);
254
}
255
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
256
257
qapi_event_send_block_job_completed(job_type(&job->job),
258
job->job.id,
259
- job->len,
260
- job->offset,
261
+ job->job.progress_total,
262
+ job->job.progress_current,
263
job->speed,
264
!!msg,
265
msg,
266
@@ -XXX,XX +XXX,XX @@ static void block_job_event_ready(Notifier *n, void *opaque)
267
268
qapi_event_send_block_job_ready(job_type(&job->job),
269
job->job.id,
270
- job->len,
271
- job->offset,
272
+ job->job.progress_total,
273
+ job->job.progress_current,
274
job->speed, &error_abort);
275
}
276
277
diff --git a/job.c b/job.c
278
index XXXXXXX..XXXXXXX 100644
279
--- a/job.c
280
+++ b/job.c
281
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
282
}
283
}
284
285
+void job_progress_update(Job *job, uint64_t done)
286
+{
287
+ job->progress_current += done;
288
+}
289
+
290
+void job_progress_set_remaining(Job *job, uint64_t remaining)
291
+{
292
+ job->progress_total = job->progress_current + remaining;
293
+}
294
+
295
void job_event_cancelled(Job *job)
296
{
297
notifier_list_notify(&job->on_finalize_cancelled, job);
298
diff --git a/qemu-img.c b/qemu-img.c
299
index XXXXXXX..XXXXXXX 100644
300
--- a/qemu-img.c
301
+++ b/qemu-img.c
302
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
303
aio_context_acquire(aio_context);
304
job_ref(&job->job);
305
do {
306
+ float progress = 0.0f;
307
aio_poll(aio_context, true);
308
- qemu_progress_print(job->len ?
309
- ((float)job->offset / job->len * 100.f) : 0.0f, 0);
310
+ if (job->job.progress_total) {
311
+ progress = (float)job->job.progress_current /
312
+ job->job.progress_total * 100.f;
313
+ }
314
+ qemu_progress_print(progress, 0);
315
} while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
316
317
if (!job_is_completed(&job->job)) {
318
--
319
2.13.6
320
321
diff view generated by jsdifflib
Deleted patch
1
This adds a separate schema file for all job-related definitions that
2
aren't tied to the block layer.
3
1
4
For a start, move the enums JobType, JobStatus and JobVerb.
5
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
qapi/block-core.json | 90 +-----------------------------------------------
10
qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
11
qapi/qapi-schema.json | 1 +
12
MAINTAINERS | 1 +
13
Makefile | 9 +++++
14
Makefile.objs | 4 +++
15
6 files changed, 110 insertions(+), 89 deletions(-)
16
create mode 100644 qapi/job.json
17
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
19
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
21
+++ b/qapi/block-core.json
22
@@ -XXX,XX +XXX,XX @@
23
24
{ 'include': 'common.json' }
25
{ 'include': 'crypto.json' }
26
+{ 'include': 'job.json' }
27
{ 'include': 'sockets.json' }
28
29
##
30
@@ -XXX,XX +XXX,XX @@
31
'data': ['top', 'full', 'none', 'incremental'] }
32
33
##
34
-# @JobType:
35
-#
36
-# Type of a background job.
37
-#
38
-# @commit: block commit job type, see "block-commit"
39
-#
40
-# @stream: block stream job type, see "block-stream"
41
-#
42
-# @mirror: drive mirror job type, see "drive-mirror"
43
-#
44
-# @backup: drive backup job type, see "drive-backup"
45
-#
46
-# Since: 1.7
47
-##
48
-{ 'enum': 'JobType',
49
- 'data': ['commit', 'stream', 'mirror', 'backup'] }
50
-
51
-##
52
-# @JobVerb:
53
-#
54
-# Represents command verbs that can be applied to a job.
55
-#
56
-# @cancel: see @block-job-cancel
57
-#
58
-# @pause: see @block-job-pause
59
-#
60
-# @resume: see @block-job-resume
61
-#
62
-# @set-speed: see @block-job-set-speed
63
-#
64
-# @complete: see @block-job-complete
65
-#
66
-# @dismiss: see @block-job-dismiss
67
-#
68
-# @finalize: see @block-job-finalize
69
-#
70
-# Since: 2.12
71
-##
72
-{ 'enum': 'JobVerb',
73
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
74
- 'finalize' ] }
75
-
76
-##
77
-# @JobStatus:
78
-#
79
-# Indicates the present state of a given job in its lifetime.
80
-#
81
-# @undefined: Erroneous, default state. Should not ever be visible.
82
-#
83
-# @created: The job has been created, but not yet started.
84
-#
85
-# @running: The job is currently running.
86
-#
87
-# @paused: The job is running, but paused. The pause may be requested by
88
-# either the QMP user or by internal processes.
89
-#
90
-# @ready: The job is running, but is ready for the user to signal completion.
91
-# This is used for long-running jobs like mirror that are designed to
92
-# run indefinitely.
93
-#
94
-# @standby: The job is ready, but paused. This is nearly identical to @paused.
95
-# The job may return to @ready or otherwise be canceled.
96
-#
97
-# @waiting: The job is waiting for other jobs in the transaction to converge
98
-# to the waiting state. This status will likely not be visible for
99
-# the last job in a transaction.
100
-#
101
-# @pending: The job has finished its work, but has finalization steps that it
102
-# needs to make prior to completing. These changes may require
103
-# manual intervention by the management process if manual was set
104
-# to true. These changes may still fail.
105
-#
106
-# @aborting: The job is in the process of being aborted, and will finish with
107
-# an error. The job will afterwards report that it is @concluded.
108
-# This status may not be visible to the management process.
109
-#
110
-# @concluded: The job has finished all work. If manual was set to true, the job
111
-# will remain in the query list until it is dismissed.
112
-#
113
-# @null: The job is in the process of being dismantled. This state should not
114
-# ever be visible externally.
115
-#
116
-# Since: 2.12
117
-##
118
-{ 'enum': 'JobStatus',
119
- 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
120
- 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
121
-
122
-##
123
# @BlockJobInfo:
124
#
125
# Information about a long-running block device operation.
126
diff --git a/qapi/job.json b/qapi/job.json
127
new file mode 100644
128
index XXXXXXX..XXXXXXX
129
--- /dev/null
130
+++ b/qapi/job.json
131
@@ -XXX,XX +XXX,XX @@
132
+# -*- Mode: Python -*-
133
+
134
+##
135
+# == Background jobs
136
+##
137
+
138
+##
139
+# @JobType:
140
+#
141
+# Type of a background job.
142
+#
143
+# @commit: block commit job type, see "block-commit"
144
+#
145
+# @stream: block stream job type, see "block-stream"
146
+#
147
+# @mirror: drive mirror job type, see "drive-mirror"
148
+#
149
+# @backup: drive backup job type, see "drive-backup"
150
+#
151
+# Since: 1.7
152
+##
153
+{ 'enum': 'JobType',
154
+ 'data': ['commit', 'stream', 'mirror', 'backup'] }
155
+
156
+##
157
+# @JobStatus:
158
+#
159
+# Indicates the present state of a given job in its lifetime.
160
+#
161
+# @undefined: Erroneous, default state. Should not ever be visible.
162
+#
163
+# @created: The job has been created, but not yet started.
164
+#
165
+# @running: The job is currently running.
166
+#
167
+# @paused: The job is running, but paused. The pause may be requested by
168
+# either the QMP user or by internal processes.
169
+#
170
+# @ready: The job is running, but is ready for the user to signal completion.
171
+# This is used for long-running jobs like mirror that are designed to
172
+# run indefinitely.
173
+#
174
+# @standby: The job is ready, but paused. This is nearly identical to @paused.
175
+# The job may return to @ready or otherwise be canceled.
176
+#
177
+# @waiting: The job is waiting for other jobs in the transaction to converge
178
+# to the waiting state. This status will likely not be visible for
179
+# the last job in a transaction.
180
+#
181
+# @pending: The job has finished its work, but has finalization steps that it
182
+# needs to make prior to completing. These changes may require
183
+# manual intervention by the management process if manual was set
184
+# to true. These changes may still fail.
185
+#
186
+# @aborting: The job is in the process of being aborted, and will finish with
187
+# an error. The job will afterwards report that it is @concluded.
188
+# This status may not be visible to the management process.
189
+#
190
+# @concluded: The job has finished all work. If manual was set to true, the job
191
+# will remain in the query list until it is dismissed.
192
+#
193
+# @null: The job is in the process of being dismantled. This state should not
194
+# ever be visible externally.
195
+#
196
+# Since: 2.12
197
+##
198
+{ 'enum': 'JobStatus',
199
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
200
+ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
201
+
202
+##
203
+# @JobVerb:
204
+#
205
+# Represents command verbs that can be applied to a job.
206
+#
207
+# @cancel: see @block-job-cancel
208
+#
209
+# @pause: see @block-job-pause
210
+#
211
+# @resume: see @block-job-resume
212
+#
213
+# @set-speed: see @block-job-set-speed
214
+#
215
+# @complete: see @block-job-complete
216
+#
217
+# @dismiss: see @block-job-dismiss
218
+#
219
+# @finalize: see @block-job-finalize
220
+#
221
+# Since: 2.12
222
+##
223
+{ 'enum': 'JobVerb',
224
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
225
+ 'finalize' ] }
226
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
227
index XXXXXXX..XXXXXXX 100644
228
--- a/qapi/qapi-schema.json
229
+++ b/qapi/qapi-schema.json
230
@@ -XXX,XX +XXX,XX @@
231
{ 'include': 'crypto.json' }
232
{ 'include': 'block.json' }
233
{ 'include': 'char.json' }
234
+{ 'include': 'job.json' }
235
{ 'include': 'net.json' }
236
{ 'include': 'rocker.json' }
237
{ 'include': 'tpm.json' }
238
diff --git a/MAINTAINERS b/MAINTAINERS
239
index XXXXXXX..XXXXXXX 100644
240
--- a/MAINTAINERS
241
+++ b/MAINTAINERS
242
@@ -XXX,XX +XXX,XX @@ F: block/backup.c
243
F: block/commit.c
244
F: block/stream.c
245
F: block/mirror.c
246
+F: qapi/job.json
247
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
248
249
Block QAPI, monitor, command line
250
diff --git a/Makefile b/Makefile
251
index XXXXXXX..XXXXXXX 100644
252
--- a/Makefile
253
+++ b/Makefile
254
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
255
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
256
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
257
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
258
+GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
259
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
260
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
261
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
262
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
263
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
264
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
265
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
266
+GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
267
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
268
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
269
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
270
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
271
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
272
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
273
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
274
+GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c
275
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
276
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
277
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
278
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
279
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
280
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
281
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
282
+GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c
283
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
284
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
285
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
286
@@ -XXX,XX +XXX,XX @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
287
$(SRC_PATH)/qapi/char.json \
288
$(SRC_PATH)/qapi/crypto.json \
289
$(SRC_PATH)/qapi/introspect.json \
290
+ $(SRC_PATH)/qapi/job.json \
291
$(SRC_PATH)/qapi/migration.json \
292
$(SRC_PATH)/qapi/misc.json \
293
$(SRC_PATH)/qapi/net.json \
294
@@ -XXX,XX +XXX,XX @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \
295
qapi/qapi-types-common.c qapi/qapi-types-common.h \
296
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
297
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
298
+qapi/qapi-types-job.c qapi/qapi-types-job.h \
299
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
300
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
301
qapi/qapi-types-net.c qapi/qapi-types-net.h \
302
@@ -XXX,XX +XXX,XX @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
303
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
304
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
305
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
306
+qapi/qapi-visit-job.c qapi/qapi-visit-job.h \
307
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
308
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
309
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
310
@@ -XXX,XX +XXX,XX @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
311
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
312
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
313
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
314
+qapi/qapi-commands-job.c qapi/qapi-commands-job.h \
315
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
316
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
317
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
318
@@ -XXX,XX +XXX,XX @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \
319
qapi/qapi-events-common.c qapi/qapi-events-common.h \
320
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
321
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
322
+qapi/qapi-events-job.c qapi/qapi-events-job.h \
323
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
324
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
325
qapi/qapi-events-net.c qapi/qapi-events-net.h \
326
diff --git a/Makefile.objs b/Makefile.objs
327
index XXXXXXX..XXXXXXX 100644
328
--- a/Makefile.objs
329
+++ b/Makefile.objs
330
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-types-char.o
331
util-obj-y += qapi/qapi-types-common.o
332
util-obj-y += qapi/qapi-types-crypto.o
333
util-obj-y += qapi/qapi-types-introspect.o
334
+util-obj-y += qapi/qapi-types-job.o
335
util-obj-y += qapi/qapi-types-migration.o
336
util-obj-y += qapi/qapi-types-misc.o
337
util-obj-y += qapi/qapi-types-net.o
338
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-visit-char.o
339
util-obj-y += qapi/qapi-visit-common.o
340
util-obj-y += qapi/qapi-visit-crypto.o
341
util-obj-y += qapi/qapi-visit-introspect.o
342
+util-obj-y += qapi/qapi-visit-job.o
343
util-obj-y += qapi/qapi-visit-migration.o
344
util-obj-y += qapi/qapi-visit-misc.o
345
util-obj-y += qapi/qapi-visit-net.o
346
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-events-char.o
347
util-obj-y += qapi/qapi-events-common.o
348
util-obj-y += qapi/qapi-events-crypto.o
349
util-obj-y += qapi/qapi-events-introspect.o
350
+util-obj-y += qapi/qapi-events-job.o
351
util-obj-y += qapi/qapi-events-migration.o
352
util-obj-y += qapi/qapi-events-misc.o
353
util-obj-y += qapi/qapi-events-net.o
354
@@ -XXX,XX +XXX,XX @@ common-obj-y += qapi/qapi-commands-char.o
355
common-obj-y += qapi/qapi-commands-common.o
356
common-obj-y += qapi/qapi-commands-crypto.o
357
common-obj-y += qapi/qapi-commands-introspect.o
358
+common-obj-y += qapi/qapi-commands-job.o
359
common-obj-y += qapi/qapi-commands-migration.o
360
common-obj-y += qapi/qapi-commands-misc.o
361
common-obj-y += qapi/qapi-commands-net.o
362
--
363
2.13.6
364
365
diff view generated by jsdifflib