1
The following changes since commit 4f50c1673a89b07f376ce5c42d22d79a79cd466d:
1
The following changes since commit 239b8b0699a222fd21da1c5fdeba0a2456085a47:
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 tag 'trivial-branch-for-8.0-pull-request' of https://gitlab.com/laurent_vivier/qemu into staging (2023-01-19 15:05:29 +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
https://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 4711b0a8490827c332b3f9281f689ce9555b7fab:
10
10
11
qemu-iotests: Test job-* with block jobs (2018-05-23 14:30:52 +0200)
11
qemu-img: Change info key names for protocol nodes (2023-01-20 13:11:01 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches
15
15
16
- Generic background jobs
16
- qemu-img info: Show protocol-level information
17
- qemu-iotests fixes for NFS and the 'migration' group
17
- Move more functions to coroutines
18
- sheepdog: Minor code simplification
18
- Make coroutine annotations ready for static analysis
19
- qemu-img: Fix exit code for errors closing the image
20
- qcow2 bitmaps: Fix theoretical corruption in error path
21
- pflash: Only load non-zero parts of backend image to save memory
22
- Code cleanup and test case improvements
19
23
20
----------------------------------------------------------------
24
----------------------------------------------------------------
21
Kevin Wolf (45):
25
Alberto Faria (2):
22
qemu-iotests: Fix paths for NFS
26
coroutine: annotate coroutine_fn for libclang
23
qemu-iotests: Filter NFS paths
27
block: Add no_coroutine_fn and coroutine_mixed_fn marker
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
28
68
Peter Maydell (1):
29
Emanuele Giuseppe Esposito (14):
69
sheepdog: Remove unnecessary NULL check in sd_prealloc()
30
block-coroutine-wrapper: support void functions
31
block: Convert bdrv_io_plug() to co_wrapper
32
block: Convert bdrv_io_unplug() to co_wrapper
33
block: Convert bdrv_is_inserted() to co_wrapper
34
block: Rename refresh_total_sectors to bdrv_refresh_total_sectors
35
block: Convert bdrv_refresh_total_sectors() to co_wrapper_mixed
36
block-backend: use bdrv_getlength instead of blk_getlength
37
block: use bdrv_co_refresh_total_sectors when possible
38
block: Convert bdrv_get_allocated_file_size() to co_wrapper
39
block: Convert bdrv_get_info() to co_wrapper_mixed
40
block: Convert bdrv_eject() to co_wrapper
41
block: Convert bdrv_lock_medium() to co_wrapper
42
block: Convert bdrv_debug_event() to co_wrapper_mixed
43
block: Rename bdrv_load/save_vmstate() to bdrv_co_load/save_vmstate()
70
44
71
qapi/block-core.json | 116 +---
45
Hanna Reitz (12):
72
qapi/job.json | 253 +++++++++
46
block: Improve empty format-specific info dump
73
qapi/qapi-schema.json | 1 +
47
block/file: Add file-specific image info
74
include/block/block_int.h | 2 +-
48
block/vmdk: Change extent info type
75
include/block/blockjob.h | 324 +----------
49
block: Split BlockNodeInfo off of ImageInfo
76
include/block/blockjob_int.h | 176 +-----
50
qemu-img: Use BlockNodeInfo
77
include/qemu/job.h | 562 ++++++++++++++++++++
51
block/qapi: Let bdrv_query_image_info() recurse
78
block.c | 2 +-
52
block/qapi: Introduce BlockGraphInfo
79
block/backup.c | 59 +-
53
block/qapi: Add indentation to bdrv_node_info_dump()
80
block/commit.c | 44 +-
54
iotests: Filter child node information
81
block/mirror.c | 113 ++--
55
iotests/106, 214, 308: Read only one size line
82
block/replication.c | 10 +-
56
qemu-img: Let info print block graph
83
block/sheepdog.c | 4 +-
57
qemu-img: Change info key names for protocol nodes
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
58
59
Kevin Wolf (4):
60
qcow2: Fix theoretical corruption in store_bitmap() error path
61
qemu-img commit: Report errors while closing the image
62
qemu-img bitmap: Report errors while closing the image
63
qemu-iotests: Test qemu-img bitmap/commit exit code on error
64
65
Paolo Bonzini (2):
66
qemu-io: do not reinvent the blk_pwrite_zeroes wheel
67
block: remove bdrv_coroutine_enter
68
69
Philippe Mathieu-Daudé (1):
70
block/nbd: Add missing <qemu/bswap.h> include
71
72
Thomas Huth (2):
73
tests/qemu-iotests/312: Mark "quorum" as required driver
74
tests/qemu-iotests/262: Check for availability of "blkverify" first
75
76
Xiang Zheng (1):
77
pflash: Only read non-zero parts of backend image
78
79
qapi/block-core.json | 123 +++++++-
80
include/block/block-common.h | 11 +-
81
include/block/block-io.h | 41 ++-
82
include/block/block_int-common.h | 26 +-
83
include/block/block_int-io.h | 5 +-
84
include/block/nbd.h | 1 +
85
include/block/qapi.h | 14 +-
86
include/qemu/coroutine.h | 43 +++
87
include/sysemu/block-backend-io.h | 31 +-
88
block.c | 88 +++---
89
block/blkdebug.c | 11 +-
90
block/blkio.c | 15 +-
91
block/blklogwrites.c | 6 +-
92
block/blkreplay.c | 6 +-
93
block/blkverify.c | 6 +-
94
block/block-backend.c | 38 +--
95
block/commit.c | 4 +-
96
block/copy-on-read.c | 18 +-
97
block/crypto.c | 14 +-
98
block/curl.c | 10 +-
99
block/file-posix.c | 137 +++++----
100
block/file-win32.c | 18 +-
101
block/filter-compress.c | 20 +-
102
block/gluster.c | 23 +-
103
block/io.c | 76 ++---
104
block/iscsi.c | 17 +-
105
block/mirror.c | 6 +-
106
block/monitor/block-hmp-cmds.c | 2 +-
107
block/nbd.c | 8 +-
108
block/nfs.c | 4 +-
109
block/null.c | 13 +-
110
block/nvme.c | 14 +-
111
block/preallocate.c | 16 +-
112
block/qapi.c | 317 ++++++++++++++++-----
113
block/qcow.c | 5 +-
114
block/qcow2-bitmap.c | 5 +-
115
block/qcow2-refcount.c | 2 +-
116
block/qcow2.c | 17 +-
117
block/qed.c | 11 +-
118
block/quorum.c | 8 +-
119
block/raw-format.c | 25 +-
120
block/rbd.c | 9 +-
121
block/replication.c | 6 +-
122
block/ssh.c | 4 +-
123
block/throttle.c | 6 +-
124
block/vdi.c | 7 +-
125
block/vhdx.c | 5 +-
126
block/vmdk.c | 22 +-
127
block/vpc.c | 5 +-
128
blockdev.c | 8 +-
129
hw/block/block.c | 36 ++-
130
hw/scsi/scsi-disk.c | 5 +
131
qemu-img.c | 100 +++++--
132
qemu-io-cmds.c | 62 +---
133
tests/unit/test-block-iothread.c | 3 +
134
scripts/block-coroutine-wrapper.py | 20 +-
135
tests/qemu-iotests/iotests.py | 18 +-
136
block/meson.build | 1 +
137
tests/qemu-iotests/065 | 2 +-
138
tests/qemu-iotests/106 | 4 +-
139
tests/qemu-iotests/214 | 6 +-
140
tests/qemu-iotests/262 | 3 +-
141
tests/qemu-iotests/302.out | 5 +
142
tests/qemu-iotests/308 | 4 +-
143
tests/qemu-iotests/312 | 1 +
144
tests/qemu-iotests/common.filter | 22 +-
145
tests/qemu-iotests/common.rc | 22 +-
146
tests/qemu-iotests/tests/qemu-img-close-errors | 95 ++++++
147
tests/qemu-iotests/tests/qemu-img-close-errors.out | 23 ++
148
69 files changed, 1207 insertions(+), 552 deletions(-)
149
create mode 100755 tests/qemu-iotests/tests/qemu-img-close-errors
150
create mode 100644 tests/qemu-iotests/tests/qemu-img-close-errors.out
151
152
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
From: Thomas Huth <thuth@redhat.com>
2
started to add more Python test cases that don't make use of
3
QMPTestCase. In order to make the method usable there, move it to VM.
4
2
3
"quorum" is required by iotest 312 - if it is not compiled into the
4
QEMU binary, the test fails. Thus list "quorum" as required driver
5
so that the test gets skipped in case it is not available.
6
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
Message-Id: <20230104114601.269351-1-thuth@redhat.com>
9
Reviewed-by: Alberto Garcia <berto@igalia.com>
10
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
13
---
8
tests/qemu-iotests/041 | 6 +++---
14
tests/qemu-iotests/312 | 1 +
9
tests/qemu-iotests/155 | 2 +-
15
1 file changed, 1 insertion(+)
10
tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++++++---------------------
11
3 files changed, 27 insertions(+), 26 deletions(-)
12
16
13
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
17
diff --git a/tests/qemu-iotests/312 b/tests/qemu-iotests/312
14
index XXXXXXX..XXXXXXX 100755
18
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/041
19
--- a/tests/qemu-iotests/312
16
+++ b/tests/qemu-iotests/041
20
+++ b/tests/qemu-iotests/312
17
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
21
@@ -XXX,XX +XXX,XX @@ _supported_fmt qcow2
18
'read-only': 'on' }
22
_supported_proto file
19
23
_supported_os Linux
20
self.vm = iotests.VM()
24
_unsupported_imgopts cluster_size data_file
21
- self.vm.add_blockdev(self.qmp_to_opts(blk0))
25
+_require_drivers quorum
22
- self.vm.add_blockdev(self.qmp_to_opts(blk1))
26
23
- self.vm.add_blockdev(self.qmp_to_opts(blk2))
27
echo
24
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
28
echo '### Create all images' # three source (quorum), one destination
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
45
--- a/tests/qemu-iotests/iotests.py
46
+++ b/tests/qemu-iotests/iotests.py
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
48
return self.qmp('human-monitor-command',
49
command_line='qemu-io %s "%s"' % (drive, cmd))
50
51
+ def flatten_qmp_object(self, obj, output=None, basestr=''):
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
+
64
+ def qmp_to_opts(self, obj):
65
+ obj = self.flatten_qmp_object(obj)
66
+ output_list = list()
67
+ for key in obj:
68
+ output_list += [key + '=' + obj[key]]
69
+ return ','.join(output_list)
70
+
71
+
72
73
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
74
75
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
76
self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
77
return d
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
--
29
--
114
2.13.6
30
2.38.1
115
31
116
32
diff view generated by jsdifflib
1
This adds a minimal query-jobs implementation that shouldn't pose many
1
From: Thomas Huth <thuth@redhat.com>
2
design questions. It can later be extended to expose more information,
3
and especially job-specific information.
4
2
3
In downstream RHEL builds, we do not have "blkverify" enabled, so
4
iotest 262 is currently failing there. Thus let's list "blkverify"
5
as required item so that the test properly gets skipped instead if
6
"blkverify" is missing.
7
8
Signed-off-by: Thomas Huth <thuth@redhat.com>
9
Message-Id: <20230104112850.261480-1-thuth@redhat.com>
10
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
13
---
7
qapi/job.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++
14
tests/qemu-iotests/262 | 3 ++-
8
include/qemu/job.h | 3 +++
15
1 file changed, 2 insertions(+), 1 deletion(-)
9
job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
job.c | 2 +-
11
4 files changed, 104 insertions(+), 1 deletion(-)
12
16
13
diff --git a/qapi/job.json b/qapi/job.json
17
diff --git a/tests/qemu-iotests/262 b/tests/qemu-iotests/262
14
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100755
15
--- a/qapi/job.json
19
--- a/tests/qemu-iotests/262
16
+++ b/qapi/job.json
20
+++ b/tests/qemu-iotests/262
17
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ import iotests
18
# Since: 2.13
22
import os
19
##
23
20
{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
24
iotests.script_initialize(supported_fmts=['qcow2'],
21
+
25
- supported_platforms=['linux'])
22
+##
26
+ supported_platforms=['linux'],
23
+# @JobInfo:
27
+ required_fmts=['blkverify'])
24
+#
28
25
+# Information about a job.
29
with iotests.FilePath('img') as img_path, \
26
+#
30
iotests.FilePath('mig_fifo') as fifo, \
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
+
57
+##
58
+# @query-jobs:
59
+#
60
+# Return information about jobs.
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
+
78
/** Returns whether the job is scheduled for cancellation. */
79
bool job_is_cancelled(Job *job);
80
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
+
90
+static JobInfo *job_query_single(Job *job, Error **errp)
91
+{
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
+ }
100
+
101
+ info = g_new(JobInfo, 1);
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
+}
114
+
115
+JobInfoList *qmp_query_jobs(Error **errp)
116
+{
117
+ JobInfoList *head = NULL, **p_next = &head;
118
+ Job *job;
119
+
120
+ for (job = job_next(NULL); job; job = job_next(job)) {
121
+ JobInfoList *elem;
122
+ AioContext *aio_context;
123
+
124
+ if (job_is_internal(job)) {
125
+ continue;
126
+ }
127
+ elem = g_new0(JobInfoList, 1);
128
+ aio_context = job->aio_context;
129
+ aio_context_acquire(aio_context);
130
+ elem->value = job_query_single(job, errp);
131
+ aio_context_release(aio_context);
132
+ if (!elem->value) {
133
+ g_free(elem);
134
+ qapi_free_JobInfoList(head);
135
+ return NULL;
136
+ }
137
+ *p_next = elem;
138
+ p_next = &elem->next;
139
+ }
140
+
141
+ return head;
142
+}
143
diff --git a/job.c b/job.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/job.c
146
+++ b/job.c
147
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
148
return rc;
149
}
150
151
-static bool job_is_internal(Job *job)
152
+bool job_is_internal(Job *job)
153
{
154
return (job->id == NULL);
155
}
156
--
31
--
157
2.13.6
32
2.38.1
158
33
159
34
diff view generated by jsdifflib
1
block_job_drain() contains a blk_drain() call which cannot be moved to
1
From: Xiang Zheng <zhengxiang9@huawei.com>
2
Job, so add a new JobDriver callback JobDriver.drain which has a common
3
implementation for all BlockJobs. In addition to this we keep the
4
existing BlockJobDriver.drain callback that is called by the common
5
drain implementation for all block jobs.
6
2
3
Currently we fill the VIRT_FLASH memory space with two 64MB NOR images
4
when using persistent UEFI variables on virt board. Actually we only use
5
a very small(non-zero) part of the memory while the rest significant
6
large(zero) part of memory is wasted.
7
8
So this patch checks the block status and only writes the non-zero part
9
into memory. This requires pflash devices to use sparse files for
10
backends.
11
12
Signed-off-by: Xiang Zheng <zhengxiang9@huawei.com>
13
14
[ kraxel: rebased to latest master ]
15
16
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
17
Message-Id: <20221220084246.1984871-1-kraxel@redhat.com>
18
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
19
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
20
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
21
---
10
include/block/blockjob_int.h | 12 ++++++++++++
22
hw/block/block.c | 36 +++++++++++++++++++++++++++++++++++-
11
include/qemu/job.h | 13 +++++++++++++
23
1 file changed, 35 insertions(+), 1 deletion(-)
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
24
23
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
25
diff --git a/hw/block/block.c b/hw/block/block.c
24
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob_int.h
27
--- a/hw/block/block.c
26
+++ b/include/block/blockjob_int.h
28
+++ b/hw/block/block.c
27
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
29
@@ -XXX,XX +XXX,XX @@
28
* If the callback is not NULL, it will be invoked when the job has to be
30
#include "qapi/error.h"
29
* synchronously cancelled or completed; it should drain BlockDriverStates
31
#include "qapi/qapi-types-block.h"
30
* as required to ensure progress.
32
31
+ *
33
+/*
32
+ * Block jobs must use the default implementation for job_driver.drain,
34
+ * Read the non-zeroes parts of @blk into @buf
33
+ * which will in turn call this callback after doing generic block job
35
+ * Reading all of the @blk is expensive if the zeroes parts of @blk
34
+ * stuff.
36
+ * is large enough. Therefore check the block status and only write
35
*/
37
+ * the non-zeroes block into @buf.
36
void (*drain)(BlockJob *job);
38
+ *
37
};
39
+ * Return 0 on success, non-zero on error.
38
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job);
39
void block_job_user_resume(Job *job);
40
41
/**
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
+ */
40
+ */
47
+void block_job_drain(Job *job);
41
+static int blk_pread_nonzeroes(BlockBackend *blk, hwaddr size, void *buf)
42
+{
43
+ int ret;
44
+ int64_t bytes, offset = 0;
45
+ BlockDriverState *bs = blk_bs(blk);
48
+
46
+
49
+/**
47
+ for (;;) {
50
* block_job_yield:
48
+ bytes = MIN(size - offset, BDRV_REQUEST_MAX_SECTORS);
51
* @job: The job that calls the function.
49
+ if (bytes <= 0) {
52
*
50
+ return 0;
53
diff --git a/include/qemu/job.h b/include/qemu/job.h
51
+ }
54
index XXXXXXX..XXXXXXX 100644
52
+ ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL);
55
--- a/include/qemu/job.h
53
+ if (ret < 0) {
56
+++ b/include/qemu/job.h
54
+ return ret;
57
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
55
+ }
58
*/
56
+ if (!(ret & BDRV_BLOCK_ZERO)) {
59
void (*user_resume)(Job *job);
57
+ ret = bdrv_pread(bs->file, offset, bytes,
60
58
+ (uint8_t *) buf + offset, 0);
61
+ /*
59
+ if (ret < 0) {
62
+ * If the callback is not NULL, it will be invoked when the job has to be
60
+ return ret;
63
+ * synchronously cancelled or completed; it should drain any activities
61
+ }
64
+ * as required to ensure progress.
62
+ }
65
+ */
63
+ offset += bytes;
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
}
147
148
-static void block_job_drain(BlockJob *job)
149
+void block_job_drain(Job *job)
150
{
151
- /* If job is !job->job.busy this kicks it into the next pause point. */
152
- block_job_enter(job);
153
+ BlockJob *bjob = container_of(job, BlockJob, job);
154
155
- blk_drain(job->blk);
156
- if (job->driver->drain) {
157
- job->driver->drain(job);
158
+ blk_drain(bjob->blk);
159
+ if (bjob->driver->drain) {
160
+ bjob->driver->drain(bjob);
161
}
162
}
163
164
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
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
}
171
172
job->job.aio_context = NULL;
173
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
174
job_unref(&job->job);
175
return -EBUSY;
176
}
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
+ }
64
+ }
212
+}
65
+}
213
+
66
+
214
+
67
/*
215
/**
68
* Read the entire contents of @blk into @buf.
216
* All jobs must allow a pause point before entering their job proper. This
69
* @blk's contents must be @size bytes, and @size must be at most
217
* ensures that jobs can be paused prior to being started, then resumed later.
70
@@ -XXX,XX +XXX,XX @@ bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size,
218
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
71
* block device and read only on demand.
219
index XXXXXXX..XXXXXXX 100644
72
*/
220
--- a/tests/test-bdrv-drain.c
73
assert(size <= BDRV_REQUEST_MAX_BYTES);
221
+++ b/tests/test-bdrv-drain.c
74
- ret = blk_pread(blk, 0, size, buf, 0);
222
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
75
+ ret = blk_pread_nonzeroes(blk, size, buf);
223
.instance_size = sizeof(TestBlockJob),
76
if (ret < 0) {
224
.free = block_job_free,
77
error_setg_errno(errp, -ret, "can't read block backend");
225
.user_resume = block_job_user_resume,
78
return false;
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
--
79
--
263
2.13.6
80
2.38.1
264
81
265
82
diff view generated by jsdifflib
1
This moves block_job_yield() to the Job layer.
1
From: Alberto Faria <afaria@redhat.com>
2
2
3
Clang has a generic __annotate__ attribute that can be used by
4
static analyzers to understand properties of functions and
5
analyze the control flow. Furthermore, unlike TSA annotations, the
6
__annotate__ attribute applies to function pointers as well.
7
8
As a first step towards static analysis of coroutine_fn markers,
9
attach the attribute to the marker when compiling with clang.
10
11
Signed-off-by: Alberto Faria <afaria@redhat.com>
12
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
13
Message-Id: <20221216110758.559947-2-pbonzini@redhat.com>
14
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
16
---
6
include/block/blockjob_int.h | 8 --------
17
include/qemu/coroutine.h | 4 ++++
7
include/qemu/job.h | 9 +++++++--
18
1 file changed, 4 insertions(+)
8
block/backup.c | 2 +-
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
19
15
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
20
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
16
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/blockjob_int.h
22
--- a/include/qemu/coroutine.h
18
+++ b/include/block/blockjob_int.h
23
+++ b/include/qemu/coroutine.h
19
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job);
24
@@ -XXX,XX +XXX,XX @@
20
void block_job_drain(Job *job);
25
* ....
21
26
* }
22
/**
27
*/
23
- * block_job_yield:
28
+#ifdef __clang__
24
- * @job: The job that calls the function.
29
+#define coroutine_fn __attribute__((__annotate__("coroutine_fn")))
25
- *
30
+#else
26
- * Yield the block job coroutine.
31
#define coroutine_fn
27
- */
32
+#endif
28
-void block_job_yield(BlockJob *job);
33
29
-
34
typedef struct Coroutine Coroutine;
30
-/**
35
31
* block_job_ratelimit_get_delay:
32
*
33
* Calculate and return delay for the next request in ns. See the documentation
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 @@ void coroutine_fn job_pause_point(Job *job);
39
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
}
94
95
-void block_job_yield(BlockJob *job)
96
-{
97
- assert(job->job.busy);
98
-
99
- /* Check cancellation *before* setting busy = false, too! */
100
- if (job_is_cancelled(&job->job)) {
101
- return;
102
- }
103
-
104
- if (!job_should_pause(&job->job)) {
105
- job_do_yield(&job->job, -1);
106
- }
107
-
108
- job_pause_point(&job->job);
109
-}
110
-
111
void block_job_iostatus_reset(BlockJob *job)
112
{
113
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
114
diff --git a/job.c b/job.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/job.c
117
+++ b/job.c
118
@@ -XXX,XX +XXX,XX @@ static bool job_started(Job *job)
119
return job->co;
120
}
121
122
-bool job_should_pause(Job *job)
123
+static bool job_should_pause(Job *job)
124
{
125
return job->pause_count > 0;
126
}
127
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
128
*
129
* If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
130
* called explicitly. */
131
-void coroutine_fn job_do_yield(Job *job, uint64_t ns)
132
+static void coroutine_fn job_do_yield(Job *job, uint64_t ns)
133
{
134
job_lock();
135
if (ns != -1) {
136
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job)
137
}
138
}
139
140
+void job_yield(Job *job)
141
+{
142
+ assert(job->busy);
143
+
144
+ /* Check cancellation *before* setting busy = false, too! */
145
+ if (job_is_cancelled(job)) {
146
+ return;
147
+ }
148
+
149
+ if (!job_should_pause(job)) {
150
+ job_do_yield(job, -1);
151
+ }
152
+
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
--
36
--
173
2.13.6
37
2.38.1
174
175
diff view generated by jsdifflib
1
BlockJob has fields .offset and .len, which are actually misnomers today
1
From: Alberto Faria <afaria@redhat.com>
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
2
5
This patch moves the fields to Job and renames them to .progress_current
3
Add more annotations to functions, describing valid and invalid
6
and .progress_total to describe their function better.
4
calls from coroutine to non-coroutine context.
7
5
6
When applied to a function, no_coroutine_fn advertises that it should
7
not be called from coroutine_fn functions. This can be because the
8
function blocks or, in the case of generated_co_wrapper, to enforce
9
that coroutine_fn functions directly call the coroutine_fn that backs
10
the generated_co_wrapper.
11
12
coroutine_mixed_fn instead is for function that can be called in
13
both coroutine and non-coroutine context, but will suspend when
14
called in coroutine context. Annotating them is a first step
15
towards enforcing that non-annotated functions are absolutely
16
not going to suspend.
17
18
These can be used for example with the vrc tool:
19
20
# find functions that *really* cannot be called from no_coroutine_fn
21
(vrc) load --loader clang libblock.fa.p/meson-generated_.._block_block-gen.c.o
22
(vrc) paths [no_coroutine_fn,!coroutine_mixed_fn]
23
bdrv_remove_persistent_dirty_bitmap
24
bdrv_create
25
bdrv_can_store_new_dirty_bitmap
26
27
# find how coroutine_fns end up calling a mixed function
28
(vrc) load --loader clang --force libblock.fa.p/*.c.o
29
(vrc) paths [coroutine_fn] [!no_coroutine_fn]* [coroutine_mixed_fn]
30
...
31
bdrv_pread <- vhdx_log_write <- vhdx_log_write_and_flush <- vhdx_co_writev
32
...
33
34
Signed-off-by: Alberto Faria <afaria@redhat.com>
35
[Rebase, add coroutine_mixed_fn. - Paolo]
36
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
37
Message-Id: <20221216110758.559947-3-pbonzini@redhat.com>
38
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
39
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
40
---
11
include/block/blockjob.h | 25 -------------------------
41
include/block/block-common.h | 11 ++++++----
12
include/qemu/job.h | 28 ++++++++++++++++++++++++++++
42
include/qemu/coroutine.h | 39 ++++++++++++++++++++++++++++++++++++
13
block/backup.c | 8 ++++----
43
2 files changed, 46 insertions(+), 4 deletions(-)
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
44
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
45
diff --git a/include/block/block-common.h b/include/block/block-common.h
23
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
47
--- a/include/block/block-common.h
25
+++ b/include/block/blockjob.h
48
+++ b/include/block/block-common.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
49
@@ -XXX,XX +XXX,XX @@
27
/** Status that is published by the query-block-jobs QMP API */
50
* - co_wrapper_mixed_bdrv_rdlock are co_wrapper_mixed functions but
28
BlockDeviceIoStatus iostatus;
51
* automatically take and release the graph rdlock when creating a new
29
52
* coroutine.
30
- /** Offset that is published by the query-block-jobs QMP API */
53
+ *
31
- int64_t offset;
54
+ * These functions should not be called from a coroutine_fn; instead,
32
-
55
+ * call the wrapped function directly.
33
- /** Length that is published by the query-block-jobs QMP API */
56
*/
34
- int64_t len;
57
-#define co_wrapper
35
-
58
-#define co_wrapper_mixed
36
/** Speed that was set with @block_job_set_speed. */
59
-#define co_wrapper_bdrv_rdlock
37
int64_t speed;
60
-#define co_wrapper_mixed_bdrv_rdlock
38
61
+#define co_wrapper no_coroutine_fn
39
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
62
+#define co_wrapper_mixed no_coroutine_fn coroutine_mixed_fn
40
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
63
+#define co_wrapper_bdrv_rdlock no_coroutine_fn
41
64
+#define co_wrapper_mixed_bdrv_rdlock no_coroutine_fn coroutine_mixed_fn
42
/**
65
43
- * block_job_progress_update:
66
#include "block/dirty-bitmap.h"
44
- * @job: The job that has made progress
67
#include "block/blockjob.h"
45
- * @done: How much progress the job made
68
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
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
69
index XXXXXXX..XXXXXXX 100644
67
--- a/include/qemu/job.h
70
--- a/include/qemu/coroutine.h
68
+++ b/include/qemu/job.h
71
+++ b/include/qemu/coroutine.h
69
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
72
@@ -XXX,XX +XXX,XX @@
70
/** True if this job should automatically dismiss itself */
73
#define coroutine_fn
71
bool auto_dismiss;
74
#endif
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
75
90
+/**
76
+/**
91
+ * @job: The job that has made progress
77
+ * Mark a function that can suspend when executed in coroutine context,
92
+ * @done: How much progress the job made since the last call
78
+ * but can handle running in non-coroutine context too.
93
+ *
94
+ * Updates the progress counter of the job.
95
+ */
79
+ */
96
+void job_progress_update(Job *job, uint64_t done);
80
+#ifdef __clang__
81
+#define coroutine_mixed_fn __attribute__((__annotate__("coroutine_mixed_fn")))
82
+#else
83
+#define coroutine_mixed_fn
84
+#endif
97
+
85
+
98
+/**
86
+/**
99
+ * @job: The job whose expected progress end value is set
87
+ * Mark a function that should not be called from a coroutine context.
100
+ * @remaining: Missing progress (on top of the current progress counter value)
88
+ * Usually there will be an analogous, coroutine_fn function that should
101
+ * until the new expected end value is reached
89
+ * be used instead.
102
+ *
90
+ *
103
+ * Sets the expected end value of the progress counter of a job so that a
91
+ * When the function is also marked as coroutine_mixed_fn, the function should
104
+ * completion percentage can be calculated when the progress is updated.
92
+ * only be called if the caller does not know whether it is in coroutine
93
+ * context.
94
+ *
95
+ * Functions that are only no_coroutine_fn, on the other hand, should not
96
+ * be called from within coroutines at all. This for example includes
97
+ * functions that block.
98
+ *
99
+ * In the future it would be nice to enable compiler or static checker
100
+ * support for catching such errors. This annotation is the first step
101
+ * towards this, and in the meantime it serves as documentation.
102
+ *
103
+ * For example:
104
+ *
105
+ * static void no_coroutine_fn foo(void) {
106
+ * ....
107
+ * }
105
+ */
108
+ */
106
+void job_progress_set_remaining(Job *job, uint64_t remaining);
109
+#ifdef __clang__
110
+#define no_coroutine_fn __attribute__((__annotate__("no_coroutine_fn")))
111
+#else
112
+#define no_coroutine_fn
113
+#endif
107
+
114
+
108
/** To be called when a cancelled job is finalised. */
115
typedef struct Coroutine Coroutine;
109
void job_event_cancelled(Job *job);
116
110
117
/**
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
--
118
--
319
2.13.6
119
2.38.1
320
321
diff view generated by jsdifflib
1
This moves block_job_dismiss() to the Job layer.
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
2
3
qemu-io's do_co_pwrite_zeroes is reinventing the coroutine wrapper
4
blk_pwrite_zeroes. Just use the real thing directly.
5
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
7
Message-Id: <20221215130225.476477-1-pbonzini@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
10
---
6
include/block/blockjob.h | 9 ---------
11
qemu-io-cmds.c | 57 +++++++++-----------------------------------------
7
include/qemu/job.h | 7 ++++++-
12
1 file changed, 10 insertions(+), 47 deletions(-)
8
blockdev.c | 10 ++++++----
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
14
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
16
--- a/qemu-io-cmds.c
17
+++ b/include/block/blockjob.h
17
+++ b/qemu-io-cmds.c
18
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
18
@@ -XXX,XX +XXX,XX @@ static int do_pwrite(BlockBackend *blk, char *buf, int64_t offset,
19
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
19
return 1;
20
20
}
21
/**
21
22
- * block_job_dismiss:
22
-typedef struct {
23
- * @job: The job to be dismissed.
23
- BlockBackend *blk;
24
- * @errp: Error object.
24
- int64_t offset;
25
- *
25
- int64_t bytes;
26
- * Remove a concluded job from the query list.
26
- int64_t *total;
27
- */
27
- int flags;
28
-void block_job_dismiss(BlockJob **job, Error **errp);
28
- int ret;
29
- bool done;
30
-} CoWriteZeroes;
29
-
31
-
30
-/**
32
-static void coroutine_fn co_pwrite_zeroes_entry(void *opaque)
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
59
index XXXXXXX..XXXXXXX 100644
60
--- a/blockdev.c
61
+++ b/blockdev.c
62
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
63
void qmp_block_job_dismiss(const char *id, Error **errp)
64
{
65
AioContext *aio_context;
66
- BlockJob *job = find_block_job(id, &aio_context, errp);
67
+ BlockJob *bjob = find_block_job(id, &aio_context, errp);
68
+ Job *job;
69
70
- if (!job) {
71
+ if (!bjob) {
72
return;
73
}
74
75
- trace_qmp_block_job_dismiss(job);
76
- block_job_dismiss(&job, errp);
77
+ trace_qmp_block_job_dismiss(bjob);
78
+ job = &bjob->job;
79
+ job_dismiss(&job, errp);
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
-{
33
-{
93
- BlockJob *job = *jobptr;
34
- CoWriteZeroes *data = opaque;
94
- /* similarly to _complete, this is QMP-interface only. */
35
-
95
- assert(job->job.id);
36
- data->ret = blk_co_pwrite_zeroes(data->blk, data->offset, data->bytes,
96
- if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
37
- data->flags);
38
- data->done = true;
39
- if (data->ret < 0) {
40
- *data->total = data->ret;
97
- return;
41
- return;
98
- }
42
- }
99
-
43
-
100
- job_do_dismiss(&job->job);
44
- *data->total = data->bytes;
101
- *jobptr = NULL;
102
-}
45
-}
103
-
46
-
104
void block_job_progress_update(BlockJob *job, uint64_t done)
47
-static int do_co_pwrite_zeroes(BlockBackend *blk, int64_t offset,
48
+static int do_pwrite_zeroes(BlockBackend *blk, int64_t offset,
49
int64_t bytes, int flags, int64_t *total)
105
{
50
{
106
job->offset += done;
51
- Coroutine *co;
107
diff --git a/job.c b/job.c
52
- CoWriteZeroes data = {
108
index XXXXXXX..XXXXXXX 100644
53
- .blk = blk,
109
--- a/job.c
54
- .offset = offset,
110
+++ b/job.c
55
- .bytes = bytes,
111
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
56
- .total = total,
112
job_resume(job);
57
- .flags = flags,
58
- .done = false,
59
- };
60
-
61
- co = qemu_coroutine_create(co_pwrite_zeroes_entry, &data);
62
- bdrv_coroutine_enter(blk_bs(blk), co);
63
- while (!data.done) {
64
- aio_poll(blk_get_aio_context(blk), true);
65
- }
66
- if (data.ret < 0) {
67
- return data.ret;
68
- } else {
69
- return 1;
70
+ int ret = blk_pwrite_zeroes(blk, offset, bytes,
71
+ flags | BDRV_REQ_ZERO_WRITE);
72
+
73
+ if (ret < 0) {
74
+ return ret;
75
}
76
+ *total = bytes;
77
+ return 1;
113
}
78
}
114
79
115
-void job_do_dismiss(Job *job)
80
static int do_write_compressed(BlockBackend *blk, char *buf, int64_t offset,
116
+static void job_do_dismiss(Job *job)
81
@@ -XXX,XX +XXX,XX @@ static void write_help(void)
117
{
82
" -C, -- report statistics in a machine parsable format\n"
118
assert(job);
83
" -q, -- quiet mode, do not show I/O statistics\n"
119
job->busy = false;
84
" -u, -- with -z, allow unmapping\n"
120
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
85
-" -z, -- write zeroes using blk_co_pwrite_zeroes\n"
121
job_unref(job);
86
+" -z, -- write zeroes using blk_pwrite_zeroes\n"
87
"\n");
122
}
88
}
123
89
124
+void job_dismiss(Job **jobptr, Error **errp)
90
@@ -XXX,XX +XXX,XX @@ static int write_f(BlockBackend *blk, int argc, char **argv)
125
+{
91
if (bflag) {
126
+ Job *job = *jobptr;
92
ret = do_save_vmstate(blk, buf, offset, count, &total);
127
+ /* similarly to _complete, this is QMP-interface only. */
93
} else if (zflag) {
128
+ assert(job->id);
94
- ret = do_co_pwrite_zeroes(blk, offset, count, flags, &total);
129
+ if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) {
95
+ ret = do_pwrite_zeroes(blk, offset, count, flags, &total);
130
+ return;
96
} else if (cflag) {
131
+ }
97
ret = do_write_compressed(blk, buf, offset, count, &total);
132
+
98
} else {
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
--
99
--
156
2.13.6
100
2.38.1
157
158
diff view generated by jsdifflib
1
While we already moved the state related to job pausing to Job, the
1
From: Paolo Bonzini <pbonzini@redhat.com>
2
functions to do were still BlockJob only. This commit moves them over to
3
Job.
4
2
3
It has only one caller---inline it and remove the function.
4
5
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
6
Message-Id: <20221215130225.476477-2-pbonzini@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
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
---
9
include/block/blockjob.h | 32 -----------------
10
include/block/block-io.h | 5 -----
10
include/block/blockjob_int.h | 7 ++++
11
block.c | 6 ------
11
include/qemu/job.h | 37 ++++++++++++++++++++
12
block/block-backend.c | 2 +-
12
block/backup.c | 1 +
13
3 files changed, 1 insertion(+), 12 deletions(-)
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
14
24
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
15
diff --git a/include/block/block-io.h b/include/block/block-io.h
25
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/blockjob.h
17
--- a/include/block/block-io.h
27
+++ b/include/block/blockjob.h
18
+++ b/include/block/block-io.h
28
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
19
@@ -XXX,XX +XXX,XX @@ AioContext *coroutine_fn bdrv_co_enter(BlockDriverState *bs);
29
bool force;
20
*/
30
21
void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx);
31
/**
22
32
- * Set to true if the job is paused by user. Can be unpaused with the
23
-/**
33
- * block-job-resume QMP command.
24
- * Transfer control to @co in the aio context of @bs
34
- */
25
- */
35
- bool user_paused;
26
-void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co);
36
-
27
-
37
- /**
28
AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
38
* Set to true when the job is ready to be completed.
29
39
*/
30
void bdrv_io_plug(BlockDriverState *bs);
40
bool ready;
31
diff --git a/block.c b/block.c
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
32
index XXXXXXX..XXXXXXX 100644
76
--- a/include/block/blockjob_int.h
33
--- a/block.c
77
+++ b/include/block/blockjob_int.h
34
+++ b/block.c
78
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
35
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_unlock(BlockDriverState *bs)
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
}
36
}
247
}
37
}
248
38
249
-/* Assumes the job_mutex is held */
39
-void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co)
250
-static bool job_timer_not_pending(Job *job)
251
-{
40
-{
252
- return !timer_pending(&job->sleep_timer);
41
- IO_CODE();
42
- aio_co_enter(bdrv_get_aio_context(bs), co);
253
-}
43
-}
254
-
44
-
255
-static void block_job_pause(BlockJob *job)
45
static void bdrv_do_remove_aio_context_notifier(BdrvAioNotifier *ban)
256
-{
257
- job->job.pause_count++;
258
-}
259
-
260
-static void block_job_resume(BlockJob *job)
261
-{
262
- assert(job->job.pause_count > 0);
263
- job->job.pause_count--;
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
}
278
279
- block_job_resume(job);
280
+ job_resume(&job->job);
281
}
282
283
static void block_job_drain(BlockJob *job)
284
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
285
/* In case the job terminates during aio_poll()... */
286
job_ref(&job->job);
287
288
- block_job_pause(job);
289
+ job_pause(&job->job);
290
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
{
46
{
296
BlockJob *job = c->opaque;
47
GLOBAL_STATE_CODE();
297
- block_job_pause(job);
48
diff --git a/block/block-backend.c b/block/block-backend.c
298
+ job_pause(&job->job);
299
}
300
301
static void child_job_drained_end(BdrvChild *c)
302
{
303
BlockJob *job = c->opaque;
304
- block_job_resume(job);
305
+ job_resume(&job->job);
306
}
307
308
static const BdrvChildRole child_job = {
309
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
310
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
311
block_job_iostatus_reset(job);
312
}
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
49
index XXXXXXX..XXXXXXX 100644
401
--- a/job.c
50
--- a/block/block-backend.c
402
+++ b/job.c
51
+++ b/block/block-backend.c
403
@@ -XXX,XX +XXX,XX @@ void job_start(Job *job)
52
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *blk_aio_prwv(BlockBackend *blk, int64_t offset,
404
aio_co_enter(job->aio_context, job->co);
53
acb->has_returned = false;
405
}
54
406
55
co = qemu_coroutine_create(co_entry, acb);
407
+/* Assumes the block_job_mutex is held */
56
- bdrv_coroutine_enter(blk_bs(blk), co);
408
+static bool job_timer_not_pending(Job *job)
57
+ aio_co_enter(blk_get_aio_context(blk), co);
409
+{
58
410
+ return !timer_pending(&job->sleep_timer);
59
acb->has_returned = true;
411
+}
60
if (acb->rwco.ret != NOT_DONE) {
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
531
--
61
--
532
2.13.6
62
2.38.1
533
534
diff view generated by jsdifflib
1
This moves the .complete callback that tells a READY job to complete
1
In order to write the bitmap table to the image file, it is converted to
2
from BlockJobDriver to JobDriver. The wrapper function job_complete()
2
big endian. If the write fails, it is passed to clear_bitmap_table() to
3
doesn't require anything block job specific any more and can be moved
3
free all of the clusters it had allocated before. However, if we don't
4
to Job.
4
convert it back to native endianness first, we'll free things at a wrong
5
offset.
5
6
7
In practical terms, the offsets will be so high that we won't actually
8
free any allocated clusters, but just run into an error, but in theory
9
this can cause image corruption.
10
11
Cc: qemu-stable@nongnu.org
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20230112191454.169353-2-kwolf@redhat.com>
14
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
15
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
17
---
9
include/block/blockjob.h | 10 ----------
18
block/qcow2-bitmap.c | 5 +++--
10
include/block/blockjob_int.h | 6 ------
19
1 file changed, 3 insertions(+), 2 deletions(-)
11
include/qemu/job.h | 8 ++++++++
12
block/mirror.c | 10 +++++-----
13
blockdev.c | 2 +-
14
blockjob.c | 23 +++++------------------
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
20
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
21
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
21
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/blockjob.h
23
--- a/block/qcow2-bitmap.c
23
+++ b/include/block/blockjob.h
24
+++ b/block/qcow2-bitmap.c
24
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
25
@@ -XXX,XX +XXX,XX @@ static int update_header_sync(BlockDriverState *bs)
25
void block_job_cancel(BlockJob *job, bool force);
26
return bdrv_flush(bs->file->bs);
26
27
/**
28
- * block_job_complete:
29
- * @job: The job to be completed.
30
- * @errp: Error object.
31
- *
32
- * Asynchronously complete the specified job.
33
- */
34
-void block_job_complete(BlockJob *job, Error **errp);
35
-
36
-
37
-/**
38
* block_job_finalize:
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
43
--- a/include/block/blockjob_int.h
44
+++ b/include/block/blockjob_int.h
45
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
46
JobDriver job_driver;
47
48
/**
49
- * Optional callback for job types whose completion must be triggered
50
- * manually.
51
- */
52
- void (*complete)(BlockJob *job, Error **errp);
53
-
54
- /**
55
* If the callback is not NULL, prepare will be invoked when all the jobs
56
* belonging to the same transaction complete; or upon this job's completion
57
* if it is not in a transaction.
58
diff --git a/include/qemu/job.h b/include/qemu/job.h
59
index XXXXXXX..XXXXXXX 100644
60
--- a/include/qemu/job.h
61
+++ b/include/qemu/job.h
62
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
63
*/
64
void (*user_resume)(Job *job);
65
66
+ /**
67
+ * Optional callback for job types whose completion must be triggered
68
+ * manually.
69
+ */
70
+ void (*complete)(Job *job, Error **errp);
71
+
72
/*
73
* If the callback is not NULL, it will be invoked when the job has to be
74
* synchronously cancelled or completed; it should drain any activities
75
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
76
/** The @job could not be started, free it. */
77
void job_early_fail(Job *job);
78
79
+/** Asynchronously complete the specified @job. */
80
+void job_complete(Job *job, Error **errp);;
81
82
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
83
84
diff --git a/block/mirror.c b/block/mirror.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/mirror.c
87
+++ b/block/mirror.c
88
@@ -XXX,XX +XXX,XX @@ immediate_exit:
89
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
90
}
27
}
91
28
92
-static void mirror_complete(BlockJob *job, Error **errp)
29
-static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
93
+static void mirror_complete(Job *job, Error **errp)
30
+static inline void bitmap_table_bswap_be(uint64_t *bitmap_table, size_t size)
94
{
31
{
95
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
32
size_t i;
96
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
33
97
BlockDriverState *target;
34
@@ -XXX,XX +XXX,XX @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp)
98
35
goto fail;
99
target = blk_bs(s->target);
100
101
if (!s->synced) {
102
error_setg(errp, "The active block job '%s' cannot be completed",
103
- job->job.id);
104
+ job->id);
105
return;
106
}
36
}
107
37
108
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
38
- bitmap_table_to_be(tb, tb_size);
109
.drain = block_job_drain,
39
+ bitmap_table_bswap_be(tb, tb_size);
110
.start = mirror_run,
40
ret = bdrv_pwrite(bs->file, tb_offset, tb_size * sizeof(tb[0]), tb, 0);
111
.pause = mirror_pause,
41
if (ret < 0) {
112
+ .complete = mirror_complete,
42
+ bitmap_table_bswap_be(tb, tb_size);
113
},
43
error_setg_errno(errp, -ret, "Failed to write bitmap '%s' to file",
114
- .complete = mirror_complete,
44
bm_name);
115
.attached_aio_context = mirror_attached_aio_context,
45
goto fail;
116
.drain = mirror_drain,
117
};
118
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
119
.drain = block_job_drain,
120
.start = mirror_run,
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
+{
176
+ job_complete(&job->job, errp);
177
+}
178
+
179
int block_job_complete_sync(BlockJob *job, Error **errp)
180
{
181
return block_job_finish_sync(job, &block_job_complete, errp);
182
diff --git a/job.c b/job.c
183
index XXXXXXX..XXXXXXX 100644
184
--- a/job.c
185
+++ b/job.c
186
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
187
return 0;
188
}
189
190
+void job_complete(Job *job, Error **errp)
191
+{
192
+ /* Should not be reachable via external interface for internal jobs */
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
+}
205
+
206
207
typedef struct {
208
Job *job;
209
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
210
index XXXXXXX..XXXXXXX 100644
211
--- a/tests/test-bdrv-drain.c
212
+++ b/tests/test-bdrv-drain.c
213
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
214
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
215
}
216
217
-static void test_job_complete(BlockJob *job, Error **errp)
218
+static void test_job_complete(Job *job, Error **errp)
219
{
220
- TestBlockJob *s = container_of(job, TestBlockJob, common);
221
+ TestBlockJob *s = container_of(job, TestBlockJob, common.job);
222
s->should_complete = true;
223
}
224
225
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
226
.user_resume = block_job_user_resume,
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
--
46
--
280
2.13.6
47
2.38.1
281
48
282
49
diff view generated by jsdifflib
1
This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL
1
blk_unref() can't report any errors that happen while closing the image.
2
checks to job_create() and the auto_{finalize,dismiss} fields from
2
For example, if qcow2 hits an -ENOSPC error while writing out dirty
3
BlockJob to Job.
3
bitmaps when it's closed, it prints error messages to stderr, but
4
'qemu-img commit' won't see any error return value and will therefore
5
look successful with exit code 0.
6
7
In order to fix this, manually inactivate the image first before calling
8
blk_unref(). This already performs the operations that would be most
9
likely to fail while closing the image, but it can still return errors.
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>
12
Message-Id: <20230112191454.169353-3-kwolf@redhat.com>
13
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
15
---
8
include/block/blockjob.h | 17 -----------------
16
qemu-img.c | 13 +++++++++++++
9
include/block/blockjob_int.h | 3 +--
17
1 file changed, 13 insertions(+)
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
18
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
118
index XXXXXXX..XXXXXXX 100644
119
--- a/block/commit.c
120
+++ b/block/commit.c
121
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
122
}
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
{
242
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
243
- if (job->auto_dismiss || !job_started(&job->job)) {
244
+ if (job->job.auto_dismiss || !job_started(&job->job)) {
245
block_job_do_dismiss(job);
246
}
247
}
248
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
249
250
static int block_job_needs_finalize(BlockJob *job)
251
{
252
- return !job->auto_finalize;
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
- }
289
-
290
- if (job_id) {
291
- if (flags & BLOCK_JOB_INTERNAL) {
292
- error_setg(errp, "Cannot specify job ID for internal block job");
293
- return NULL;
294
- }
295
}
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
19
diff --git a/qemu-img.c b/qemu-img.c
357
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
358
--- a/qemu-img.c
21
--- a/qemu-img.c
359
+++ b/qemu-img.c
22
+++ b/qemu-img.c
360
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
23
@@ -XXX,XX +XXX,XX @@ static BlockBackend *img_open(bool image_opts,
361
24
blk = img_open_file(filename, NULL, fmt, flags, writethrough, quiet,
362
aio_context = bdrv_get_aio_context(bs);
25
force_share);
363
aio_context_acquire(aio_context);
26
}
364
- commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
27
+
365
+ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
28
+ if (blk) {
366
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
29
+ blk_set_force_allow_inactivate(blk);
367
&cbi, false, &local_err);
30
+ }
368
aio_context_release(aio_context);
31
+
369
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
32
return blk;
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
}
33
}
393
34
394
/* This creates a BlockBackend (optionally with a name) with a
35
@@ -XXX,XX +XXX,XX @@ unref_backing:
395
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
36
done:
396
37
qemu_progress_end();
397
blk = create_blk(NULL);
38
398
job = mk_job(blk, "Steve", &test_cancel_driver, true,
39
+ /*
399
- BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
40
+ * Manually inactivate the image first because this way we can know whether
400
+ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
41
+ * an error occurred. blk_unref() doesn't tell us about failures.
401
job_ref(&job->job);
42
+ */
402
assert(job->job.status == JOB_STATUS_CREATED);
43
+ ret = bdrv_inactivate_all();
403
s = container_of(job, CancelJob, common);
44
+ if (ret < 0 && !local_err) {
45
+ error_setg_errno(&local_err, -ret, "Error while closing the image");
46
+ }
47
blk_unref(blk);
48
49
if (local_err) {
404
--
50
--
405
2.13.6
51
2.38.1
406
407
diff view generated by jsdifflib
1
Instead of having a 'bool ready' in BlockJob, add a function that
1
blk_unref() can't report any errors that happen while closing the image.
2
derives its value from the job status.
2
For example, if qcow2 hits an -ENOSPC error while writing out dirty
3
bitmaps when it's closed, it prints error messages to stderr, but
4
'qemu-img bitmap' won't see any error return value and will therefore
5
look successful with exit code 0.
3
6
4
At the same time, this fixes the behaviour to match what the QAPI
7
In order to fix this, manually inactivate the image first before calling
5
documentation promises for query-block-job: 'true if the job may be
8
blk_unref(). This already performs the operations that would be most
6
completed'. When the ready flag was introduced in commit ef6dbf1e46e,
9
likely to fail while closing the image, but it can still return errors.
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
10
Job transactions and manual job finalisation were introduced only later.
11
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1330
11
With these changes, jobs may stay around even after having completed
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
(and they are not ready to be completed a second time), however their
13
Message-Id: <20230112191454.169353-4-kwolf@redhat.com>
13
patches forgot to reset the ready flag.
14
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
15
Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
18
qemu-img.c | 11 +++++++++++
19
1 file changed, 11 insertions(+)
14
20
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
17
---
18
include/block/blockjob.h | 5 -----
19
include/qemu/job.h | 3 +++
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
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 @@ typedef struct BlockJob {
31
/** The block device on which the job is operating. */
32
BlockBackend *blk;
33
34
- /**
35
- * Set to true when the job is ready to be completed.
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
}
84
85
+bool job_is_ready(Job *job)
86
+{
87
+ switch (job->status) {
88
+ case JOB_STATUS_UNDEFINED:
89
+ case JOB_STATUS_CREATED:
90
+ case JOB_STATUS_RUNNING:
91
+ case JOB_STATUS_PAUSED:
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
21
diff --git a/qemu-img.c b/qemu-img.c
111
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
112
--- a/qemu-img.c
23
--- a/qemu-img.c
113
+++ b/qemu-img.c
24
+++ b/qemu-img.c
114
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
25
@@ -XXX,XX +XXX,XX @@ static int img_bitmap(int argc, char **argv)
115
aio_poll(aio_context, true);
26
QSIMPLEQ_HEAD(, ImgBitmapAction) actions;
116
qemu_progress_print(job->len ?
27
ImgBitmapAction *act, *act_next;
117
((float)job->offset / job->len * 100.f) : 0.0f, 0);
28
const char *op;
118
- } while (!job->ready && !job_is_completed(&job->job));
29
+ int inactivate_ret;
119
+ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
30
120
31
QSIMPLEQ_INIT(&actions);
121
if (!job_is_completed(&job->job)) {
32
122
ret = job_complete_sync(&job->job, errp);
33
@@ -XXX,XX +XXX,XX @@ static int img_bitmap(int argc, char **argv)
123
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
34
ret = 0;
124
index XXXXXXX..XXXXXXX 100644
35
125
--- a/tests/test-blockjob.c
36
out:
126
+++ b/tests/test-blockjob.c
37
+ /*
127
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
38
+ * Manually inactivate the images first because this way we can know whether
128
goto defer;
39
+ * an error occurred. blk_unref() doesn't tell us about failures.
129
}
40
+ */
130
41
+ inactivate_ret = bdrv_inactivate_all();
131
- if (!s->common.ready && s->should_converge) {
42
+ if (inactivate_ret < 0) {
132
+ if (!job_is_ready(&s->common.job) && s->should_converge) {
43
+ error_report("Error while closing the image: %s", strerror(-inactivate_ret));
133
block_job_event_ready(&s->common);
44
+ ret = 1;
134
}
45
+ }
135
46
+
47
blk_unref(src);
48
blk_unref(blk);
49
qemu_opts_del(opts);
136
--
50
--
137
2.13.6
51
2.38.1
138
52
139
53
diff view generated by jsdifflib
1
This adds a test case that tests the new job-* QMP commands with
1
This tests that when an error happens while writing back bitmaps to the
2
mirror and backup block jobs.
2
image file in qcow2_inactivate(), 'qemu-img bitmap/commit' actually
3
return an error value in their exit code instead of making the operation
4
look successful to scripts.
3
5
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Message-Id: <20230112191454.169353-5-kwolf@redhat.com>
8
Reviewed-by: Hanna Czenczek <hreitz@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
tests/qemu-iotests/219 | 209 +++++++++++++++++++++++++++++
11
.../qemu-iotests/tests/qemu-img-close-errors | 95 +++++++++++++++++++
7
tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++++++++++
12
.../tests/qemu-img-close-errors.out | 23 +++++
8
tests/qemu-iotests/group | 1 +
13
2 files changed, 118 insertions(+)
9
3 files changed, 537 insertions(+)
14
create mode 100755 tests/qemu-iotests/tests/qemu-img-close-errors
10
create mode 100755 tests/qemu-iotests/219
15
create mode 100644 tests/qemu-iotests/tests/qemu-img-close-errors.out
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/tests/qemu-iotests/tests/qemu-img-close-errors b/tests/qemu-iotests/tests/qemu-img-close-errors
14
new file mode 100755
18
new file mode 100755
15
index XXXXXXX..XXXXXXX
19
index XXXXXXX..XXXXXXX
16
--- /dev/null
20
--- /dev/null
17
+++ b/tests/qemu-iotests/219
21
+++ b/tests/qemu-iotests/tests/qemu-img-close-errors
18
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@
19
+#!/usr/bin/env python
23
+#!/usr/bin/env bash
24
+# group: rw auto quick
20
+#
25
+#
21
+# Copyright (C) 2018 Red Hat, Inc.
26
+# Check that errors while closing the image, in particular writing back dirty
27
+# bitmaps, is correctly reported with a failing qemu-img exit code.
28
+#
29
+# Copyright (C) 2023 Red Hat, Inc.
22
+#
30
+#
23
+# This program is free software; you can redistribute it and/or modify
31
+# This program is free software; you can redistribute it and/or modify
24
+# it under the terms of the GNU General Public License as published by
32
+# it under the terms of the GNU General Public License as published by
25
+# the Free Software Foundation; either version 2 of the License, or
33
+# the Free Software Foundation; either version 2 of the License, or
26
+# (at your option) any later version.
34
+# (at your option) any later version.
...
...
31
+# GNU General Public License for more details.
39
+# GNU General Public License for more details.
32
+#
40
+#
33
+# You should have received a copy of the GNU General Public License
41
+# You should have received a copy of the GNU General Public License
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
42
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
43
+#
36
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
37
+#
38
+# Check using the job-* QMP commands with block jobs
39
+
44
+
40
+import iotests
45
+# creator
46
+owner=kwolf@redhat.com
41
+
47
+
42
+iotests.verify_image_format(supported_fmts=['qcow2'])
48
+seq="$(basename $0)"
49
+echo "QA output created by $seq"
43
+
50
+
44
+def pause_wait(vm, job_id):
51
+status=1    # failure is the default!
45
+ with iotests.Timeout(3, "Timeout waiting for job to pause"):
46
+ while True:
47
+ result = vm.qmp('query-jobs')
48
+ for job in result['return']:
49
+ if job['id'] == job_id and job['status'] in ['paused', 'standby']:
50
+ return job
51
+
52
+
52
+# Test that block-job-pause/resume and job-pause/resume can be mixed
53
+_cleanup()
53
+def test_pause_resume(vm):
54
+{
54
+ for pause_cmd, pause_arg in [('block-job-pause', 'device'),
55
+ _cleanup_test_img
55
+ ('job-pause', 'id')]:
56
+}
56
+ for resume_cmd, resume_arg in [('block-job-resume', 'device'),
57
+trap "_cleanup; exit \$status" 0 1 2 3 15
57
+ ('job-resume', 'id')]:
58
+ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
59
+
58
+
60
+ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
59
+# get standard environment, filters and checks
61
+ pause_wait(vm, 'job0')
60
+cd ..
62
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
61
+. ./common.rc
63
+ iotests.log(vm.qmp('query-jobs'))
62
+. ./common.filter
64
+
63
+
65
+ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
64
+_supported_fmt qcow2
66
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
65
+_supported_proto generic
67
+ iotests.log(vm.qmp('query-jobs'))
68
+
66
+
69
+def test_job_lifecycle(vm, job, job_args, has_ready=False):
67
+size=1G
70
+ iotests.log('')
71
+ iotests.log('')
72
+ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
73
+ (job,
74
+ job_args.get('auto-finalize', True),
75
+ job_args.get('auto-dismiss', True)))
76
+ iotests.log(vm.qmp(job, job_id='job0', **job_args))
77
+
68
+
78
+ # Depending on the storage, the first request may or may not have completed
69
+# The error we are going to use is ENOSPC. Depending on how many bitmaps we
79
+ # yet, so filter out the progress. Later query-job calls don't need the
70
+# create in the backing file (and therefore increase the used up space), we get
80
+ # filtering because the progress is made deterministic by the block job
71
+# failures in different places. With a low number, only merging the bitmap
81
+ # speed
72
+# fails, whereas with a higher number, already 'qemu-img commit' fails.
82
+ result = vm.qmp('query-jobs')
73
+for max_bitmap in 6 7; do
83
+ for j in result['return']:
74
+ echo
84
+ del j['current-progress']
75
+ echo "=== Test with $max_bitmap bitmaps ==="
85
+ iotests.log(result)
86
+
76
+
87
+ # undefined -> created -> running
77
+ TEST_IMG="$TEST_IMG.base" _make_test_img -q $size
88
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
78
+ for i in $(seq 1 $max_bitmap); do
89
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
79
+ $QEMU_IMG bitmap --add "$TEST_IMG.base" "stale-bitmap-$i"
80
+ done
90
+
81
+
91
+ # RUNNING state:
82
+ # Simulate a block device of 128 MB by resizing the image file accordingly
92
+ # pause/resume should work, complete/finalize/dismiss should error out
83
+ # and then enforcing the size with the raw driver
93
+ iotests.log('')
84
+ truncate "$TEST_IMG.base" --size 128M
94
+ iotests.log('Pause/resume in RUNNING')
85
+ BASE_JSON='json:{
95
+ test_pause_resume(vm)
86
+ "driver": "qcow2",
87
+ "file": {
88
+ "driver": "raw",
89
+ "size": 134217728,
90
+ "file": {
91
+ "driver": "file",
92
+ "filename":"'"$TEST_IMG.base"'"
93
+ }
94
+ }
95
+ }'
96
+
96
+
97
+ iotests.log(vm.qmp('job-complete', id='job0'))
97
+ _make_test_img -q -b "$BASE_JSON" -F $IMGFMT
98
+ iotests.log(vm.qmp('job-finalize', id='job0'))
98
+ $QEMU_IMG bitmap --add "$TEST_IMG" "good-bitmap"
99
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
100
+
99
+
101
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
100
+ $QEMU_IO -c 'write 0 126m' "$TEST_IMG" | _filter_qemu_io
102
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
103
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
104
+
101
+
105
+ # Let the job complete (or transition to READY if it supports that)
102
+ $QEMU_IMG commit -d "$TEST_IMG" 2>&1 | _filter_generated_node_ids
106
+ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
103
+ echo "qemu-img commit exit code: ${PIPESTATUS[0]}"
107
+ if has_ready:
108
+ iotests.log('')
109
+ iotests.log('Waiting for READY state...')
110
+ vm.event_wait('BLOCK_JOB_READY')
111
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
112
+ iotests.log(vm.qmp('query-jobs'))
113
+
104
+
114
+ # READY state:
105
+ $QEMU_IMG bitmap --add "$BASE_JSON" "good-bitmap"
115
+ # pause/resume/complete should work, finalize/dismiss should error out
106
+ echo "qemu-img bitmap --add exit code: $?"
116
+ iotests.log('')
117
+ iotests.log('Pause/resume in READY')
118
+ test_pause_resume(vm)
119
+
107
+
120
+ iotests.log(vm.qmp('job-finalize', id='job0'))
108
+ $QEMU_IMG bitmap --merge "good-bitmap" -b "$TEST_IMG" "$BASE_JSON" \
121
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
109
+ "good-bitmap" 2>&1 | _filter_generated_node_ids
110
+ echo "qemu-img bitmap --merge exit code: ${PIPESTATUS[0]}"
111
+done
122
+
112
+
123
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
113
+# success, all done
124
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
114
+echo "*** done"
115
+rm -f $seq.full
116
+status=0
125
+
117
+
126
+ # Transition to WAITING
118
diff --git a/tests/qemu-iotests/tests/qemu-img-close-errors.out b/tests/qemu-iotests/tests/qemu-img-close-errors.out
127
+ iotests.log(vm.qmp('job-complete', id='job0'))
128
+
129
+ # Move to WAITING and PENDING state
130
+ iotests.log('')
131
+ iotests.log('Waiting for PENDING state...')
132
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
133
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
134
+
135
+ if not job_args.get('auto-finalize', True):
136
+ # PENDING state:
137
+ # finalize should work, pause/complete/dismiss should error out
138
+ iotests.log(vm.qmp('query-jobs'))
139
+
140
+ iotests.log(vm.qmp('job-pause', id='job0'))
141
+ iotests.log(vm.qmp('job-complete', id='job0'))
142
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
143
+
144
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
145
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
146
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
147
+
148
+ # Transition to CONCLUDED
149
+ iotests.log(vm.qmp('job-finalize', id='job0'))
150
+
151
+
152
+ # Move to CONCLUDED state
153
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
154
+
155
+ if not job_args.get('auto-dismiss', True):
156
+ # CONCLUDED state:
157
+ # dismiss should work, pause/complete/finalize should error out
158
+ iotests.log(vm.qmp('query-jobs'))
159
+
160
+ iotests.log(vm.qmp('job-pause', id='job0'))
161
+ iotests.log(vm.qmp('job-complete', id='job0'))
162
+ iotests.log(vm.qmp('job-finalize', id='job0'))
163
+
164
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
165
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
166
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
167
+
168
+ # Transition to NULL
169
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
170
+
171
+ # Move to NULL state
172
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
173
+ iotests.log(vm.qmp('query-jobs'))
174
+
175
+
176
+with iotests.FilePath('disk.img') as disk_path, \
177
+ iotests.FilePath('copy.img') as copy_path, \
178
+ iotests.VM() as vm:
179
+
180
+ img_size = '4M'
181
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
182
+ iotests.qemu_io('-c', 'write 0 %s' % (img_size),
183
+ '-f', iotests.imgfmt, disk_path)
184
+
185
+ iotests.log('Launching VM...')
186
+ vm.add_blockdev(vm.qmp_to_opts({
187
+ 'driver': iotests.imgfmt,
188
+ 'node-name': 'drive0-node',
189
+ 'file': {
190
+ 'driver': 'file',
191
+ 'filename': disk_path,
192
+ },
193
+ }))
194
+ vm.launch()
195
+
196
+ # In order to keep things deterministic (especially progress in query-job,
197
+ # but related to this also automatic state transitions like job
198
+ # completion), but still get pause points often enough to avoid making this
199
+ # test very slow, it's important to have the right ratio between speed and
200
+ # buf_size.
201
+ #
202
+ # For backup, buf_size is hard-coded to the source image cluster size (64k),
203
+ # so we'll pick the same for mirror. The slice time, i.e. the granularity
204
+ # of the rate limiting is 100ms. With a speed of 256k per second, we can
205
+ # get four pause points per second. This gives us 250ms per iteration,
206
+ # which should be enough to stay deterministic.
207
+
208
+ test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
209
+ 'device': 'drive0-node',
210
+ 'target': copy_path,
211
+ 'sync': 'full',
212
+ 'speed': 262144,
213
+ 'buf_size': 65536,
214
+ })
215
+
216
+ for auto_finalize in [True, False]:
217
+ for auto_dismiss in [True, False]:
218
+ test_job_lifecycle(vm, 'drive-backup', job_args={
219
+ 'device': 'drive0-node',
220
+ 'target': copy_path,
221
+ 'sync': 'full',
222
+ 'speed': 262144,
223
+ 'auto-finalize': auto_finalize,
224
+ 'auto-dismiss': auto_dismiss,
225
+ })
226
+
227
+ vm.shutdown()
228
diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out
229
new file mode 100644
119
new file mode 100644
230
index XXXXXXX..XXXXXXX
120
index XXXXXXX..XXXXXXX
231
--- /dev/null
121
--- /dev/null
232
+++ b/tests/qemu-iotests/219.out
122
+++ b/tests/qemu-iotests/tests/qemu-img-close-errors.out
233
@@ -XXX,XX +XXX,XX @@
123
@@ -XXX,XX +XXX,XX @@
234
+Launching VM...
124
+QA output created by qemu-img-close-errors
235
+
125
+
126
+=== Test with 6 bitmaps ===
127
+wrote 132120576/132120576 bytes at offset 0
128
+126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
129
+Image committed.
130
+qemu-img commit exit code: 0
131
+qemu-img bitmap --add exit code: 0
132
+qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device
133
+qemu-img: Error while closing the image: Invalid argument
134
+qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'good-bitmap' to file: No space left on device
135
+qemu-img bitmap --merge exit code: 1
236
+
136
+
237
+Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
137
+=== Test with 7 bitmaps ===
238
+{u'return': {}}
138
+wrote 132120576/132120576 bytes at offset 0
239
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
139
+126 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
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'}
140
+qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device
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'}
141
+qemu-img: Lost persistent bitmaps during inactivation of node 'NODE_NAME': Failed to write bitmap 'stale-bitmap-7' to file: No space left on device
242
+
142
+qemu-img: Error while closing the image: Invalid argument
243
+Pause/resume in RUNNING
143
+qemu-img commit exit code: 1
244
+=== Testing block-job-pause/block-job-resume ===
144
+qemu-img bitmap --add exit code: 0
245
+{u'return': {}}
145
+qemu-img bitmap --merge exit code: 0
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'}
146
+*** done
247
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
248
+{u'return': {}}
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'}
250
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
251
+=== Testing block-job-pause/job-resume ===
252
+{u'return': {}}
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'}
254
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
255
+{u'return': {}}
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'}
257
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
258
+=== Testing job-pause/block-job-resume ===
259
+{u'return': {}}
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'}
261
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
262
+{u'return': {}}
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'}
264
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
265
+=== Testing job-pause/job-resume ===
266
+{u'return': {}}
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'}
268
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
269
+{u'return': {}}
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'}
271
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
272
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
273
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
274
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
275
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
276
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
277
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
278
+{u'return': {}}
279
+
280
+Waiting for READY state...
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'}
282
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
283
+
284
+Pause/resume in READY
285
+=== Testing block-job-pause/block-job-resume ===
286
+{u'return': {}}
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'}
288
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
289
+{u'return': {}}
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'}
291
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
292
+=== Testing block-job-pause/job-resume ===
293
+{u'return': {}}
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'}
295
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
296
+{u'return': {}}
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'}
298
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
299
+=== Testing job-pause/block-job-resume ===
300
+{u'return': {}}
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'}
302
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
303
+{u'return': {}}
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'}
305
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
306
+=== Testing job-pause/job-resume ===
307
+{u'return': {}}
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'}
309
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
310
+{u'return': {}}
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'}
312
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
313
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
314
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
315
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
316
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
317
+{u'return': {}}
318
+
319
+Waiting for PENDING state...
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'}
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'}
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'}
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'}
324
+{u'return': []}
325
+
326
+
327
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True)
328
+{u'return': {}}
329
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
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'}
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'}
332
+
333
+Pause/resume in RUNNING
334
+=== Testing block-job-pause/block-job-resume ===
335
+{u'return': {}}
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'}
337
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
338
+{u'return': {}}
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'}
340
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
341
+=== Testing block-job-pause/job-resume ===
342
+{u'return': {}}
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'}
344
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
345
+{u'return': {}}
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'}
347
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
348
+=== Testing job-pause/block-job-resume ===
349
+{u'return': {}}
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'}
351
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
352
+{u'return': {}}
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'}
354
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
355
+=== Testing job-pause/job-resume ===
356
+{u'return': {}}
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'}
358
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
359
+{u'return': {}}
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'}
361
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
362
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
363
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
364
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
365
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
366
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
367
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
368
+{u'return': {}}
369
+
370
+Waiting for PENDING state...
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'}
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'}
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'}
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'}
375
+{u'return': []}
376
+
377
+
378
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False)
379
+{u'return': {}}
380
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
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'}
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'}
383
+
384
+Pause/resume in RUNNING
385
+=== Testing block-job-pause/block-job-resume ===
386
+{u'return': {}}
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'}
388
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
389
+{u'return': {}}
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'}
391
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
392
+=== Testing block-job-pause/job-resume ===
393
+{u'return': {}}
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'}
395
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
396
+{u'return': {}}
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'}
398
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
399
+=== Testing job-pause/block-job-resume ===
400
+{u'return': {}}
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'}
402
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
403
+{u'return': {}}
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'}
405
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
406
+=== Testing job-pause/job-resume ===
407
+{u'return': {}}
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'}
409
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
410
+{u'return': {}}
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'}
412
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
413
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
414
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
415
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
416
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
417
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
418
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
419
+{u'return': {}}
420
+
421
+Waiting for PENDING state...
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'}
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'}
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'}
425
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
426
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
427
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
428
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
429
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
430
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
431
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
432
+{u'return': {}}
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'}
434
+{u'return': []}
435
+
436
+
437
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True)
438
+{u'return': {}}
439
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
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'}
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'}
442
+
443
+Pause/resume in RUNNING
444
+=== Testing block-job-pause/block-job-resume ===
445
+{u'return': {}}
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'}
447
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
448
+{u'return': {}}
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'}
450
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
451
+=== Testing block-job-pause/job-resume ===
452
+{u'return': {}}
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'}
454
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
455
+{u'return': {}}
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'}
457
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
458
+=== Testing job-pause/block-job-resume ===
459
+{u'return': {}}
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'}
461
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
462
+{u'return': {}}
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'}
464
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
465
+=== Testing job-pause/job-resume ===
466
+{u'return': {}}
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'}
468
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
469
+{u'return': {}}
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'}
471
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
472
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
473
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
474
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
475
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
476
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
477
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
478
+{u'return': {}}
479
+
480
+Waiting for PENDING state...
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'}
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'}
483
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
484
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
485
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
486
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
487
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
488
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
489
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
490
+{u'return': {}}
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'}
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'}
493
+{u'return': []}
494
+
495
+
496
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False)
497
+{u'return': {}}
498
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
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'}
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'}
501
+
502
+Pause/resume in RUNNING
503
+=== Testing block-job-pause/block-job-resume ===
504
+{u'return': {}}
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'}
506
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
507
+{u'return': {}}
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'}
509
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
510
+=== Testing block-job-pause/job-resume ===
511
+{u'return': {}}
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'}
513
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
514
+{u'return': {}}
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'}
516
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
517
+=== Testing job-pause/block-job-resume ===
518
+{u'return': {}}
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'}
520
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
521
+{u'return': {}}
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'}
523
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
524
+=== Testing job-pause/job-resume ===
525
+{u'return': {}}
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'}
527
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
528
+{u'return': {}}
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'}
530
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
531
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
532
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
533
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
534
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
535
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
536
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
537
+{u'return': {}}
538
+
539
+Waiting for PENDING state...
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'}
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'}
542
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
543
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
544
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
545
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
546
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
547
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
548
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
549
+{u'return': {}}
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'}
551
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
552
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
553
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
554
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
555
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
556
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
557
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
558
+{u'return': {}}
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'}
560
+{u'return': []}
561
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
562
index XXXXXXX..XXXXXXX 100644
563
--- a/tests/qemu-iotests/group
564
+++ b/tests/qemu-iotests/group
565
@@ -XXX,XX +XXX,XX @@
566
215 rw auto quick
567
216 rw auto quick
568
218 rw auto quick
569
+219 rw auto
570
--
147
--
571
2.13.6
148
2.38.1
572
573
diff view generated by jsdifflib
1
We cannot yet move the whole logic around job cancelling to Job because
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
it depends on quite a few other things that are still only in BlockJob,
3
but we can move the cancelled field at least.
4
2
3
Just omit the various 'return' when the return type is void.
4
5
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Message-Id: <20230113204212.359076-2-kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
10
---
9
include/block/blockjob.h | 8 --------
11
scripts/block-coroutine-wrapper.py | 20 +++++++++++++++-----
10
include/block/blockjob_int.h | 8 --------
12
1 file changed, 15 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
13
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
14
diff --git a/scripts/block-coroutine-wrapper.py b/scripts/block-coroutine-wrapper.py
23
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
16
--- a/scripts/block-coroutine-wrapper.py
25
+++ b/include/block/blockjob.h
17
+++ b/scripts/block-coroutine-wrapper.py
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
18
@@ -XXX,XX +XXX,XX @@ def __init__(self, return_type: str, name: str, args: str,
27
Coroutine *co;
19
ctx = 'qemu_get_aio_context()'
28
20
self.ctx = ctx
29
/**
21
30
- * Set to true if the job should cancel itself. The flag must
22
+ self.get_result = 's->ret = '
31
- * always be tested just before toggling the busy flag from false
23
+ self.ret = 'return s.ret;'
32
- * to true. After a job has been cancelled, it should only yield
24
+ self.co_ret = 'return '
33
- * if #aio_poll will ("sooner or later") reenter the coroutine.
25
+ self.return_field = self.return_type + " ret;"
34
- */
26
+ if self.return_type == 'void':
35
- bool cancelled;
27
+ self.get_result = ''
36
-
28
+ self.ret = ''
37
- /**
29
+ self.co_ret = ''
38
* Set to true if the job should abort immediately without waiting
30
+ self.return_field = ''
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
+
31
+
76
/** Element of the list of jobs */
32
def gen_list(self, format: str) -> str:
77
QLIST_ENTRY(Job) job_list;
33
return ', '.join(format.format_map(arg.__dict__) for arg in self.args)
78
} Job;
34
79
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
35
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
80
/** Returns the enum string for the JobType of a given Job. */
36
{{
81
const char *job_type_str(const Job *job);
37
if (qemu_in_coroutine()) {{
82
38
{graph_assume_lock}
83
+/** Returns whether the job is scheduled for cancellation. */
39
- return {name}({ func.gen_list('{name}') });
84
+bool job_is_cancelled(Job *job);
40
+ {func.co_ret}{name}({ func.gen_list('{name}') });
85
+
41
}} else {{
86
/**
42
{struct_name} s = {{
87
* Get the next element from the list of block jobs after @job, or the
43
.poll_state.ctx = {func.ctx},
88
* first one if @job is %NULL.
44
@@ -XXX,XX +XXX,XX @@ def create_mixed_wrapper(func: FuncDecl) -> str:
89
diff --git a/block/backup.c b/block/backup.c
45
s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
90
index XXXXXXX..XXXXXXX 100644
46
91
--- a/block/backup.c
47
bdrv_poll_co(&s.poll_state);
92
+++ b/block/backup.c
48
- return s.ret;
93
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
49
+ {func.ret}
94
{
50
}}
95
uint64_t delay_ns;
51
}}"""
96
52
97
- if (block_job_is_cancelled(&job->common)) {
53
@@ -XXX,XX +XXX,XX @@ def create_co_wrapper(func: FuncDecl) -> str:
98
+ if (job_is_cancelled(&job->common.job)) {
54
s.poll_state.co = qemu_coroutine_create({name}_entry, &s);
99
return true;
55
100
}
56
bdrv_poll_co(&s.poll_state);
101
57
- return s.ret;
102
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
58
+ {func.ret}
103
job->bytes_read = 0;
59
}}"""
104
block_job_sleep_ns(&job->common, delay_ns);
60
105
61
106
- if (block_job_is_cancelled(&job->common)) {
62
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
107
+ if (job_is_cancelled(&job->common.job)) {
63
108
return true;
64
typedef struct {struct_name} {{
109
}
65
BdrvPollCo poll_state;
110
66
- {func.return_type} ret;
111
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
67
+ {func.return_field}
112
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
68
{ func.gen_block(' {decl};') }
113
/* All bits are set in copy_bitmap to allow any cluster to be copied.
69
}} {struct_name};
114
* This does not actually require them to be copied. */
70
115
- while (!block_job_is_cancelled(&job->common)) {
71
@@ -XXX,XX +XXX,XX @@ def gen_wrapper(func: FuncDecl) -> str:
116
+ while (!job_is_cancelled(&job->common.job)) {
72
{struct_name} *s = opaque;
117
/* Yield until the job is cancelled. We just let our before_write
73
118
* notify callback service CoW requests. */
74
{graph_lock}
119
block_job_yield(&job->common);
75
- s->ret = {name}({ func.gen_list('s->{name}') });
120
diff --git a/block/commit.c b/block/commit.c
76
+ {func.get_result}{name}({ func.gen_list('s->{name}') });
121
index XXXXXXX..XXXXXXX 100644
77
{graph_unlock}
122
--- a/block/commit.c
78
s->poll_state.in_progress = false;
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;
306
}
307
- if (job->pause_count || job->cancelled || !job->driver->complete) {
308
+ if (job->pause_count || job_is_cancelled(&job->job) ||
309
+ !job->driver->complete)
310
+ {
311
error_setg(errp, "The active block job '%s' cannot be completed",
312
job->job.id);
313
return;
314
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
315
if (!block_job_should_pause(job)) {
316
return;
317
}
318
- if (block_job_is_cancelled(job)) {
319
+ if (job_is_cancelled(&job->job)) {
320
return;
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
}
335
336
-bool block_job_is_cancelled(BlockJob *job)
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
79
420
--
80
--
421
2.13.6
81
2.38.1
422
423
diff view generated by jsdifflib
1
This commit moves some core functions for dealing with the job coroutine
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
from BlockJob to Job. This includes primarily entering the coroutine
3
(both for the first and reentering) and yielding explicitly and at pause
4
points.
5
2
3
BlockDriver->bdrv_io_plug is categorized as IO callback, and it
4
currently doesn't run in a coroutine. We should let it take a graph
5
rdlock since the callback traverses the block nodes graph, which however
6
is only possible in a coroutine.
7
8
The only caller of this function is blk_io_plug(), therefore make
9
blk_io_plug() a co_wrapper, so that we're always running in a coroutine
10
where the lock can be taken.
11
12
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
14
Message-Id: <20230113204212.359076-3-kwolf@redhat.com>
15
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
17
---
9
include/block/blockjob.h | 40 --------
18
include/block/block-io.h | 3 ++-
10
include/block/blockjob_int.h | 26 -----
19
include/block/block_int-common.h | 2 +-
11
include/qemu/job.h | 76 +++++++++++++++
20
include/sysemu/block-backend-io.h | 4 +++-
12
block/backup.c | 2 +-
21
block/block-backend.c | 4 ++--
13
block/commit.c | 4 +-
22
block/file-posix.c | 10 +++++-----
14
block/mirror.c | 22 ++---
23
block/io.c | 8 ++++----
15
block/replication.c | 2 +-
24
block/nvme.c | 4 ++--
16
block/stream.c | 4 +-
25
7 files changed, 19 insertions(+), 16 deletions(-)
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
26
25
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
27
diff --git a/include/block/block-io.h b/include/block/block-io.h
26
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/blockjob.h
29
--- a/include/block/block-io.h
28
+++ b/include/block/blockjob.h
30
+++ b/include/block/block-io.h
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
31
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx);
30
BlockBackend *blk;
32
33
AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
34
35
-void bdrv_io_plug(BlockDriverState *bs);
36
+void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs);
37
+
38
void bdrv_io_unplug(BlockDriverState *bs);
39
40
bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
41
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/block_int-common.h
44
+++ b/include/block/block_int-common.h
45
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
46
void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
47
48
/* io queue for linux-aio */
49
- void (*bdrv_io_plug)(BlockDriverState *bs);
50
+ void coroutine_fn (*bdrv_co_io_plug)(BlockDriverState *bs);
51
void (*bdrv_io_unplug)(BlockDriverState *bs);
31
52
32
/**
53
/**
33
- * The coroutine that executes the job. If not NULL, it is
54
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
34
- * reentered when busy is false and the job is cancelled.
35
- */
36
- Coroutine *co;
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
- * 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
-
50
- /**
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
55
index XXXXXXX..XXXXXXX 100644
104
--- a/include/block/blockjob_int.h
56
--- a/include/sysemu/block-backend-io.h
105
+++ b/include/block/blockjob_int.h
57
+++ b/include/sysemu/block-backend-io.h
106
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
58
@@ -XXX,XX +XXX,XX @@ void blk_iostatus_set_err(BlockBackend *blk, int error);
107
/** Generic JobDriver callbacks and settings */
59
int blk_get_max_iov(BlockBackend *blk);
108
JobDriver job_driver;
60
int blk_get_max_hw_iov(BlockBackend *blk);
109
61
110
- /** Mandatory: Entrypoint for the Coroutine. */
62
-void blk_io_plug(BlockBackend *blk);
111
- CoroutineEntry *start;
63
+void coroutine_fn blk_co_io_plug(BlockBackend *blk);
112
-
64
+void co_wrapper blk_io_plug(BlockBackend *blk);
113
/**
65
+
114
* Optional callback for job types whose completion must be triggered
66
void blk_io_unplug(BlockBackend *blk);
115
* manually.
67
AioContext *blk_get_aio_context(BlockBackend *blk);
116
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
68
BlockAcctStats *blk_get_stats(BlockBackend *blk);
117
*/
69
diff --git a/block/block-backend.c b/block/block-backend.c
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
70
index XXXXXXX..XXXXXXX 100644
155
--- a/include/qemu/job.h
71
--- a/block/block-backend.c
156
+++ b/include/qemu/job.h
72
+++ b/block/block-backend.c
157
@@ -XXX,XX +XXX,XX @@
73
@@ -XXX,XX +XXX,XX @@ void blk_add_insert_bs_notifier(BlockBackend *blk, Notifier *notify)
158
74
notifier_list_add(&blk->insert_bs_notifiers, notify);
159
#include "qapi/qapi-types-block-core.h"
75
}
160
#include "qemu/queue.h"
76
161
+#include "qemu/coroutine.h"
77
-void blk_io_plug(BlockBackend *blk)
162
78
+void coroutine_fn blk_co_io_plug(BlockBackend *blk)
163
typedef struct JobDriver JobDriver;
79
{
164
80
BlockDriverState *bs = blk_bs(blk);
165
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
81
IO_CODE();
166
AioContext *aio_context;
82
167
83
if (bs) {
168
/**
84
- bdrv_io_plug(bs);
169
+ * The coroutine that executes the job. If not NULL, it is reentered when
85
+ bdrv_co_io_plug(bs);
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
}
86
}
315
}
87
}
316
88
317
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
89
diff --git a/block/file-posix.c b/block/file-posix.c
318
mirror_wait_for_io(s);
90
index XXXXXXX..XXXXXXX 100644
91
--- a/block/file-posix.c
92
+++ b/block/file-posix.c
93
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset,
94
return raw_co_prw(bs, offset, bytes, qiov, QEMU_AIO_WRITE);
95
}
96
97
-static void raw_aio_plug(BlockDriverState *bs)
98
+static void coroutine_fn raw_co_io_plug(BlockDriverState *bs)
99
{
100
BDRVRawState __attribute__((unused)) *s = bs->opaque;
101
#ifdef CONFIG_LINUX_AIO
102
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
103
.bdrv_co_copy_range_from = raw_co_copy_range_from,
104
.bdrv_co_copy_range_to = raw_co_copy_range_to,
105
.bdrv_refresh_limits = raw_refresh_limits,
106
- .bdrv_io_plug = raw_aio_plug,
107
+ .bdrv_co_io_plug = raw_co_io_plug,
108
.bdrv_io_unplug = raw_aio_unplug,
109
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
110
111
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
112
.bdrv_co_copy_range_from = raw_co_copy_range_from,
113
.bdrv_co_copy_range_to = raw_co_copy_range_to,
114
.bdrv_refresh_limits = raw_refresh_limits,
115
- .bdrv_io_plug = raw_aio_plug,
116
+ .bdrv_co_io_plug = raw_co_io_plug,
117
.bdrv_io_unplug = raw_aio_unplug,
118
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
119
120
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
121
.bdrv_co_pwritev = raw_co_pwritev,
122
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
123
.bdrv_refresh_limits = raw_refresh_limits,
124
- .bdrv_io_plug = raw_aio_plug,
125
+ .bdrv_co_io_plug = raw_co_io_plug,
126
.bdrv_io_unplug = raw_aio_unplug,
127
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
128
129
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
130
.bdrv_co_pwritev = raw_co_pwritev,
131
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
132
.bdrv_refresh_limits = raw_refresh_limits,
133
- .bdrv_io_plug = raw_aio_plug,
134
+ .bdrv_co_io_plug = raw_co_io_plug,
135
.bdrv_io_unplug = raw_aio_unplug,
136
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
137
138
diff --git a/block/io.c b/block/io.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/block/io.c
141
+++ b/block/io.c
142
@@ -XXX,XX +XXX,XX @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size)
143
return mem;
144
}
145
146
-void bdrv_io_plug(BlockDriverState *bs)
147
+void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs)
148
{
149
BdrvChild *child;
150
IO_CODE();
151
152
QLIST_FOREACH(child, &bs->children, next) {
153
- bdrv_io_plug(child->bs);
154
+ bdrv_co_io_plug(child->bs);
319
}
155
}
320
156
321
- block_job_pause_point(&s->common);
157
if (qatomic_fetch_inc(&bs->io_plugged) == 0) {
322
+ job_pause_point(&s->common.job);
158
BlockDriver *drv = bs->drv;
323
159
- if (drv && drv->bdrv_io_plug) {
324
/* Find the number of consective dirty chunks following the first dirty
160
- drv->bdrv_io_plug(bs);
325
* one, and wait for in flight requests in them. */
161
+ if (drv && drv->bdrv_co_io_plug) {
326
@@ -XXX,XX +XXX,XX @@ static void mirror_throttle(MirrorBlockJob *s)
162
+ drv->bdrv_co_io_plug(bs);
327
s->last_pause_ns = now;
163
}
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
}
164
}
333
}
165
}
334
166
diff --git a/block/nvme.c b/block/nvme.c
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
167
index XXXXXXX..XXXXXXX 100644
393
--- a/block/replication.c
168
--- a/block/nvme.c
394
+++ b/block/replication.c
169
+++ b/block/nvme.c
395
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
170
@@ -XXX,XX +XXX,XX @@ static void nvme_attach_aio_context(BlockDriverState *bs,
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
}
171
}
456
}
172
}
457
173
458
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
174
-static void nvme_aio_plug(BlockDriverState *bs)
459
BlockJob *job;
175
+static void coroutine_fn nvme_co_io_plug(BlockDriverState *bs)
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
{
176
{
517
- return !block_job_timer_pending(job);
177
BDRVNVMeState *s = bs->opaque;
518
+ return !timer_pending(&job->sleep_timer);
178
assert(!s->plugged);
519
}
179
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nvme = {
520
180
.bdrv_detach_aio_context = nvme_detach_aio_context,
521
static void block_job_pause(BlockJob *job)
181
.bdrv_attach_aio_context = nvme_attach_aio_context,
522
{
182
523
- job->pause_count++;
183
- .bdrv_io_plug = nvme_aio_plug,
524
+ job->job.pause_count++;
184
+ .bdrv_co_io_plug = nvme_co_io_plug,
525
}
185
.bdrv_io_unplug = nvme_aio_unplug,
526
186
527
static void block_job_resume(BlockJob *job)
187
.bdrv_register_buf = nvme_register_buf,
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
1046
index XXXXXXX..XXXXXXX 100644
1047
--- a/tests/test-bdrv-drain.c
1048
+++ 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)
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);
1074
1075
if (drain_type == BDRV_DRAIN_ALL) {
1076
/* bdrv_drain_all() drains both src and target */
1077
- g_assert_cmpint(job->pause_count, ==, 2);
1078
+ g_assert_cmpint(job->job.pause_count, ==, 2);
1079
} else {
1080
- g_assert_cmpint(job->pause_count, ==, 1);
1081
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1082
}
1083
/* XXX We don't wait until the job is actually paused. Is this okay? */
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);
1099
1100
if (drain_type == BDRV_DRAIN_ALL) {
1101
/* bdrv_drain_all() drains both src and target */
1102
- g_assert_cmpint(job->pause_count, ==, 2);
1103
+ g_assert_cmpint(job->job.pause_count, ==, 2);
1104
} else {
1105
- g_assert_cmpint(job->pause_count, ==, 1);
1106
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1107
}
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
--
188
--
1239
2.13.6
189
2.38.1
1240
1241
diff view generated by jsdifflib
1
Go through the Job layer in order to send QMP events. For the moment,
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
these functions only call a notifier in the BlockJob layer that sends
2
3
the existing commands.
3
BlockDriver->bdrv_io_unplug is categorized as IO callback, and it
4
4
currently doesn't run in a coroutine. We should let it take a graph
5
This uses notifiers rather than JobDriver callbacks because internal
5
rdlock since the callback traverses the block nodes graph, which however
6
users of jobs won't receive QMP events, but might still be interested
6
is only possible in a coroutine.
7
in getting notified for the events.
7
8
8
The only caller of this function is blk_io_unplug(), therefore make
9
blk_io_unplug() a co_wrapper, so that we're always running in a
10
coroutine where the lock can be taken.
11
12
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Message-Id: <20230113204212.359076-4-kwolf@redhat.com>
15
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
17
---
12
include/block/blockjob.h | 9 +++++++++
18
include/block/block-io.h | 3 +--
13
include/qemu/job.h | 18 ++++++++++++++++++
19
include/block/block_int-common.h | 2 +-
14
blockjob.c | 41 +++++++++++++++++++++++++++--------------
20
include/sysemu/block-backend-io.h | 4 +++-
15
job.c | 19 +++++++++++++++++++
21
block/blkio.c | 4 ++--
16
4 files changed, 73 insertions(+), 14 deletions(-)
22
block/block-backend.c | 4 ++--
17
23
block/file-posix.c | 10 +++++-----
18
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
24
block/io.c | 8 ++++----
19
index XXXXXXX..XXXXXXX 100644
25
block/nvme.c | 4 ++--
20
--- a/include/block/blockjob.h
26
8 files changed, 20 insertions(+), 19 deletions(-)
21
+++ b/include/block/blockjob.h
27
22
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
28
diff --git a/include/block/block-io.h b/include/block/block-io.h
23
/** Block other operations when block job is running */
29
index XXXXXXX..XXXXXXX 100644
24
Error *blocker;
30
--- a/include/block/block-io.h
25
31
+++ b/include/block/block-io.h
26
+ /** Called when a cancelled job is finalised. */
32
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_leave(BlockDriverState *bs, AioContext *old_ctx);
27
+ Notifier finalize_cancelled_notifier;
33
AioContext *child_of_bds_get_parent_aio_context(BdrvChild *c);
34
35
void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs);
36
-
37
-void bdrv_io_unplug(BlockDriverState *bs);
38
+void coroutine_fn bdrv_co_io_unplug(BlockDriverState *bs);
39
40
bool coroutine_fn bdrv_co_can_store_new_dirty_bitmap(BlockDriverState *bs,
41
const char *name,
42
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/block/block_int-common.h
45
+++ b/include/block/block_int-common.h
46
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
47
48
/* io queue for linux-aio */
49
void coroutine_fn (*bdrv_co_io_plug)(BlockDriverState *bs);
50
- void (*bdrv_io_unplug)(BlockDriverState *bs);
51
+ void coroutine_fn (*bdrv_co_io_unplug)(BlockDriverState *bs);
52
53
/**
54
* bdrv_drain_begin is called if implemented in the beginning of a
55
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
56
index XXXXXXX..XXXXXXX 100644
57
--- a/include/sysemu/block-backend-io.h
58
+++ b/include/sysemu/block-backend-io.h
59
@@ -XXX,XX +XXX,XX @@ int blk_get_max_hw_iov(BlockBackend *blk);
60
void coroutine_fn blk_co_io_plug(BlockBackend *blk);
61
void co_wrapper blk_io_plug(BlockBackend *blk);
62
63
-void blk_io_unplug(BlockBackend *blk);
64
+void coroutine_fn blk_co_io_unplug(BlockBackend *blk);
65
+void co_wrapper blk_io_unplug(BlockBackend *blk);
28
+
66
+
29
+ /** Called when a successfully completed job is finalised. */
67
AioContext *blk_get_aio_context(BlockBackend *blk);
30
+ Notifier finalize_completed_notifier;
68
BlockAcctStats *blk_get_stats(BlockBackend *blk);
31
+
69
void *blk_aio_get(const AIOCBInfo *aiocb_info, BlockBackend *blk,
32
+ /** Called when the job transitions to PENDING */
70
diff --git a/block/blkio.c b/block/blkio.c
33
+ Notifier pending_notifier;
71
index XXXXXXX..XXXXXXX 100644
34
+
72
--- a/block/blkio.c
35
/** BlockDriverStates that are involved in this block job */
73
+++ b/block/blkio.c
36
GSList *nodes;
74
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_co_pwrite_zeroes(BlockDriverState *bs,
37
75
return cod.ret;
38
diff --git a/include/qemu/job.h b/include/qemu/job.h
76
}
39
index XXXXXXX..XXXXXXX 100644
77
40
--- a/include/qemu/job.h
78
-static void blkio_io_unplug(BlockDriverState *bs)
41
+++ b/include/qemu/job.h
79
+static void coroutine_fn blkio_co_io_unplug(BlockDriverState *bs)
42
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
80
{
43
/** True if this job should automatically dismiss itself */
81
BDRVBlkioState *s = bs->opaque;
44
bool auto_dismiss;
82
45
83
@@ -XXX,XX +XXX,XX @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp)
46
+ /** Notifiers called when a cancelled job is finalised */
84
.bdrv_co_pwritev = blkio_co_pwritev, \
47
+ NotifierList on_finalize_cancelled;
85
.bdrv_co_flush_to_disk = blkio_co_flush, \
48
+
86
.bdrv_co_pwrite_zeroes = blkio_co_pwrite_zeroes, \
49
+ /** Notifiers called when a successfully completed job is finalised */
87
- .bdrv_io_unplug = blkio_io_unplug, \
50
+ NotifierList on_finalize_completed;
88
+ .bdrv_co_io_unplug = blkio_co_io_unplug, \
51
+
89
.bdrv_refresh_limits = blkio_refresh_limits, \
52
+ /** Notifiers called when the job transitions to PENDING */
90
.bdrv_register_buf = blkio_register_buf, \
53
+ NotifierList on_pending;
91
.bdrv_unregister_buf = blkio_unregister_buf, \
54
+
92
diff --git a/block/block-backend.c b/block/block-backend.c
55
/** Element of the list of jobs */
93
index XXXXXXX..XXXXXXX 100644
56
QLIST_ENTRY(Job) job_list;
94
--- a/block/block-backend.c
57
} Job;
95
+++ b/block/block-backend.c
58
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
96
@@ -XXX,XX +XXX,XX @@ void coroutine_fn blk_co_io_plug(BlockBackend *blk)
59
*/
97
}
60
void job_unref(Job *job);
98
}
61
99
62
+/** To be called when a cancelled job is finalised. */
100
-void blk_io_unplug(BlockBackend *blk)
63
+void job_event_cancelled(Job *job);
101
+void coroutine_fn blk_co_io_unplug(BlockBackend *blk)
64
+
102
{
65
+/** To be called when a successfully completed job is finalised. */
103
BlockDriverState *bs = blk_bs(blk);
66
+void job_event_completed(Job *job);
104
IO_CODE();
67
+
105
68
+/** To be called when the job transitions to PENDING */
106
if (bs) {
69
+void job_event_pending(Job *job);
107
- bdrv_io_unplug(bs);
70
+
108
+ bdrv_co_io_unplug(bs);
71
/**
109
}
72
* Conditionally enter the job coroutine if the job is ready to run, not
110
}
73
* already busy and fn() returns true. fn() is called while under the job_lock
111
74
diff --git a/blockjob.c b/blockjob.c
112
diff --git a/block/file-posix.c b/block/file-posix.c
75
index XXXXXXX..XXXXXXX 100644
113
index XXXXXXX..XXXXXXX 100644
76
--- a/blockjob.c
114
--- a/block/file-posix.c
77
+++ b/blockjob.c
115
+++ b/block/file-posix.c
78
@@ -XXX,XX +XXX,XX @@
116
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn raw_co_io_plug(BlockDriverState *bs)
79
#include "qemu/coroutine.h"
117
#endif
80
#include "qemu/timer.h"
118
}
81
119
82
-static void block_job_event_cancelled(BlockJob *job);
120
-static void raw_aio_unplug(BlockDriverState *bs)
83
-static void block_job_event_completed(BlockJob *job, const char *msg);
121
+static void coroutine_fn raw_co_io_unplug(BlockDriverState *bs)
84
-static void block_job_event_pending(BlockJob *job);
122
{
85
-
123
BDRVRawState __attribute__((unused)) *s = bs->opaque;
86
/* Transactional group of block jobs */
124
#ifdef CONFIG_LINUX_AIO
87
struct BlockJobTxn {
125
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
88
126
.bdrv_co_copy_range_to = raw_co_copy_range_to,
89
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
127
.bdrv_refresh_limits = raw_refresh_limits,
90
/* Emit events only if we actually started */
128
.bdrv_co_io_plug = raw_co_io_plug,
91
if (job_started(&job->job)) {
129
- .bdrv_io_unplug = raw_aio_unplug,
92
if (job_is_cancelled(&job->job)) {
130
+ .bdrv_co_io_unplug = raw_co_io_unplug,
93
- block_job_event_cancelled(job);
131
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
94
+ job_event_cancelled(&job->job);
132
95
} else {
133
.bdrv_co_truncate = raw_co_truncate,
96
- const char *msg = NULL;
134
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
97
- if (job->ret < 0) {
135
.bdrv_co_copy_range_to = raw_co_copy_range_to,
98
- msg = strerror(-job->ret);
136
.bdrv_refresh_limits = raw_refresh_limits,
99
- }
137
.bdrv_co_io_plug = raw_co_io_plug,
100
- block_job_event_completed(job, msg);
138
- .bdrv_io_unplug = raw_aio_unplug,
101
+ job_event_completed(&job->job);
139
+ .bdrv_co_io_unplug = raw_co_io_unplug,
140
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
141
142
.bdrv_co_truncate = raw_co_truncate,
143
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
144
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
145
.bdrv_refresh_limits = raw_refresh_limits,
146
.bdrv_co_io_plug = raw_co_io_plug,
147
- .bdrv_io_unplug = raw_aio_unplug,
148
+ .bdrv_co_io_unplug = raw_co_io_unplug,
149
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
150
151
.bdrv_co_truncate = raw_co_truncate,
152
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
153
.bdrv_co_flush_to_disk = raw_co_flush_to_disk,
154
.bdrv_refresh_limits = raw_refresh_limits,
155
.bdrv_co_io_plug = raw_co_io_plug,
156
- .bdrv_io_unplug = raw_aio_unplug,
157
+ .bdrv_co_io_unplug = raw_co_io_unplug,
158
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
159
160
.bdrv_co_truncate = raw_co_truncate,
161
diff --git a/block/io.c b/block/io.c
162
index XXXXXXX..XXXXXXX 100644
163
--- a/block/io.c
164
+++ b/block/io.c
165
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_io_plug(BlockDriverState *bs)
166
}
167
}
168
169
-void bdrv_io_unplug(BlockDriverState *bs)
170
+void coroutine_fn bdrv_co_io_unplug(BlockDriverState *bs)
171
{
172
BdrvChild *child;
173
IO_CODE();
174
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs)
175
assert(bs->io_plugged);
176
if (qatomic_fetch_dec(&bs->io_plugged) == 1) {
177
BlockDriver *drv = bs->drv;
178
- if (drv && drv->bdrv_io_unplug) {
179
- drv->bdrv_io_unplug(bs);
180
+ if (drv && drv->bdrv_co_io_unplug) {
181
+ drv->bdrv_co_io_unplug(bs);
102
}
182
}
103
}
183
}
104
184
105
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
185
QLIST_FOREACH(child, &bs->children, next) {
106
{
186
- bdrv_io_unplug(child->bs);
107
job_state_transition(&job->job, JOB_STATUS_PENDING);
187
+ bdrv_co_io_unplug(child->bs);
108
if (!job->job.auto_finalize) {
188
}
109
- block_job_event_pending(job);
189
}
110
+ job_event_pending(&job->job);
190
111
}
191
diff --git a/block/nvme.c b/block/nvme.c
112
return 0;
192
index XXXXXXX..XXXXXXX 100644
113
}
193
--- a/block/nvme.c
114
@@ -XXX,XX +XXX,XX @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
194
+++ b/block/nvme.c
115
}
195
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn nvme_co_io_plug(BlockDriverState *bs)
116
}
196
s->plugged = true;
117
197
}
118
-static void block_job_event_cancelled(BlockJob *job)
198
119
+static void block_job_event_cancelled(Notifier *n, void *opaque)
199
-static void nvme_aio_unplug(BlockDriverState *bs)
120
{
200
+static void coroutine_fn nvme_co_io_unplug(BlockDriverState *bs)
121
+ BlockJob *job = opaque;
201
{
122
+
202
BDRVNVMeState *s = bs->opaque;
123
if (block_job_is_internal(job)) {
203
assert(s->plugged);
124
return;
204
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nvme = {
125
}
205
.bdrv_attach_aio_context = nvme_attach_aio_context,
126
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
206
127
&error_abort);
207
.bdrv_co_io_plug = nvme_co_io_plug,
128
}
208
- .bdrv_io_unplug = nvme_aio_unplug,
129
209
+ .bdrv_co_io_unplug = nvme_co_io_unplug,
130
-static void block_job_event_completed(BlockJob *job, const char *msg)
210
131
+static void block_job_event_completed(Notifier *n, void *opaque)
211
.bdrv_register_buf = nvme_register_buf,
132
{
212
.bdrv_unregister_buf = nvme_unregister_buf,
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
--
213
--
214
2.13.6
214
2.38.1
215
216
diff view generated by jsdifflib
1
This doesn't actually move any transaction code to Job yet, but it
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
renames the type for transactions from BlockJobTxn to JobTxn and makes
2
3
them contain Jobs rather than BlockJobs
3
bdrv_is_inserted() is categorized as an I/O function, and it currently
4
4
doesn't run in a coroutine. We should let it take a graph rdlock since
5
it traverses the block nodes graph, which however is only possible in a
6
coroutine.
7
8
Therefore turn it into a co_wrapper to move the actual function into a
9
coroutine where the lock can be taken.
10
11
At the same time, add also blk_is_inserted as co_wrapper_mixed, since it
12
is called in both coroutine and non-coroutine contexts.
13
14
Because now this function creates a new coroutine and polls, we need to
15
take the AioContext lock where it is missing, for the only reason that
16
internally c_w_mixed_bdrv_rdlock calls AIO_WAIT_WHILE and it expects to
17
release the AioContext lock. Once the rwlock is ultimated and placed in
18
every place it needs to be, we will poll using AIO_WAIT_WHILE_UNLOCKED
19
and remove the AioContext lock.
20
21
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
23
Message-Id: <20230113204212.359076-5-kwolf@redhat.com>
24
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
26
---
8
include/block/block_int.h | 2 +-
27
include/block/block-io.h | 5 ++++-
9
include/block/blockjob.h | 11 ++++----
28
include/block/block_int-common.h | 2 +-
10
include/block/blockjob_int.h | 2 +-
29
include/sysemu/block-backend-io.h | 5 ++++-
11
include/qemu/job.h | 3 +++
30
block.c | 8 ++++----
12
block/backup.c | 2 +-
31
block/block-backend.c | 4 ++--
13
blockdev.c | 14 +++++------
32
block/file-posix.c | 8 ++++----
14
blockjob.c | 60 +++++++++++++++++++++++---------------------
33
block/io.c | 12 ++++++------
15
tests/test-blockjob-txn.c | 8 +++---
34
blockdev.c | 8 +++++++-
16
8 files changed, 54 insertions(+), 48 deletions(-)
35
8 files changed, 32 insertions(+), 20 deletions(-)
17
36
18
diff --git a/include/block/block_int.h b/include/block/block_int.h
37
diff --git a/include/block/block-io.h b/include/block/block-io.h
19
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/block_int.h
39
--- a/include/block/block-io.h
21
+++ b/include/block/block_int.h
40
+++ b/include/block/block-io.h
22
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
41
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_read_only(BlockDriverState *bs);
23
BlockdevOnError on_target_error,
42
bool bdrv_is_writable(BlockDriverState *bs);
24
int creation_flags,
43
bool bdrv_is_sg(BlockDriverState *bs);
25
BlockCompletionFunc *cb, void *opaque,
44
int bdrv_get_flags(BlockDriverState *bs);
26
- BlockJobTxn *txn, Error **errp);
45
-bool bdrv_is_inserted(BlockDriverState *bs);
27
+ JobTxn *txn, Error **errp);
46
+
28
47
+bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs);
29
void hmp_drive_add_node(Monitor *mon, const char *optstr);
48
+bool co_wrapper bdrv_is_inserted(BlockDriverState *bs);
30
49
+
31
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
50
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
32
index XXXXXXX..XXXXXXX 100644
51
void bdrv_eject(BlockDriverState *bs, bool eject_flag);
33
--- a/include/block/blockjob.h
52
const char *bdrv_get_format_name(BlockDriverState *bs);
34
+++ b/include/block/blockjob.h
53
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
35
@@ -XXX,XX +XXX,XX @@
54
index XXXXXXX..XXXXXXX 100644
36
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
55
--- a/include/block/block_int-common.h
37
56
+++ b/include/block/block_int-common.h
38
typedef struct BlockJobDriver BlockJobDriver;
57
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
39
-typedef struct BlockJobTxn BlockJobTxn;
58
BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
40
+typedef struct JobTxn JobTxn;
59
41
60
/* removable device specific */
61
- bool (*bdrv_is_inserted)(BlockDriverState *bs);
62
+ bool coroutine_fn (*bdrv_co_is_inserted)(BlockDriverState *bs);
63
void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
64
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
65
66
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
67
index XXXXXXX..XXXXXXX 100644
68
--- a/include/sysemu/block-backend-io.h
69
+++ b/include/sysemu/block-backend-io.h
70
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_aio_ioctl(BlockBackend *blk, unsigned long int req, void *buf,
71
72
void blk_inc_in_flight(BlockBackend *blk);
73
void blk_dec_in_flight(BlockBackend *blk);
74
-bool blk_is_inserted(BlockBackend *blk);
75
+
76
+bool coroutine_fn blk_co_is_inserted(BlockBackend *blk);
77
+bool co_wrapper_mixed blk_is_inserted(BlockBackend *blk);
78
+
79
bool blk_is_available(BlockBackend *blk);
80
void blk_lock_medium(BlockBackend *blk, bool locked);
81
void blk_eject(BlockBackend *blk, bool eject_flag);
82
diff --git a/block.c b/block.c
83
index XXXXXXX..XXXXXXX 100644
84
--- a/block.c
85
+++ b/block.c
86
@@ -XXX,XX +XXX,XX @@ out:
42
/**
87
/**
43
* BlockJob:
88
* Return TRUE if the media is present
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
*/
89
*/
58
-BlockJobTxn *block_job_txn_new(void);
90
-bool bdrv_is_inserted(BlockDriverState *bs)
59
+JobTxn *block_job_txn_new(void);
91
+bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs)
60
92
{
61
/**
93
BlockDriver *drv = bs->drv;
62
* block_job_txn_unref:
94
BdrvChild *child;
63
@@ -XXX,XX +XXX,XX @@ BlockJobTxn *block_job_txn_new(void);
95
@@ -XXX,XX +XXX,XX @@ bool bdrv_is_inserted(BlockDriverState *bs)
64
* or block_job_txn_new. If it's the last reference to the object, it will be
96
if (!drv) {
65
* freed.
97
return false;
66
*/
98
}
67
-void block_job_txn_unref(BlockJobTxn *txn);
99
- if (drv->bdrv_is_inserted) {
68
+void block_job_txn_unref(JobTxn *txn);
100
- return drv->bdrv_is_inserted(bs);
69
101
+ if (drv->bdrv_co_is_inserted) {
70
/**
102
+ return drv->bdrv_co_is_inserted(bs);
71
* block_job_txn_add_job:
103
}
72
@@ -XXX,XX +XXX,XX @@ void block_job_txn_unref(BlockJobTxn *txn);
104
QLIST_FOREACH(child, &bs->children, next) {
73
* The caller must call either block_job_txn_unref() or block_job_completed()
105
- if (!bdrv_is_inserted(child->bs)) {
74
* to release the reference that is automatically grabbed here.
106
+ if (!bdrv_co_is_inserted(child->bs)) {
75
*/
107
return false;
76
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job);
108
}
77
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
109
}
78
110
diff --git a/block/block-backend.c b/block/block-backend.c
79
/**
111
index XXXXXXX..XXXXXXX 100644
80
* block_job_is_internal:
112
--- a/block/block-backend.c
81
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
113
+++ b/block/block-backend.c
82
index XXXXXXX..XXXXXXX 100644
114
@@ -XXX,XX +XXX,XX @@ void blk_activate(BlockBackend *blk, Error **errp)
83
--- a/include/block/blockjob_int.h
115
bdrv_activate(bs, errp);
84
+++ b/include/block/blockjob_int.h
116
}
85
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
117
86
* called from a wrapper that is specific to the job type.
118
-bool blk_is_inserted(BlockBackend *blk)
87
*/
119
+bool coroutine_fn blk_co_is_inserted(BlockBackend *blk)
88
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
120
{
89
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
121
BlockDriverState *bs = blk_bs(blk);
90
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
122
IO_CODE();
91
uint64_t shared_perm, int64_t speed, int flags,
123
92
BlockCompletionFunc *cb, void *opaque, Error **errp);
124
- return bs && bdrv_is_inserted(bs);
93
125
+ return bs && bdrv_co_is_inserted(bs);
94
diff --git a/include/qemu/job.h b/include/qemu/job.h
126
}
95
index XXXXXXX..XXXXXXX 100644
127
96
--- a/include/qemu/job.h
128
bool blk_is_available(BlockBackend *blk)
97
+++ b/include/qemu/job.h
129
diff --git a/block/file-posix.c b/block/file-posix.c
98
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
130
index XXXXXXX..XXXXXXX 100644
99
131
--- a/block/file-posix.c
100
/** Element of the list of jobs */
132
+++ b/block/file-posix.c
101
QLIST_ENTRY(Job) job_list;
133
@@ -XXX,XX +XXX,XX @@ out:
102
+
134
return prio;
103
+ /** Element of the list of jobs in a job transaction */
135
}
104
+ QLIST_ENTRY(Job) txn_list;
136
105
} Job;
137
-static bool cdrom_is_inserted(BlockDriverState *bs)
106
138
+static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs)
107
/**
139
{
108
diff --git a/block/backup.c b/block/backup.c
140
BDRVRawState *s = bs->opaque;
109
index XXXXXXX..XXXXXXX 100644
141
int ret;
110
--- a/block/backup.c
142
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
111
+++ b/block/backup.c
143
= raw_get_allocated_file_size,
112
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
144
113
BlockdevOnError on_target_error,
145
/* removable device support */
114
int creation_flags,
146
- .bdrv_is_inserted = cdrom_is_inserted,
115
BlockCompletionFunc *cb, void *opaque,
147
+ .bdrv_co_is_inserted = cdrom_co_is_inserted,
116
- BlockJobTxn *txn, Error **errp)
148
.bdrv_eject = cdrom_eject,
117
+ JobTxn *txn, Error **errp)
149
.bdrv_lock_medium = cdrom_lock_medium,
118
{
150
119
int64_t len;
151
@@ -XXX,XX +XXX,XX @@ static int cdrom_reopen(BlockDriverState *bs)
120
BlockDriverInfo bdi;
152
return 0;
153
}
154
155
-static bool cdrom_is_inserted(BlockDriverState *bs)
156
+static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs)
157
{
158
return raw_getlength(bs) > 0;
159
}
160
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
161
= raw_get_allocated_file_size,
162
163
/* removable device support */
164
- .bdrv_is_inserted = cdrom_is_inserted,
165
+ .bdrv_co_is_inserted = cdrom_co_is_inserted,
166
.bdrv_eject = cdrom_eject,
167
.bdrv_lock_medium = cdrom_lock_medium,
168
};
169
diff --git a/block/io.c b/block/io.c
170
index XXXXXXX..XXXXXXX 100644
171
--- a/block/io.c
172
+++ b/block/io.c
173
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_preadv_part(BdrvChild *child,
174
175
trace_bdrv_co_preadv_part(bs, offset, bytes, flags);
176
177
- if (!bdrv_is_inserted(bs)) {
178
+ if (!bdrv_co_is_inserted(bs)) {
179
return -ENOMEDIUM;
180
}
181
182
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev_part(BdrvChild *child,
183
184
trace_bdrv_co_pwritev_part(child->bs, offset, bytes, flags);
185
186
- if (!bdrv_is_inserted(bs)) {
187
+ if (!bdrv_co_is_inserted(bs)) {
188
return -ENOMEDIUM;
189
}
190
191
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_flush(BlockDriverState *bs)
192
193
bdrv_inc_in_flight(bs);
194
195
- if (!bdrv_is_inserted(bs) || bdrv_is_read_only(bs) ||
196
+ if (!bdrv_co_is_inserted(bs) || bdrv_is_read_only(bs) ||
197
bdrv_is_sg(bs)) {
198
goto early_exit;
199
}
200
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset,
201
BlockDriverState *bs = child->bs;
202
IO_CODE();
203
204
- if (!bs || !bs->drv || !bdrv_is_inserted(bs)) {
205
+ if (!bs || !bs->drv || !bdrv_co_is_inserted(bs)) {
206
return -ENOMEDIUM;
207
}
208
209
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(
210
assert(!(read_flags & BDRV_REQ_NO_WAIT));
211
assert(!(write_flags & BDRV_REQ_NO_WAIT));
212
213
- if (!dst || !dst->bs || !bdrv_is_inserted(dst->bs)) {
214
+ if (!dst || !dst->bs || !bdrv_co_is_inserted(dst->bs)) {
215
return -ENOMEDIUM;
216
}
217
ret = bdrv_check_request32(dst_offset, bytes, NULL, 0);
218
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_copy_range_internal(
219
return bdrv_co_pwrite_zeroes(dst, dst_offset, bytes, write_flags);
220
}
221
222
- if (!src || !src->bs || !bdrv_is_inserted(src->bs)) {
223
+ if (!src || !src->bs || !bdrv_co_is_inserted(src->bs)) {
224
return -ENOMEDIUM;
225
}
226
ret = bdrv_check_request32(src_offset, bytes, NULL, 0);
121
diff --git a/blockdev.c b/blockdev.c
227
diff --git a/blockdev.c b/blockdev.c
122
index XXXXXXX..XXXXXXX 100644
228
index XXXXXXX..XXXXXXX 100644
123
--- a/blockdev.c
229
--- a/blockdev.c
124
+++ b/blockdev.c
230
+++ b/blockdev.c
125
@@ -XXX,XX +XXX,XX @@ typedef struct BlkActionOps {
231
@@ -XXX,XX +XXX,XX @@ fail:
126
struct BlkActionState {
232
static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
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
}
173
174
-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
175
+static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
176
Error **errp)
177
{
233
{
178
BlockDriverState *bs;
234
BlockDriverState *bs;
179
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
235
+ AioContext *aio_context;
180
return bdrv_named_nodes_list(errp);
236
181
}
237
bs = bdrv_lookup_bs(name, name, errp);
182
238
if (bs == NULL) {
183
-BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
239
@@ -XXX,XX +XXX,XX @@ static BlockDriverState *qmp_get_root_bs(const char *name, Error **errp)
184
+BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
240
return NULL;
185
Error **errp)
241
}
186
{
242
187
BlockDriverState *bs;
243
+ aio_context = bdrv_get_aio_context(bs);
188
diff --git a/blockjob.c b/blockjob.c
244
+ aio_context_acquire(aio_context);
189
index XXXXXXX..XXXXXXX 100644
245
+
190
--- a/blockjob.c
246
if (!bdrv_is_inserted(bs)) {
191
+++ b/blockjob.c
247
error_setg(errp, "Device has no medium");
192
@@ -XXX,XX +XXX,XX @@
248
- return NULL;
193
#include "qemu/timer.h"
249
+ bs = NULL;
194
250
}
195
/* Transactional group of block jobs */
251
196
-struct BlockJobTxn {
252
+ aio_context_release(aio_context);
197
+struct JobTxn {
253
+
198
254
return bs;
199
/* Is this txn being cancelled? */
255
}
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
256
404
--
257
--
405
2.13.6
258
2.38.1
406
407
diff view generated by jsdifflib
1
Now that we cancel all jobs and not only block jobs on shutdown, doing
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
that in bdrv_close_all() isn't really appropriate any more. Move the
3
job_cancel_sync_all() call to the callers, and only assert that there
4
are no job running in bdrv_close_all().
5
2
3
The name is not good, not the least because we are going to convert this
4
to a generated co_wrapper, which adds a _co infix after the first part
5
of the name.
6
7
No functional change intended.
8
9
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20230113204212.359076-6-kwolf@redhat.com>
12
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
14
---
8
block.c | 4 +---
15
include/block/block_int-io.h | 2 +-
9
qemu-nbd.c | 8 +++++++-
16
block.c | 8 ++++----
10
vl.c | 1 +
17
block/io.c | 8 +++++---
11
3 files changed, 9 insertions(+), 4 deletions(-)
18
3 files changed, 10 insertions(+), 8 deletions(-)
12
19
20
diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/block_int-io.h
23
+++ b/include/block/block_int-io.h
24
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset,
25
BdrvRequestFlags read_flags,
26
BdrvRequestFlags write_flags);
27
28
-int refresh_total_sectors(BlockDriverState *bs, int64_t hint);
29
+int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint);
30
31
BdrvChild *bdrv_cow_child(BlockDriverState *bs);
32
BdrvChild *bdrv_filter_child(BlockDriverState *bs);
13
diff --git a/block.c b/block.c
33
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
35
--- a/block.c
16
+++ b/block.c
36
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
37
@@ -XXX,XX +XXX,XX @@ static int find_image_format(BlockBackend *file, const char *filename,
18
38
* Set the current 'total_sectors' value
19
void bdrv_close_all(void)
39
* Return 0 on success, -errno on error.
40
*/
41
-int refresh_total_sectors(BlockDriverState *bs, int64_t hint)
42
+int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint)
20
{
43
{
21
- /* TODO We do want to cancel all jobs instead of just block jobs on
44
BlockDriver *drv = bs->drv;
22
- * shutdown, but bdrv_close_all() isn't the right place any more. */
45
IO_CODE();
23
- job_cancel_sync_all();
46
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
24
+ assert(job_next(NULL) == NULL);
47
bs->supported_read_flags |= BDRV_REQ_REGISTERED_BUF;
25
nbd_export_close_all();
48
bs->supported_write_flags |= BDRV_REQ_REGISTERED_BUF;
26
49
27
/* Drop references from requests still in flight, such as canceled block
50
- ret = refresh_total_sectors(bs, bs->total_sectors);
28
diff --git a/qemu-nbd.c b/qemu-nbd.c
51
+ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors);
52
if (ret < 0) {
53
error_setg_errno(errp, -ret, "Could not refresh total sector count");
54
return ret;
55
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_nb_sectors(BlockDriverState *bs)
56
return -ENOMEDIUM;
57
58
if (drv->has_variable_length) {
59
- int ret = refresh_total_sectors(bs, bs->total_sectors);
60
+ int ret = bdrv_refresh_total_sectors(bs, bs->total_sectors);
61
if (ret < 0) {
62
return ret;
63
}
64
@@ -XXX,XX +XXX,XX @@ int bdrv_activate(BlockDriverState *bs, Error **errp)
65
bdrv_dirty_bitmap_skip_store(bm, false);
66
}
67
68
- ret = refresh_total_sectors(bs, bs->total_sectors);
69
+ ret = bdrv_refresh_total_sectors(bs, bs->total_sectors);
70
if (ret < 0) {
71
bs->open_flags |= BDRV_O_INACTIVE;
72
error_setg_errno(errp, -ret, "Could not refresh total sector count");
73
diff --git a/block/io.c b/block/io.c
29
index XXXXXXX..XXXXXXX 100644
74
index XXXXXXX..XXXXXXX 100644
30
--- a/qemu-nbd.c
75
--- a/block/io.c
31
+++ b/qemu-nbd.c
76
+++ b/block/io.c
32
@@ -XXX,XX +XXX,XX @@ static const char *socket_activation_validate_opts(const char *device,
77
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
33
return NULL;
78
goto out;
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
}
79
}
48
bdrv_init();
80
49
- atexit(bdrv_close_all);
81
- ret = refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
50
+ atexit(qemu_nbd_shutdown);
82
+ ret = bdrv_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
51
83
if (ret < 0) {
52
srcpath = argv[optind];
84
error_setg_errno(errp, -ret, "Could not refresh total sector count");
53
if (imageOpts) {
85
} else {
54
diff --git a/vl.c b/vl.c
86
offset = bs->total_sectors * BDRV_SECTOR_SIZE;
55
index XXXXXXX..XXXXXXX 100644
87
}
56
--- a/vl.c
88
- /* It's possible that truncation succeeded but refresh_total_sectors
57
+++ b/vl.c
89
+ /*
58
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
90
+ * It's possible that truncation succeeded but bdrv_refresh_total_sectors
59
/* No more vcpu or device emulation activity beyond this point */
91
* failed, but the latter doesn't affect how we should finish the request.
60
vm_shutdown();
92
- * Pass 0 as the last parameter so that dirty bitmaps etc. are handled. */
61
93
+ * Pass 0 as the last parameter so that dirty bitmaps etc. are handled.
62
+ job_cancel_sync_all();
94
+ */
63
bdrv_close_all();
95
bdrv_co_write_req_finish(child, offset - new_bytes, new_bytes, &req, 0);
64
96
65
res_free();
97
out:
66
--
98
--
67
2.13.6
99
2.38.1
68
69
diff view generated by jsdifflib
1
This moves the top-level job completion and cancellation functions from
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
BlockJob to Job.
3
2
3
BlockDriver->bdrv_getlength is categorized as IO callback, and it
4
currently doesn't run in a coroutine. We should let it take a graph
5
rdlock since the callback traverses the block nodes graph, which however
6
is only possible in a coroutine.
7
8
Therefore turn it into a co_wrapper to move the actual function into a
9
coroutine where the lock can be taken.
10
11
Because now this function creates a new coroutine and polls, we need to
12
take the AioContext lock where it is missing, for the only reason that
13
internally co_wrapper calls AIO_WAIT_WHILE and it expects to release the
14
AioContext lock.
15
16
This is especially messy when a co_wrapper creates a coroutine and polls
17
in bdrv_open_driver, because this function has so many callers in so
18
many context that it can easily lead to deadlocks. Therefore the new
19
rule for bdrv_open_driver is that the caller must always hold the
20
AioContext lock of the given bs (except if it is a coroutine), because
21
the function calls bdrv_refresh_total_sectors() which is now a
22
co_wrapper.
23
24
Once the rwlock is ultimated and placed in every place it needs to be,
25
we will poll using AIO_WAIT_WHILE_UNLOCKED and remove the AioContext
26
lock.
27
28
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
29
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
30
Message-Id: <20230113204212.359076-7-kwolf@redhat.com>
31
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
32
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
33
---
6
include/block/blockjob.h | 55 -----------------------------
34
include/block/block-io.h | 8 ++++--
7
include/block/blockjob_int.h | 18 ----------
35
include/block/block_int-common.h | 2 +-
8
include/qemu/job.h | 68 +++++++++++++++++++++++++++++------
36
include/block/block_int-io.h | 5 +++-
9
block.c | 4 ++-
37
include/sysemu/block-backend-io.h | 10 ++++++--
10
block/backup.c | 3 +-
38
block.c | 32 +++++++++++++++++------
11
block/commit.c | 6 ++--
39
block/blkdebug.c | 6 ++---
12
block/mirror.c | 6 ++--
40
block/blkio.c | 6 ++---
13
block/replication.c | 4 +--
41
block/blklogwrites.c | 6 ++---
14
block/stream.c | 2 +-
42
block/blkreplay.c | 6 ++---
15
blockdev.c | 8 ++---
43
block/blkverify.c | 6 ++---
16
blockjob.c | 76 ---------------------------------------
44
block/block-backend.c | 10 +++++---
17
job.c | 84 +++++++++++++++++++++++++++++++++++++++-----
45
block/commit.c | 4 +--
18
qemu-img.c | 2 +-
46
block/copy-on-read.c | 6 ++---
19
tests/test-bdrv-drain.c | 5 ++-
47
block/crypto.c | 6 ++---
20
tests/test-blockjob-txn.c | 14 ++++----
48
block/curl.c | 10 ++++----
21
tests/test-blockjob.c | 21 ++++++-----
49
block/file-posix.c | 42 +++++++++++++++----------------
22
block/trace-events | 3 --
50
block/file-win32.c | 8 +++---
23
trace-events | 1 +
51
block/filter-compress.c | 6 ++---
24
18 files changed, 171 insertions(+), 209 deletions(-)
52
block/gluster.c | 12 ++++-----
53
block/iscsi.c | 10 ++++----
54
block/mirror.c | 4 +--
55
block/nbd.c | 8 +++---
56
block/null.c | 6 ++---
57
block/nvme.c | 6 ++---
58
block/preallocate.c | 10 ++++----
59
block/qed.c | 4 +--
60
block/quorum.c | 8 +++---
61
block/raw-format.c | 6 ++---
62
block/rbd.c | 4 +--
63
block/replication.c | 6 ++---
64
block/ssh.c | 4 +--
65
block/throttle.c | 6 ++---
66
hw/scsi/scsi-disk.c | 5 ++++
67
tests/unit/test-block-iothread.c | 3 +++
68
block/meson.build | 1 +
69
35 files changed, 161 insertions(+), 121 deletions(-)
25
70
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
71
diff --git a/include/block/block-io.h b/include/block/block-io.h
27
index XXXXXXX..XXXXXXX 100644
72
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
73
--- a/include/block/block-io.h
29
+++ b/include/block/blockjob.h
74
+++ b/include/block/block-io.h
30
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
75
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
31
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
76
PreallocMode prealloc, BdrvRequestFlags flags,
32
77
Error **errp);
33
/**
78
34
- * block_job_cancel:
79
-int64_t bdrv_nb_sectors(BlockDriverState *bs);
35
- * @job: The job to be canceled.
80
-int64_t bdrv_getlength(BlockDriverState *bs);
36
- * @force: Quit a job without waiting for data to be in sync.
81
+int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs);
37
- *
82
+int64_t co_wrapper_mixed bdrv_nb_sectors(BlockDriverState *bs);
38
- * Asynchronously cancel the specified job.
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
*/
163
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
164
/** The @job could not be started, free it. */
165
void job_early_fail(Job *job);
166
167
+/**
168
+ * @job: The job being completed.
169
+ * @ret: The status code.
170
+ *
171
+ * Marks @job as completed. If @ret is non-zero, the job transaction it is part
172
+ * of is aborted. If @ret is zero, the job moves into the WAITING state. If it
173
+ * is the last job to complete in its transaction, all jobs in the transaction
174
+ * move from WAITING to PENDING.
175
+ */
176
+void job_completed(Job *job, int ret);
177
+
83
+
178
/** Asynchronously complete the specified @job. */
84
+int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs);
179
-void job_complete(Job *job, Error **errp);;
85
+int64_t co_wrapper_mixed bdrv_getlength(BlockDriverState *bs);
180
+void job_complete(Job *job, Error **errp);
181
+
86
+
182
+/**
87
int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
183
+ * Asynchronously cancel the specified @job. If @force is true, the job should
88
BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
184
+ * be cancelled immediately without waiting for a consistent state.
89
BlockDriverState *in_bs, Error **errp);
185
+ */
90
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
186
+void job_cancel(Job *job, bool force);
91
index XXXXXXX..XXXXXXX 100644
92
--- a/include/block/block_int-common.h
93
+++ b/include/block/block_int-common.h
94
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
95
int coroutine_fn (*bdrv_co_truncate)(BlockDriverState *bs, int64_t offset,
96
bool exact, PreallocMode prealloc,
97
BdrvRequestFlags flags, Error **errp);
98
- int64_t (*bdrv_getlength)(BlockDriverState *bs);
99
+ int64_t coroutine_fn (*bdrv_co_getlength)(BlockDriverState *bs);
100
int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
101
BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs,
102
Error **errp);
103
diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/block_int-io.h
106
+++ b/include/block/block_int-io.h
107
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_copy_range_to(BdrvChild *src, int64_t src_offset,
108
BdrvRequestFlags read_flags,
109
BdrvRequestFlags write_flags);
110
111
-int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint);
112
+int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs,
113
+ int64_t hint);
114
+int co_wrapper_mixed
115
+bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint);
116
117
BdrvChild *bdrv_cow_child(BlockDriverState *bs);
118
BdrvChild *bdrv_filter_child(BlockDriverState *bs);
119
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
120
index XXXXXXX..XXXXXXX 100644
121
--- a/include/sysemu/block-backend-io.h
122
+++ b/include/sysemu/block-backend-io.h
123
@@ -XXX,XX +XXX,XX @@ bool co_wrapper_mixed blk_is_inserted(BlockBackend *blk);
124
bool blk_is_available(BlockBackend *blk);
125
void blk_lock_medium(BlockBackend *blk, bool locked);
126
void blk_eject(BlockBackend *blk, bool eject_flag);
127
-int64_t blk_getlength(BlockBackend *blk);
187
+
128
+
188
+/**
129
+int64_t coroutine_fn blk_co_getlength(BlockBackend *blk);
189
+ * Cancels the specified job like job_cancel(), but may refuse to do so if the
130
+int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk);
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
+
131
+
194
+/**
132
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr);
195
+ * Synchronously cancel the @job. The completion callback is called
133
-int64_t blk_nb_sectors(BlockBackend *blk);
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
+
134
+
205
+/** Synchronously cancels all jobs using job_cancel_sync(). */
135
+int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk);
206
+void job_cancel_sync_all(void);
136
+int64_t co_wrapper_mixed blk_nb_sectors(BlockBackend *blk);
207
+
137
+
208
+/**
138
void *blk_try_blockalign(BlockBackend *blk, size_t size);
209
+ * @job: The job to be completed.
139
void *blk_blockalign(BlockBackend *blk, size_t size);
210
+ * @errp: Error object which may be set by job_complete(); this is not
140
bool blk_is_writable(BlockBackend *blk);
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
141
diff --git a/block.c b/block.c
237
index XXXXXXX..XXXXXXX 100644
142
index XXXXXXX..XXXXXXX 100644
238
--- a/block.c
143
--- a/block.c
239
+++ b/block.c
144
+++ b/block.c
240
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
145
@@ -XXX,XX +XXX,XX @@ static int find_image_format(BlockBackend *file, const char *filename,
241
146
* Set the current 'total_sectors' value
242
void bdrv_close_all(void)
147
* Return 0 on success, -errno on error.
243
{
148
*/
244
- block_job_cancel_sync_all();
149
-int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint)
245
+ /* TODO We do want to cancel all jobs instead of just block jobs on
150
+int coroutine_fn bdrv_co_refresh_total_sectors(BlockDriverState *bs,
246
+ * shutdown, but bdrv_close_all() isn't the right place any more. */
151
+ int64_t hint)
247
+ job_cancel_sync_all();
152
{
248
nbd_export_close_all();
153
BlockDriver *drv = bs->drv;
249
154
IO_CODE();
250
/* Drop references from requests still in flight, such as canceled block
155
@@ -XXX,XX +XXX,XX @@ int bdrv_refresh_total_sectors(BlockDriverState *bs, int64_t hint)
251
diff --git a/block/backup.c b/block/backup.c
156
return -ENOMEDIUM;
252
index XXXXXXX..XXXXXXX 100644
157
}
253
--- a/block/backup.c
158
254
+++ b/block/backup.c
159
- /* Do not attempt drv->bdrv_getlength() on scsi-generic devices */
255
@@ -XXX,XX +XXX,XX @@ typedef struct {
160
+ /* Do not attempt drv->bdrv_co_getlength() on scsi-generic devices */
256
161
if (bdrv_is_sg(bs))
257
static void backup_complete(Job *job, void *opaque)
162
return 0;
258
{
163
259
- BlockJob *bjob = container_of(job, BlockJob, job);
164
/* query actual device if possible, otherwise just trust the hint */
260
BackupCompleteData *data = opaque;
165
- if (drv->bdrv_getlength) {
261
166
- int64_t length = drv->bdrv_getlength(bs);
262
- block_job_completed(bjob, data->ret);
167
+ if (drv->bdrv_co_getlength) {
263
+ job_completed(job, data->ret);
168
+ int64_t length = drv->bdrv_co_getlength(bs);
264
g_free(data);
169
if (length < 0) {
265
}
170
return length;
266
171
}
172
@@ -XXX,XX +XXX,XX @@ out:
173
g_free(gen_node_name);
174
}
175
176
+/*
177
+ * The caller must always hold @bs AioContext lock, because this function calls
178
+ * bdrv_refresh_total_sectors() which polls when called from non-coroutine
179
+ * context.
180
+ */
181
static int bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv,
182
const char *node_name, QDict *options,
183
int open_flags, Error **errp)
184
@@ -XXX,XX +XXX,XX @@ out:
185
* The reference parameter may be used to specify an existing block device which
186
* should be opened. If specified, neither options nor a filename may be given,
187
* nor can an existing BDS be reused (that is, *pbs has to be NULL).
188
+ *
189
+ * The caller must always hold @filename AioContext lock, because this
190
+ * function eventually calls bdrv_refresh_total_sectors() which polls
191
+ * when called from non-coroutine context.
192
*/
193
static BlockDriverState *bdrv_open_inherit(const char *filename,
194
const char *reference,
195
@@ -XXX,XX +XXX,XX @@ close_and_fail:
196
return NULL;
197
}
198
199
+/*
200
+ * The caller must always hold @filename AioContext lock, because this
201
+ * function eventually calls bdrv_refresh_total_sectors() which polls
202
+ * when called from non-coroutine context.
203
+ */
204
BlockDriverState *bdrv_open(const char *filename, const char *reference,
205
QDict *options, int flags, Error **errp)
206
{
207
@@ -XXX,XX +XXX,XX @@ BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
208
/**
209
* Return number of sectors on success, -errno on error.
210
*/
211
-int64_t bdrv_nb_sectors(BlockDriverState *bs)
212
+int64_t coroutine_fn bdrv_co_nb_sectors(BlockDriverState *bs)
213
{
214
BlockDriver *drv = bs->drv;
215
IO_CODE();
216
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_nb_sectors(BlockDriverState *bs)
217
return -ENOMEDIUM;
218
219
if (drv->has_variable_length) {
220
- int ret = bdrv_refresh_total_sectors(bs, bs->total_sectors);
221
+ int ret = bdrv_co_refresh_total_sectors(bs, bs->total_sectors);
222
if (ret < 0) {
223
return ret;
224
}
225
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_nb_sectors(BlockDriverState *bs)
226
* Return length in bytes on success, -errno on error.
227
* The length is always a multiple of BDRV_SECTOR_SIZE.
228
*/
229
-int64_t bdrv_getlength(BlockDriverState *bs)
230
+int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs)
231
{
232
- int64_t ret = bdrv_nb_sectors(bs);
233
+ int64_t ret;
234
IO_CODE();
235
236
+ ret = bdrv_co_nb_sectors(bs);
237
if (ret < 0) {
238
return ret;
239
}
240
diff --git a/block/blkdebug.c b/block/blkdebug.c
241
index XXXXXXX..XXXXXXX 100644
242
--- a/block/blkdebug.c
243
+++ b/block/blkdebug.c
244
@@ -XXX,XX +XXX,XX @@ static bool blkdebug_debug_is_suspended(BlockDriverState *bs, const char *tag)
245
return false;
246
}
247
248
-static int64_t blkdebug_getlength(BlockDriverState *bs)
249
+static int64_t coroutine_fn blkdebug_co_getlength(BlockDriverState *bs)
250
{
251
- return bdrv_getlength(bs->file->bs);
252
+ return bdrv_co_getlength(bs->file->bs);
253
}
254
255
static void blkdebug_refresh_filename(BlockDriverState *bs)
256
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
257
.bdrv_reopen_prepare = blkdebug_reopen_prepare,
258
.bdrv_child_perm = blkdebug_child_perm,
259
260
- .bdrv_getlength = blkdebug_getlength,
261
+ .bdrv_co_getlength = blkdebug_co_getlength,
262
.bdrv_refresh_filename = blkdebug_refresh_filename,
263
.bdrv_refresh_limits = blkdebug_refresh_limits,
264
265
diff --git a/block/blkio.c b/block/blkio.c
266
index XXXXXXX..XXXXXXX 100644
267
--- a/block/blkio.c
268
+++ b/block/blkio.c
269
@@ -XXX,XX +XXX,XX @@ static void blkio_close(BlockDriverState *bs)
270
}
271
}
272
273
-static int64_t blkio_getlength(BlockDriverState *bs)
274
+static int64_t coroutine_fn blkio_co_getlength(BlockDriverState *bs)
275
{
276
BDRVBlkioState *s = bs->opaque;
277
uint64_t capacity;
278
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset,
279
return -ENOTSUP;
280
}
281
282
- current_length = blkio_getlength(bs);
283
+ current_length = blkio_co_getlength(bs);
284
285
if (offset > current_length) {
286
error_setg(errp, "Cannot grow device");
287
@@ -XXX,XX +XXX,XX @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp)
288
.instance_size = sizeof(BDRVBlkioState), \
289
.bdrv_file_open = blkio_file_open, \
290
.bdrv_close = blkio_close, \
291
- .bdrv_getlength = blkio_getlength, \
292
+ .bdrv_co_getlength = blkio_co_getlength, \
293
.bdrv_co_truncate = blkio_truncate, \
294
.bdrv_get_info = blkio_get_info, \
295
.bdrv_attach_aio_context = blkio_attach_aio_context, \
296
diff --git a/block/blklogwrites.c b/block/blklogwrites.c
297
index XXXXXXX..XXXXXXX 100644
298
--- a/block/blklogwrites.c
299
+++ b/block/blklogwrites.c
300
@@ -XXX,XX +XXX,XX @@ static void blk_log_writes_close(BlockDriverState *bs)
301
s->log_file = NULL;
302
}
303
304
-static int64_t blk_log_writes_getlength(BlockDriverState *bs)
305
+static int64_t coroutine_fn blk_log_writes_co_getlength(BlockDriverState *bs)
306
{
307
- return bdrv_getlength(bs->file->bs);
308
+ return bdrv_co_getlength(bs->file->bs);
309
}
310
311
static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c,
312
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blk_log_writes = {
313
314
.bdrv_open = blk_log_writes_open,
315
.bdrv_close = blk_log_writes_close,
316
- .bdrv_getlength = blk_log_writes_getlength,
317
+ .bdrv_co_getlength = blk_log_writes_co_getlength,
318
.bdrv_child_perm = blk_log_writes_child_perm,
319
.bdrv_refresh_limits = blk_log_writes_refresh_limits,
320
321
diff --git a/block/blkreplay.c b/block/blkreplay.c
322
index XXXXXXX..XXXXXXX 100644
323
--- a/block/blkreplay.c
324
+++ b/block/blkreplay.c
325
@@ -XXX,XX +XXX,XX @@ fail:
326
return ret;
327
}
328
329
-static int64_t blkreplay_getlength(BlockDriverState *bs)
330
+static int64_t coroutine_fn blkreplay_co_getlength(BlockDriverState *bs)
331
{
332
- return bdrv_getlength(bs->file->bs);
333
+ return bdrv_co_getlength(bs->file->bs);
334
}
335
336
/* This bh is used for synchronization of return from coroutines.
337
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkreplay = {
338
339
.bdrv_open = blkreplay_open,
340
.bdrv_child_perm = bdrv_default_perms,
341
- .bdrv_getlength = blkreplay_getlength,
342
+ .bdrv_co_getlength = blkreplay_co_getlength,
343
344
.bdrv_co_preadv = blkreplay_co_preadv,
345
.bdrv_co_pwritev = blkreplay_co_pwritev,
346
diff --git a/block/blkverify.c b/block/blkverify.c
347
index XXXXXXX..XXXXXXX 100644
348
--- a/block/blkverify.c
349
+++ b/block/blkverify.c
350
@@ -XXX,XX +XXX,XX @@ static void blkverify_close(BlockDriverState *bs)
351
s->test_file = NULL;
352
}
353
354
-static int64_t blkverify_getlength(BlockDriverState *bs)
355
+static int64_t coroutine_fn blkverify_co_getlength(BlockDriverState *bs)
356
{
357
BDRVBlkverifyState *s = bs->opaque;
358
359
- return bdrv_getlength(s->test_file->bs);
360
+ return bdrv_co_getlength(s->test_file->bs);
361
}
362
363
static void coroutine_fn blkverify_do_test_req(void *opaque)
364
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkverify = {
365
.bdrv_file_open = blkverify_open,
366
.bdrv_close = blkverify_close,
367
.bdrv_child_perm = bdrv_default_perms,
368
- .bdrv_getlength = blkverify_getlength,
369
+ .bdrv_co_getlength = blkverify_co_getlength,
370
.bdrv_refresh_filename = blkverify_refresh_filename,
371
.bdrv_dirname = blkverify_dirname,
372
373
diff --git a/block/block-backend.c b/block/block-backend.c
374
index XXXXXXX..XXXXXXX 100644
375
--- a/block/block-backend.c
376
+++ b/block/block-backend.c
377
@@ -XXX,XX +XXX,XX @@ BlockAIOCB *blk_aio_pwrite_zeroes(BlockBackend *blk, int64_t offset,
378
flags | BDRV_REQ_ZERO_WRITE, cb, opaque);
379
}
380
381
-int64_t blk_getlength(BlockBackend *blk)
382
+int64_t coroutine_fn blk_co_getlength(BlockBackend *blk)
383
{
384
IO_CODE();
385
+
386
if (!blk_is_available(blk)) {
387
return -ENOMEDIUM;
388
}
389
390
- return bdrv_getlength(blk_bs(blk));
391
+ return bdrv_co_getlength(blk_bs(blk));
392
}
393
394
void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
395
@@ -XXX,XX +XXX,XX @@ void blk_get_geometry(BlockBackend *blk, uint64_t *nb_sectors_ptr)
396
}
397
}
398
399
-int64_t blk_nb_sectors(BlockBackend *blk)
400
+int64_t coroutine_fn blk_co_nb_sectors(BlockBackend *blk)
401
{
402
IO_CODE();
403
+
404
if (!blk_is_available(blk)) {
405
return -ENOMEDIUM;
406
}
407
408
- return bdrv_nb_sectors(blk_bs(blk));
409
+ return bdrv_co_nb_sectors(blk_bs(blk));
410
}
411
412
BlockAIOCB *blk_aio_preadv(BlockBackend *blk, int64_t offset,
267
diff --git a/block/commit.c b/block/commit.c
413
diff --git a/block/commit.c b/block/commit.c
268
index XXXXXXX..XXXXXXX 100644
414
index XXXXXXX..XXXXXXX 100644
269
--- a/block/commit.c
415
--- a/block/commit.c
270
+++ b/block/commit.c
416
+++ b/block/commit.c
271
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
417
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
272
blk_unref(s->top);
418
QEMU_AUTO_VFREE void *buf = NULL;
273
419
int64_t len, base_len;
274
/* If there is more than one reference to the job (e.g. if called from
420
275
- * job_finish_sync()), block_job_completed() won't free it and therefore
421
- len = blk_getlength(s->top);
276
- * the blockers on the intermediate nodes remain. This would cause
422
+ len = blk_co_getlength(s->top);
277
+ * job_finish_sync()), job_completed() won't free it and therefore the
423
if (len < 0) {
278
+ * blockers on the intermediate nodes remain. This would cause
424
return len;
279
* bdrv_set_backing_hd() to fail. */
425
}
280
block_job_remove_all_bdrv(bjob);
426
job_progress_set_remaining(&s->common.job, len);
281
427
282
- block_job_completed(&s->common, ret);
428
- base_len = blk_getlength(s->base);
283
+ job_completed(job, ret);
429
+ base_len = blk_co_getlength(s->base);
284
g_free(data);
430
if (base_len < 0) {
285
431
return base_len;
286
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
432
}
433
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
434
index XXXXXXX..XXXXXXX 100644
435
--- a/block/copy-on-read.c
436
+++ b/block/copy-on-read.c
437
@@ -XXX,XX +XXX,XX @@ static void cor_child_perm(BlockDriverState *bs, BdrvChild *c,
438
}
439
440
441
-static int64_t cor_getlength(BlockDriverState *bs)
442
+static int64_t coroutine_fn cor_co_getlength(BlockDriverState *bs)
443
{
444
- return bdrv_getlength(bs->file->bs);
445
+ return bdrv_co_getlength(bs->file->bs);
446
}
447
448
449
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
450
.bdrv_close = cor_close,
451
.bdrv_child_perm = cor_child_perm,
452
453
- .bdrv_getlength = cor_getlength,
454
+ .bdrv_co_getlength = cor_co_getlength,
455
456
.bdrv_co_preadv_part = cor_co_preadv_part,
457
.bdrv_co_pwritev_part = cor_co_pwritev_part,
458
diff --git a/block/crypto.c b/block/crypto.c
459
index XXXXXXX..XXXXXXX 100644
460
--- a/block/crypto.c
461
+++ b/block/crypto.c
462
@@ -XXX,XX +XXX,XX @@ static void block_crypto_refresh_limits(BlockDriverState *bs, Error **errp)
463
}
464
465
466
-static int64_t block_crypto_getlength(BlockDriverState *bs)
467
+static int64_t coroutine_fn block_crypto_co_getlength(BlockDriverState *bs)
468
{
469
BlockCrypto *crypto = bs->opaque;
470
- int64_t len = bdrv_getlength(bs->file->bs);
471
+ int64_t len = bdrv_co_getlength(bs->file->bs);
472
473
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
474
assert(offset < INT64_MAX);
475
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = {
476
.bdrv_refresh_limits = block_crypto_refresh_limits,
477
.bdrv_co_preadv = block_crypto_co_preadv,
478
.bdrv_co_pwritev = block_crypto_co_pwritev,
479
- .bdrv_getlength = block_crypto_getlength,
480
+ .bdrv_co_getlength = block_crypto_co_getlength,
481
.bdrv_measure = block_crypto_measure,
482
.bdrv_get_info = block_crypto_get_info_luks,
483
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
484
diff --git a/block/curl.c b/block/curl.c
485
index XXXXXXX..XXXXXXX 100644
486
--- a/block/curl.c
487
+++ b/block/curl.c
488
@@ -XXX,XX +XXX,XX @@ static void curl_close(BlockDriverState *bs)
489
g_free(s->proxypassword);
490
}
491
492
-static int64_t curl_getlength(BlockDriverState *bs)
493
+static int64_t coroutine_fn curl_co_getlength(BlockDriverState *bs)
494
{
495
BDRVCURLState *s = bs->opaque;
496
return s->len;
497
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_http = {
498
.bdrv_parse_filename = curl_parse_filename,
499
.bdrv_file_open = curl_open,
500
.bdrv_close = curl_close,
501
- .bdrv_getlength = curl_getlength,
502
+ .bdrv_co_getlength = curl_co_getlength,
503
504
.bdrv_co_preadv = curl_co_preadv,
505
506
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_https = {
507
.bdrv_parse_filename = curl_parse_filename,
508
.bdrv_file_open = curl_open,
509
.bdrv_close = curl_close,
510
- .bdrv_getlength = curl_getlength,
511
+ .bdrv_co_getlength = curl_co_getlength,
512
513
.bdrv_co_preadv = curl_co_preadv,
514
515
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ftp = {
516
.bdrv_parse_filename = curl_parse_filename,
517
.bdrv_file_open = curl_open,
518
.bdrv_close = curl_close,
519
- .bdrv_getlength = curl_getlength,
520
+ .bdrv_co_getlength = curl_co_getlength,
521
522
.bdrv_co_preadv = curl_co_preadv,
523
524
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ftps = {
525
.bdrv_parse_filename = curl_parse_filename,
526
.bdrv_file_open = curl_open,
527
.bdrv_close = curl_close,
528
- .bdrv_getlength = curl_getlength,
529
+ .bdrv_co_getlength = curl_co_getlength,
530
531
.bdrv_co_preadv = curl_co_preadv,
532
533
diff --git a/block/file-posix.c b/block/file-posix.c
534
index XXXXXXX..XXXXXXX 100644
535
--- a/block/file-posix.c
536
+++ b/block/file-posix.c
537
@@ -XXX,XX +XXX,XX @@ static int fd_open(BlockDriverState *bs)
538
return -EIO;
539
}
540
541
-static int64_t raw_getlength(BlockDriverState *bs);
542
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs);
543
544
typedef struct RawPosixAIOData {
545
BlockDriverState *bs;
546
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
547
}
548
549
if (S_ISCHR(st.st_mode) || S_ISBLK(st.st_mode)) {
550
- int64_t cur_length = raw_getlength(bs);
551
+ int64_t cur_length = raw_co_getlength(bs);
552
553
if (offset != cur_length && exact) {
554
error_setg(errp, "Cannot resize device files");
555
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
556
}
557
558
#ifdef __OpenBSD__
559
-static int64_t raw_getlength(BlockDriverState *bs)
560
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
561
{
562
BDRVRawState *s = bs->opaque;
563
int fd = s->fd;
564
@@ -XXX,XX +XXX,XX @@ static int64_t raw_getlength(BlockDriverState *bs)
565
return st.st_size;
566
}
567
#elif defined(__NetBSD__)
568
-static int64_t raw_getlength(BlockDriverState *bs)
569
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
570
{
571
BDRVRawState *s = bs->opaque;
572
int fd = s->fd;
573
@@ -XXX,XX +XXX,XX @@ static int64_t raw_getlength(BlockDriverState *bs)
574
return st.st_size;
575
}
576
#elif defined(__sun__)
577
-static int64_t raw_getlength(BlockDriverState *bs)
578
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
579
{
580
BDRVRawState *s = bs->opaque;
581
struct dk_minfo minfo;
582
@@ -XXX,XX +XXX,XX @@ static int64_t raw_getlength(BlockDriverState *bs)
583
return size;
584
}
585
#elif defined(CONFIG_BSD)
586
-static int64_t raw_getlength(BlockDriverState *bs)
587
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
588
{
589
BDRVRawState *s = bs->opaque;
590
int fd = s->fd;
591
@@ -XXX,XX +XXX,XX @@ again:
592
return size;
593
}
594
#else
595
-static int64_t raw_getlength(BlockDriverState *bs)
596
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
597
{
598
BDRVRawState *s = bs->opaque;
599
int ret;
600
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
601
* round up if necessary.
602
*/
603
if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) {
604
- int64_t file_length = raw_getlength(bs);
605
+ int64_t file_length = raw_co_getlength(bs);
606
if (file_length > 0) {
607
/* Ignore errors, this is just a safeguard */
608
assert(hole == file_length);
609
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs,
610
611
#if defined(__linux__)
612
/* Verify that the file is not in the page cache */
613
-static void check_cache_dropped(BlockDriverState *bs, Error **errp)
614
+static void coroutine_fn check_cache_dropped(BlockDriverState *bs, Error **errp)
615
{
616
const size_t window_size = 128 * 1024 * 1024;
617
BDRVRawState *s = bs->opaque;
618
@@ -XXX,XX +XXX,XX @@ static void check_cache_dropped(BlockDriverState *bs, Error **errp)
619
page_size = sysconf(_SC_PAGESIZE);
620
vec = g_malloc(DIV_ROUND_UP(window_size, page_size));
621
622
- end = raw_getlength(bs);
623
+ end = raw_co_getlength(bs);
624
625
for (offset = 0; offset < end; offset += window_size) {
626
void *new_window;
627
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
628
.bdrv_co_io_unplug = raw_co_io_unplug,
629
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
630
631
- .bdrv_co_truncate = raw_co_truncate,
632
- .bdrv_getlength = raw_getlength,
633
+ .bdrv_co_truncate = raw_co_truncate,
634
+ .bdrv_co_getlength = raw_co_getlength,
635
.bdrv_get_info = raw_get_info,
636
.bdrv_get_allocated_file_size
637
= raw_get_allocated_file_size,
638
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
639
.bdrv_co_io_unplug = raw_co_io_unplug,
640
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
641
642
- .bdrv_co_truncate = raw_co_truncate,
643
- .bdrv_getlength    = raw_getlength,
644
+ .bdrv_co_truncate = raw_co_truncate,
645
+ .bdrv_co_getlength = raw_co_getlength,
646
.bdrv_get_info = raw_get_info,
647
.bdrv_get_allocated_file_size
648
= raw_get_allocated_file_size,
649
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
650
.bdrv_co_io_unplug = raw_co_io_unplug,
651
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
652
653
- .bdrv_co_truncate = raw_co_truncate,
654
- .bdrv_getlength = raw_getlength,
655
- .has_variable_length = true,
656
+ .bdrv_co_truncate = raw_co_truncate,
657
+ .bdrv_co_getlength = raw_co_getlength,
658
+ .has_variable_length = true,
659
.bdrv_get_allocated_file_size
660
= raw_get_allocated_file_size,
661
662
@@ -XXX,XX +XXX,XX @@ static int cdrom_reopen(BlockDriverState *bs)
663
664
static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs)
665
{
666
- return raw_getlength(bs) > 0;
667
+ return raw_co_getlength(bs) > 0;
668
}
669
670
static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
671
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
672
.bdrv_co_io_unplug = raw_co_io_unplug,
673
.bdrv_attach_aio_context = raw_aio_attach_aio_context,
674
675
- .bdrv_co_truncate = raw_co_truncate,
676
- .bdrv_getlength = raw_getlength,
677
- .has_variable_length = true,
678
+ .bdrv_co_truncate = raw_co_truncate,
679
+ .bdrv_co_getlength = raw_co_getlength,
680
+ .has_variable_length = true,
681
.bdrv_get_allocated_file_size
682
= raw_get_allocated_file_size,
683
684
diff --git a/block/file-win32.c b/block/file-win32.c
685
index XXXXXXX..XXXXXXX 100644
686
--- a/block/file-win32.c
687
+++ b/block/file-win32.c
688
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
689
return 0;
690
}
691
692
-static int64_t raw_getlength(BlockDriverState *bs)
693
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
694
{
695
BDRVRawState *s = bs->opaque;
696
LARGE_INTEGER l;
697
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
698
.bdrv_aio_flush = raw_aio_flush,
699
700
.bdrv_co_truncate = raw_co_truncate,
701
- .bdrv_getlength    = raw_getlength,
702
+ .bdrv_co_getlength = raw_co_getlength,
703
.bdrv_get_allocated_file_size
704
= raw_get_allocated_file_size,
705
706
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
707
.bdrv_detach_aio_context = raw_detach_aio_context,
708
.bdrv_attach_aio_context = raw_attach_aio_context,
709
710
- .bdrv_getlength = raw_getlength,
711
- .has_variable_length = true,
712
+ .bdrv_co_getlength = raw_co_getlength,
713
+ .has_variable_length = true,
714
715
.bdrv_get_allocated_file_size
716
= raw_get_allocated_file_size,
717
diff --git a/block/filter-compress.c b/block/filter-compress.c
718
index XXXXXXX..XXXXXXX 100644
719
--- a/block/filter-compress.c
720
+++ b/block/filter-compress.c
721
@@ -XXX,XX +XXX,XX @@ static int compress_open(BlockDriverState *bs, QDict *options, int flags,
722
}
723
724
725
-static int64_t compress_getlength(BlockDriverState *bs)
726
+static int64_t coroutine_fn compress_co_getlength(BlockDriverState *bs)
727
{
728
- return bdrv_getlength(bs->file->bs);
729
+ return bdrv_co_getlength(bs->file->bs);
730
}
731
732
733
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_compress = {
734
.bdrv_open = compress_open,
735
.bdrv_child_perm = bdrv_default_perms,
736
737
- .bdrv_getlength = compress_getlength,
738
+ .bdrv_co_getlength = compress_co_getlength,
739
740
.bdrv_co_preadv_part = compress_co_preadv_part,
741
.bdrv_co_pwritev_part = compress_co_pwritev_part,
742
diff --git a/block/gluster.c b/block/gluster.c
743
index XXXXXXX..XXXXXXX 100644
744
--- a/block/gluster.c
745
+++ b/block/gluster.c
746
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qemu_gluster_co_pdiscard(BlockDriverState *bs,
747
}
748
#endif
749
750
-static int64_t qemu_gluster_getlength(BlockDriverState *bs)
751
+static int64_t coroutine_fn qemu_gluster_co_getlength(BlockDriverState *bs)
752
{
753
BDRVGlusterState *s = bs->opaque;
754
int64_t ret;
755
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs,
756
* round up if necessary.
757
*/
758
if (!QEMU_IS_ALIGNED(*pnum, bs->bl.request_alignment)) {
759
- int64_t file_length = qemu_gluster_getlength(bs);
760
+ int64_t file_length = qemu_gluster_co_getlength(bs);
761
if (file_length > 0) {
762
/* Ignore errors, this is just a safeguard */
763
assert(hole == file_length);
764
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster = {
765
.bdrv_close = qemu_gluster_close,
766
.bdrv_co_create = qemu_gluster_co_create,
767
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
768
- .bdrv_getlength = qemu_gluster_getlength,
769
+ .bdrv_co_getlength = qemu_gluster_co_getlength,
770
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
771
.bdrv_co_truncate = qemu_gluster_co_truncate,
772
.bdrv_co_readv = qemu_gluster_co_readv,
773
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_tcp = {
774
.bdrv_close = qemu_gluster_close,
775
.bdrv_co_create = qemu_gluster_co_create,
776
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
777
- .bdrv_getlength = qemu_gluster_getlength,
778
+ .bdrv_co_getlength = qemu_gluster_co_getlength,
779
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
780
.bdrv_co_truncate = qemu_gluster_co_truncate,
781
.bdrv_co_readv = qemu_gluster_co_readv,
782
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_unix = {
783
.bdrv_close = qemu_gluster_close,
784
.bdrv_co_create = qemu_gluster_co_create,
785
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
786
- .bdrv_getlength = qemu_gluster_getlength,
787
+ .bdrv_co_getlength = qemu_gluster_co_getlength,
788
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
789
.bdrv_co_truncate = qemu_gluster_co_truncate,
790
.bdrv_co_readv = qemu_gluster_co_readv,
791
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_rdma = {
792
.bdrv_close = qemu_gluster_close,
793
.bdrv_co_create = qemu_gluster_co_create,
794
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
795
- .bdrv_getlength = qemu_gluster_getlength,
796
+ .bdrv_co_getlength = qemu_gluster_co_getlength,
797
.bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
798
.bdrv_co_truncate = qemu_gluster_co_truncate,
799
.bdrv_co_readv = qemu_gluster_co_readv,
800
diff --git a/block/iscsi.c b/block/iscsi.c
801
index XXXXXXX..XXXXXXX 100644
802
--- a/block/iscsi.c
803
+++ b/block/iscsi.c
804
@@ -XXX,XX +XXX,XX @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs,
805
806
#endif
807
808
-static int64_t
809
-iscsi_getlength(BlockDriverState *bs)
810
+static int64_t coroutine_fn
811
+iscsi_co_getlength(BlockDriverState *bs)
812
{
813
IscsiLun *iscsilun = bs->opaque;
814
int64_t len;
815
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
816
return -EIO;
817
}
818
819
- cur_length = iscsi_getlength(bs);
820
+ cur_length = iscsi_co_getlength(bs);
821
if (offset != cur_length && exact) {
822
error_setg(errp, "Cannot resize iSCSI devices");
823
return -ENOTSUP;
824
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_iscsi = {
825
.bdrv_reopen_commit = iscsi_reopen_commit,
826
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
827
828
- .bdrv_getlength = iscsi_getlength,
829
+ .bdrv_co_getlength = iscsi_co_getlength,
830
.bdrv_get_info = iscsi_get_info,
831
.bdrv_co_truncate = iscsi_co_truncate,
832
.bdrv_refresh_limits = iscsi_refresh_limits,
833
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_iser = {
834
.bdrv_reopen_commit = iscsi_reopen_commit,
835
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
836
837
- .bdrv_getlength = iscsi_getlength,
838
+ .bdrv_co_getlength = iscsi_co_getlength,
839
.bdrv_get_info = iscsi_get_info,
840
.bdrv_co_truncate = iscsi_co_truncate,
841
.bdrv_refresh_limits = iscsi_refresh_limits,
287
diff --git a/block/mirror.c b/block/mirror.c
842
diff --git a/block/mirror.c b/block/mirror.c
288
index XXXXXXX..XXXXXXX 100644
843
index XXXXXXX..XXXXXXX 100644
289
--- a/block/mirror.c
844
--- a/block/mirror.c
290
+++ b/block/mirror.c
845
+++ b/block/mirror.c
291
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
846
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
292
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
847
goto immediate_exit;
293
848
}
294
/* Make sure that the source BDS doesn't go away before we called
849
295
- * block_job_completed(). */
850
- s->bdev_length = bdrv_getlength(bs);
296
+ * job_completed(). */
851
+ s->bdev_length = bdrv_co_getlength(bs);
297
bdrv_ref(src);
852
if (s->bdev_length < 0) {
298
bdrv_ref(mirror_top_bs);
853
ret = s->bdev_length;
299
bdrv_ref(target_bs);
854
goto immediate_exit;
300
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
855
}
301
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
856
302
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
857
- target_length = blk_getlength(s->target);
303
858
+ target_length = blk_co_getlength(s->target);
304
- block_job_completed(&s->common, data->ret);
859
if (target_length < 0) {
305
+ job_completed(job, data->ret);
860
ret = target_length;
306
861
goto immediate_exit;
307
g_free(data);
862
diff --git a/block/nbd.c b/block/nbd.c
308
bdrv_drained_end(src);
863
index XXXXXXX..XXXXXXX 100644
309
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(Job *job, Error **errp)
864
--- a/block/nbd.c
310
}
865
+++ b/block/nbd.c
311
866
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nbd_co_truncate(BlockDriverState *bs, int64_t offset,
312
s->should_complete = true;
867
return 0;
313
- block_job_enter(&s->common);
868
}
314
+ job_enter(job);
869
315
}
870
-static int64_t nbd_getlength(BlockDriverState *bs)
316
871
+static int64_t coroutine_fn nbd_co_getlength(BlockDriverState *bs)
317
static void mirror_pause(Job *job)
872
{
873
BDRVNBDState *s = bs->opaque;
874
875
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nbd = {
876
.bdrv_co_pdiscard = nbd_client_co_pdiscard,
877
.bdrv_refresh_limits = nbd_refresh_limits,
878
.bdrv_co_truncate = nbd_co_truncate,
879
- .bdrv_getlength = nbd_getlength,
880
+ .bdrv_co_getlength = nbd_co_getlength,
881
.bdrv_refresh_filename = nbd_refresh_filename,
882
.bdrv_co_block_status = nbd_client_co_block_status,
883
.bdrv_dirname = nbd_dirname,
884
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nbd_tcp = {
885
.bdrv_co_pdiscard = nbd_client_co_pdiscard,
886
.bdrv_refresh_limits = nbd_refresh_limits,
887
.bdrv_co_truncate = nbd_co_truncate,
888
- .bdrv_getlength = nbd_getlength,
889
+ .bdrv_co_getlength = nbd_co_getlength,
890
.bdrv_refresh_filename = nbd_refresh_filename,
891
.bdrv_co_block_status = nbd_client_co_block_status,
892
.bdrv_dirname = nbd_dirname,
893
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nbd_unix = {
894
.bdrv_co_pdiscard = nbd_client_co_pdiscard,
895
.bdrv_refresh_limits = nbd_refresh_limits,
896
.bdrv_co_truncate = nbd_co_truncate,
897
- .bdrv_getlength = nbd_getlength,
898
+ .bdrv_co_getlength = nbd_co_getlength,
899
.bdrv_refresh_filename = nbd_refresh_filename,
900
.bdrv_co_block_status = nbd_client_co_block_status,
901
.bdrv_dirname = nbd_dirname,
902
diff --git a/block/null.c b/block/null.c
903
index XXXXXXX..XXXXXXX 100644
904
--- a/block/null.c
905
+++ b/block/null.c
906
@@ -XXX,XX +XXX,XX @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags,
907
return ret;
908
}
909
910
-static int64_t null_getlength(BlockDriverState *bs)
911
+static int64_t coroutine_fn null_co_getlength(BlockDriverState *bs)
912
{
913
BDRVNullState *s = bs->opaque;
914
return s->length;
915
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_null_co = {
916
917
.bdrv_file_open = null_file_open,
918
.bdrv_parse_filename = null_co_parse_filename,
919
- .bdrv_getlength = null_getlength,
920
+ .bdrv_co_getlength = null_co_getlength,
921
.bdrv_get_allocated_file_size = null_allocated_file_size,
922
923
.bdrv_co_preadv = null_co_preadv,
924
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_null_aio = {
925
926
.bdrv_file_open = null_file_open,
927
.bdrv_parse_filename = null_aio_parse_filename,
928
- .bdrv_getlength = null_getlength,
929
+ .bdrv_co_getlength = null_co_getlength,
930
.bdrv_get_allocated_file_size = null_allocated_file_size,
931
932
.bdrv_aio_preadv = null_aio_preadv,
933
diff --git a/block/nvme.c b/block/nvme.c
934
index XXXXXXX..XXXXXXX 100644
935
--- a/block/nvme.c
936
+++ b/block/nvme.c
937
@@ -XXX,XX +XXX,XX @@ fail:
938
return ret;
939
}
940
941
-static int64_t nvme_getlength(BlockDriverState *bs)
942
+static int64_t coroutine_fn nvme_co_getlength(BlockDriverState *bs)
943
{
944
BDRVNVMeState *s = bs->opaque;
945
return s->nsze << s->blkshift;
946
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn nvme_co_truncate(BlockDriverState *bs, int64_t offset,
947
return -ENOTSUP;
948
}
949
950
- cur_length = nvme_getlength(bs);
951
+ cur_length = nvme_co_getlength(bs);
952
if (offset != cur_length && exact) {
953
error_setg(errp, "Cannot resize NVMe devices");
954
return -ENOTSUP;
955
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nvme = {
956
.bdrv_parse_filename = nvme_parse_filename,
957
.bdrv_file_open = nvme_file_open,
958
.bdrv_close = nvme_close,
959
- .bdrv_getlength = nvme_getlength,
960
+ .bdrv_co_getlength = nvme_co_getlength,
961
.bdrv_probe_blocksizes = nvme_probe_blocksizes,
962
.bdrv_co_truncate = nvme_co_truncate,
963
964
diff --git a/block/preallocate.c b/block/preallocate.c
965
index XXXXXXX..XXXXXXX 100644
966
--- a/block/preallocate.c
967
+++ b/block/preallocate.c
968
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn preallocate_co_flush(BlockDriverState *bs)
969
return bdrv_co_flush(bs->file->bs);
970
}
971
972
-static int64_t preallocate_getlength(BlockDriverState *bs)
973
+static int64_t coroutine_fn preallocate_co_getlength(BlockDriverState *bs)
974
{
975
int64_t ret;
976
BDRVPreallocateState *s = bs->opaque;
977
@@ -XXX,XX +XXX,XX @@ static int64_t preallocate_getlength(BlockDriverState *bs)
978
return s->data_end;
979
}
980
981
- ret = bdrv_getlength(bs->file->bs);
982
+ ret = bdrv_co_getlength(bs->file->bs);
983
984
if (has_prealloc_perms(bs)) {
985
s->file_end = s->zero_start = s->data_end = ret;
986
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_preallocate_filter = {
987
.format_name = "preallocate",
988
.instance_size = sizeof(BDRVPreallocateState),
989
990
- .bdrv_getlength = preallocate_getlength,
991
- .bdrv_open = preallocate_open,
992
- .bdrv_close = preallocate_close,
993
+ .bdrv_co_getlength = preallocate_co_getlength,
994
+ .bdrv_open = preallocate_open,
995
+ .bdrv_close = preallocate_close,
996
997
.bdrv_reopen_prepare = preallocate_reopen_prepare,
998
.bdrv_reopen_commit = preallocate_reopen_commit,
999
diff --git a/block/qed.c b/block/qed.c
1000
index XXXXXXX..XXXXXXX 100644
1001
--- a/block/qed.c
1002
+++ b/block/qed.c
1003
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_truncate(BlockDriverState *bs,
1004
return ret;
1005
}
1006
1007
-static int64_t bdrv_qed_getlength(BlockDriverState *bs)
1008
+static int64_t coroutine_fn bdrv_qed_co_getlength(BlockDriverState *bs)
1009
{
1010
BDRVQEDState *s = bs->opaque;
1011
return s->header.image_size;
1012
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
1013
.bdrv_co_writev = bdrv_qed_co_writev,
1014
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
1015
.bdrv_co_truncate = bdrv_qed_co_truncate,
1016
- .bdrv_getlength = bdrv_qed_getlength,
1017
+ .bdrv_co_getlength = bdrv_qed_co_getlength,
1018
.bdrv_get_info = bdrv_qed_get_info,
1019
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
1020
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
1021
diff --git a/block/quorum.c b/block/quorum.c
1022
index XXXXXXX..XXXXXXX 100644
1023
--- a/block/quorum.c
1024
+++ b/block/quorum.c
1025
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn quorum_co_pwrite_zeroes(BlockDriverState *bs,
1026
flags | BDRV_REQ_ZERO_WRITE);
1027
}
1028
1029
-static int64_t quorum_getlength(BlockDriverState *bs)
1030
+static int64_t coroutine_fn quorum_co_getlength(BlockDriverState *bs)
1031
{
1032
BDRVQuorumState *s = bs->opaque;
1033
int64_t result;
1034
int i;
1035
1036
/* check that all file have the same length */
1037
- result = bdrv_getlength(s->children[0]->bs);
1038
+ result = bdrv_co_getlength(s->children[0]->bs);
1039
if (result < 0) {
1040
return result;
1041
}
1042
for (i = 1; i < s->num_children; i++) {
1043
- int64_t value = bdrv_getlength(s->children[i]->bs);
1044
+ int64_t value = bdrv_co_getlength(s->children[i]->bs);
1045
if (value < 0) {
1046
return value;
1047
}
1048
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_quorum = {
1049
1050
.bdrv_co_flush = quorum_co_flush,
1051
1052
- .bdrv_getlength = quorum_getlength,
1053
+ .bdrv_co_getlength = quorum_co_getlength,
1054
1055
.bdrv_co_preadv = quorum_co_preadv,
1056
.bdrv_co_pwritev = quorum_co_pwritev,
1057
diff --git a/block/raw-format.c b/block/raw-format.c
1058
index XXXXXXX..XXXXXXX 100644
1059
--- a/block/raw-format.c
1060
+++ b/block/raw-format.c
1061
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_pdiscard(BlockDriverState *bs,
1062
return bdrv_co_pdiscard(bs->file, offset, bytes);
1063
}
1064
1065
-static int64_t raw_getlength(BlockDriverState *bs)
1066
+static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
1067
{
1068
int64_t len;
1069
BDRVRawState *s = bs->opaque;
1070
1071
/* Update size. It should not change unless the file was externally
1072
* modified. */
1073
- len = bdrv_getlength(bs->file->bs);
1074
+ len = bdrv_co_getlength(bs->file->bs);
1075
if (len < 0) {
1076
return len;
1077
}
1078
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
1079
.bdrv_co_copy_range_from = &raw_co_copy_range_from,
1080
.bdrv_co_copy_range_to = &raw_co_copy_range_to,
1081
.bdrv_co_truncate = &raw_co_truncate,
1082
- .bdrv_getlength = &raw_getlength,
1083
+ .bdrv_co_getlength = &raw_co_getlength,
1084
.is_format = true,
1085
.has_variable_length = true,
1086
.bdrv_measure = &raw_measure,
1087
diff --git a/block/rbd.c b/block/rbd.c
1088
index XXXXXXX..XXXXXXX 100644
1089
--- a/block/rbd.c
1090
+++ b/block/rbd.c
1091
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs,
1092
return status;
1093
}
1094
1095
-static int64_t qemu_rbd_getlength(BlockDriverState *bs)
1096
+static int64_t coroutine_fn qemu_rbd_co_getlength(BlockDriverState *bs)
1097
{
1098
BDRVRBDState *s = bs->opaque;
1099
int r;
1100
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
1101
.bdrv_get_info = qemu_rbd_getinfo,
1102
.bdrv_get_specific_info = qemu_rbd_get_specific_info,
1103
.create_opts = &qemu_rbd_create_opts,
1104
- .bdrv_getlength = qemu_rbd_getlength,
1105
+ .bdrv_co_getlength = qemu_rbd_co_getlength,
1106
.bdrv_co_truncate = qemu_rbd_co_truncate,
1107
.protocol_name = "rbd",
1108
318
diff --git a/block/replication.c b/block/replication.c
1109
diff --git a/block/replication.c b/block/replication.c
319
index XXXXXXX..XXXXXXX 100644
1110
index XXXXXXX..XXXXXXX 100644
320
--- a/block/replication.c
1111
--- a/block/replication.c
321
+++ b/block/replication.c
1112
+++ b/block/replication.c
322
@@ -XXX,XX +XXX,XX @@ static void replication_close(BlockDriverState *bs)
1113
@@ -XXX,XX +XXX,XX @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c,
323
replication_stop(s->rs, false, NULL);
1114
return;
324
}
1115
}
325
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
1116
326
- block_job_cancel_sync(s->active_disk->bs->job);
1117
-static int64_t replication_getlength(BlockDriverState *bs)
327
+ job_cancel_sync(&s->active_disk->bs->job->job);
1118
+static int64_t coroutine_fn replication_co_getlength(BlockDriverState *bs)
328
}
1119
{
329
1120
- return bdrv_getlength(bs->file->bs);
330
if (s->mode == REPLICATION_MODE_SECONDARY) {
1121
+ return bdrv_co_getlength(bs->file->bs);
331
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
1122
}
332
* disk, secondary disk in backup_job_completed().
1123
333
*/
1124
static int replication_get_io_status(BDRVReplicationState *s)
334
if (s->secondary_disk->bs->job) {
1125
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_replication = {
335
- block_job_cancel_sync(s->secondary_disk->bs->job);
1126
.bdrv_close = replication_close,
336
+ job_cancel_sync(&s->secondary_disk->bs->job->job);
1127
.bdrv_child_perm = replication_child_perm,
337
}
1128
338
1129
- .bdrv_getlength = replication_getlength,
339
if (!failover) {
1130
+ .bdrv_co_getlength = replication_co_getlength,
340
diff --git a/block/stream.c b/block/stream.c
1131
.bdrv_co_readv = replication_co_readv,
341
index XXXXXXX..XXXXXXX 100644
1132
.bdrv_co_writev = replication_co_writev,
342
--- a/block/stream.c
1133
343
+++ b/block/stream.c
1134
diff --git a/block/ssh.c b/block/ssh.c
344
@@ -XXX,XX +XXX,XX @@ out:
1135
index XXXXXXX..XXXXXXX 100644
345
}
1136
--- a/block/ssh.c
346
1137
+++ b/block/ssh.c
347
g_free(s->backing_file_str);
1138
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int ssh_co_flush(BlockDriverState *bs)
348
- block_job_completed(&s->common, data->ret);
1139
return ret;
349
+ job_completed(job, data->ret);
1140
}
350
g_free(data);
1141
351
}
1142
-static int64_t ssh_getlength(BlockDriverState *bs)
352
1143
+static int64_t coroutine_fn ssh_co_getlength(BlockDriverState *bs)
353
diff --git a/blockdev.c b/blockdev.c
1144
{
354
index XXXXXXX..XXXXXXX 100644
1145
BDRVSSHState *s = bs->opaque;
355
--- a/blockdev.c
1146
int64_t length;
356
+++ b/blockdev.c
1147
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_ssh = {
357
@@ -XXX,XX +XXX,XX @@ void blockdev_mark_auto_del(BlockBackend *blk)
1148
.bdrv_has_zero_init = ssh_has_zero_init,
358
aio_context_acquire(aio_context);
1149
.bdrv_co_readv = ssh_co_readv,
359
1150
.bdrv_co_writev = ssh_co_writev,
360
if (bs->job) {
1151
- .bdrv_getlength = ssh_getlength,
361
- block_job_cancel(bs->job, false);
1152
+ .bdrv_co_getlength = ssh_co_getlength,
362
+ job_cancel(&bs->job->job, false);
1153
.bdrv_co_truncate = ssh_co_truncate,
363
}
1154
.bdrv_co_flush_to_disk = ssh_co_flush,
364
1155
.bdrv_refresh_filename = ssh_refresh_filename,
365
aio_context_release(aio_context);
1156
diff --git a/block/throttle.c b/block/throttle.c
366
@@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(BlkActionState *common)
1157
index XXXXXXX..XXXXXXX 100644
367
aio_context = bdrv_get_aio_context(state->bs);
1158
--- a/block/throttle.c
368
aio_context_acquire(aio_context);
1159
+++ b/block/throttle.c
369
1160
@@ -XXX,XX +XXX,XX @@ static void throttle_close(BlockDriverState *bs)
370
- block_job_cancel_sync(state->job);
1161
}
371
+ job_cancel_sync(&state->job->job);
1162
372
1163
373
aio_context_release(aio_context);
1164
-static int64_t throttle_getlength(BlockDriverState *bs)
374
}
1165
+static int64_t coroutine_fn throttle_co_getlength(BlockDriverState *bs)
375
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(BlkActionState *common)
1166
{
376
aio_context = bdrv_get_aio_context(state->bs);
1167
- return bdrv_getlength(bs->file->bs);
377
aio_context_acquire(aio_context);
1168
+ return bdrv_co_getlength(bs->file->bs);
378
1169
}
379
- block_job_cancel_sync(state->job);
1170
380
+ job_cancel_sync(&state->job->job);
1171
static int coroutine_fn throttle_co_preadv(BlockDriverState *bs,
381
1172
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_throttle = {
382
aio_context_release(aio_context);
1173
383
}
1174
.bdrv_child_perm = bdrv_default_perms,
384
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
1175
385
}
1176
- .bdrv_getlength = throttle_getlength,
386
1177
+ .bdrv_co_getlength = throttle_co_getlength,
387
trace_qmp_block_job_cancel(job);
1178
388
- block_job_user_cancel(job, force, errp);
1179
.bdrv_co_preadv = throttle_co_preadv,
389
+ job_user_cancel(&job->job, force, errp);
1180
.bdrv_co_pwritev = throttle_co_pwritev,
390
out:
1181
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
391
aio_context_release(aio_context);
1182
index XXXXXXX..XXXXXXX 100644
392
}
1183
--- a/hw/scsi/scsi-disk.c
393
diff --git a/blockjob.c b/blockjob.c
1184
+++ b/hw/scsi/scsi-disk.c
394
index XXXXXXX..XXXXXXX 100644
1185
@@ -XXX,XX +XXX,XX @@ static void scsi_disk_reset(DeviceState *dev)
395
--- a/blockjob.c
1186
{
396
+++ b/blockjob.c
1187
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev.qdev, dev);
397
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
1188
uint64_t nb_sectors;
398
*jobptr = NULL;
1189
+ AioContext *ctx;
399
}
1190
400
1191
scsi_device_purge_requests(&s->qdev, SENSE_CODE(RESET));
401
-void block_job_cancel(BlockJob *job, bool force)
1192
402
-{
1193
+ ctx = blk_get_aio_context(s->qdev.conf.blk);
403
- if (job->job.status == JOB_STATUS_CONCLUDED) {
1194
+ aio_context_acquire(ctx);
404
- job_do_dismiss(&job->job);
1195
blk_get_geometry(s->qdev.conf.blk, &nb_sectors);
405
- return;
1196
+ aio_context_release(ctx);
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
+ }
565
+}
566
+
1197
+
567
+void job_cancel(Job *job, bool force)
1198
nb_sectors /= s->qdev.blocksize / BDRV_SECTOR_SIZE;
568
+{
1199
if (nb_sectors) {
569
+ if (job->status == JOB_STATUS_CONCLUDED) {
1200
nb_sectors--;
570
+ job_do_dismiss(job);
1201
diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c
571
+ return;
1202
index XXXXXXX..XXXXXXX 100644
572
+ }
1203
--- a/tests/unit/test-block-iothread.c
573
+ job_cancel_async(job, force);
1204
+++ b/tests/unit/test-block-iothread.c
574
+ if (!job_started(job)) {
1205
@@ -XXX,XX +XXX,XX @@ static void test_attach_second_node(void)
575
+ job_completed(job, -ECANCELED);
1206
qdict_put_str(options, "driver", "raw");
576
+ } else if (job->deferred_to_main_loop) {
1207
qdict_put_str(options, "file", "base");
577
+ job_completed_txn_abort(job);
1208
578
+ } else {
1209
+ aio_context_acquire(ctx);
579
+ job_enter(job);
1210
filter = bdrv_open(NULL, NULL, options, BDRV_O_RDWR, &error_abort);
580
+ }
1211
+ aio_context_release(ctx);
581
+}
582
+
1212
+
583
+void job_user_cancel(Job *job, bool force, Error **errp)
1213
g_assert(blk_get_aio_context(blk) == ctx);
584
+{
1214
g_assert(bdrv_get_aio_context(bs) == ctx);
585
+ if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) {
1215
g_assert(bdrv_get_aio_context(filter) == ctx);
586
+ return;
1216
diff --git a/block/meson.build b/block/meson.build
587
+ }
1217
index XXXXXXX..XXXXXXX 100644
588
+ job_cancel(job, force);
1218
--- a/block/meson.build
589
+}
1219
+++ b/block/meson.build
590
+
1220
@@ -XXX,XX +XXX,XX @@ block_gen_c = custom_target('block-gen.c',
591
+/* A wrapper around job_cancel() taking an Error ** parameter so it may be
1221
input: files(
592
+ * used with job_finish_sync() without the need for (rather nasty) function
1222
'../include/block/block-io.h',
593
+ * pointer casts there. */
1223
'../include/block/dirty-bitmap.h',
594
+static void job_cancel_err(Job *job, Error **errp)
1224
+ '../include/block/block_int-io.h',
595
+{
1225
'../include/block/block-global-state.h',
596
+ job_cancel(job, false);
1226
'../include/sysemu/block-backend-io.h',
597
+}
1227
'coroutines.h'
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
--
1228
--
825
2.13.6
1229
2.38.1
826
827
diff view generated by jsdifflib
1
BlockJob.driver is redundant with Job.driver and only used in very few
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
places any more. Remove it.
3
2
3
The only difference is that blk_ checks if the block is available,
4
but this check is already performed above in blk_check_byte_request().
5
6
This is in preparation for the graph rdlock, which will be taken
7
by both the callers of blk_check_byte_request() and blk_getlength().
8
9
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Message-Id: <20230113204212.359076-8-kwolf@redhat.com>
12
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
14
---
6
include/block/blockjob.h | 3 ---
15
block/block-backend.c | 2 +-
7
blockjob.c | 17 ++++++++++-------
16
1 file changed, 1 insertion(+), 1 deletion(-)
8
2 files changed, 10 insertions(+), 10 deletions(-)
9
17
10
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
diff --git a/block/block-backend.c b/block/block-backend.c
11
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/blockjob.h
20
--- a/block/block-backend.c
13
+++ b/include/block/blockjob.h
21
+++ b/block/block-backend.c
14
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
22
@@ -XXX,XX +XXX,XX @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
15
/** Data belonging to the generic Job infrastructure */
16
Job job;
17
18
- /** The job type, including the job vtable. */
19
- const BlockJobDriver *driver;
20
-
21
/** The block device on which the job is operating. */
22
BlockBackend *blk;
23
24
diff --git a/blockjob.c b/blockjob.c
25
index XXXXXXX..XXXXXXX 100644
26
--- a/blockjob.c
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
}
23
}
41
24
42
job_resume(&job->job);
25
if (!blk->allow_write_beyond_eof) {
43
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
26
- len = blk_getlength(blk);
44
void block_job_drain(Job *job)
27
+ len = bdrv_getlength(blk_bs(blk));
45
{
28
if (len < 0) {
46
BlockJob *bjob = container_of(job, BlockJob, job);
29
return len;
47
+ const JobDriver *drv = job->driver;
30
}
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
--
31
--
78
2.13.6
32
2.38.1
79
80
diff view generated by jsdifflib
1
When block jobs need an AioContext, they just take it from their main
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
block node. Generic jobs don't have a main block node, so we need to
3
assign them an AioContext explicitly.
4
2
3
In some places we are sure we are always running in a
4
coroutine, therefore it's useless to call the generated_co_wrapper,
5
instead call directly the _co_ function.
6
7
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-Id: <20230113204212.359076-9-kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
10
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
12
---
9
include/qemu/job.h | 7 ++++++-
13
block/block-backend.c | 6 +++---
10
blockjob.c | 5 ++++-
14
block/io.c | 4 ++--
11
job.c | 4 +++-
15
block/preallocate.c | 6 +++---
12
3 files changed, 13 insertions(+), 3 deletions(-)
16
block/qed.c | 2 +-
17
4 files changed, 9 insertions(+), 9 deletions(-)
13
18
14
diff --git a/include/qemu/job.h b/include/qemu/job.h
19
diff --git a/block/block-backend.c b/block/block-backend.c
15
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
16
--- a/include/qemu/job.h
21
--- a/block/block-backend.c
17
+++ b/include/qemu/job.h
22
+++ b/block/block-backend.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
23
@@ -XXX,XX +XXX,XX @@ void blk_set_disable_request_queuing(BlockBackend *blk, bool disable)
19
/** Current state; See @JobStatus for details. */
24
blk->disable_request_queuing = disable;
20
JobStatus status;
25
}
21
26
22
+ /** AioContext to run the job coroutine in */
27
-static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
23
+ AioContext *aio_context;
28
- int64_t bytes)
24
+
29
+static coroutine_fn int blk_check_byte_request(BlockBackend *blk,
25
/**
30
+ int64_t offset, int64_t bytes)
26
* Set to true if the job should cancel itself. The flag must
31
{
27
* always be tested just before toggling the busy flag from false
32
int64_t len;
28
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
33
29
*
34
@@ -XXX,XX +XXX,XX @@ static int blk_check_byte_request(BlockBackend *blk, int64_t offset,
30
* @job_id: The id of the newly-created job, or %NULL for internal jobs
35
}
31
* @driver: The class object for the newly-created job.
36
32
+ * @ctx: The AioContext to run the job coroutine in.
37
if (!blk->allow_write_beyond_eof) {
33
* @errp: Error object.
38
- len = bdrv_getlength(blk_bs(blk));
34
*/
39
+ len = bdrv_co_getlength(blk_bs(blk));
35
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
40
if (len < 0) {
36
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
41
return len;
37
+ Error **errp);
42
}
38
43
diff --git a/block/io.c b/block/io.c
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
44
index XXXXXXX..XXXXXXX 100644
43
--- a/blockjob.c
45
--- a/block/io.c
44
+++ b/blockjob.c
46
+++ b/block/io.c
45
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
47
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
46
{
48
if (new_bytes && backing) {
47
BlockJob *job = opaque;
49
int64_t backing_len;
48
50
49
+ job->job.aio_context = new_context;
51
- backing_len = bdrv_getlength(backing->bs);
50
if (job->driver->attached_aio_context) {
52
+ backing_len = bdrv_co_getlength(backing->bs);
51
job->driver->attached_aio_context(job, new_context);
53
if (backing_len < 0) {
54
ret = backing_len;
55
error_setg_errno(errp, -ret, "Could not get backing file size");
56
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_truncate(BdrvChild *child, int64_t offset, bool exact,
57
goto out;
52
}
58
}
53
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
59
54
block_job_drain(job);
60
- ret = bdrv_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
61
+ ret = bdrv_co_refresh_total_sectors(bs, offset >> BDRV_SECTOR_BITS);
62
if (ret < 0) {
63
error_setg_errno(errp, -ret, "Could not refresh total sector count");
64
} else {
65
diff --git a/block/preallocate.c b/block/preallocate.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/block/preallocate.c
68
+++ b/block/preallocate.c
69
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
55
}
70
}
56
71
57
+ job->job.aio_context = NULL;
72
if (s->data_end < 0) {
58
job_unref(&job->job);
73
- s->data_end = bdrv_getlength(bs->file->bs);
59
}
74
+ s->data_end = bdrv_co_getlength(bs->file->bs);
60
75
if (s->data_end < 0) {
61
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
76
return false;
62
return NULL;
77
}
78
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn handle_write(BlockDriverState *bs, int64_t offset,
63
}
79
}
64
80
65
- job = job_create(job_id, &driver->job_driver, errp);
81
if (s->file_end < 0) {
66
+ job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
82
- s->file_end = bdrv_getlength(bs->file->bs);
67
+ errp);
83
+ s->file_end = bdrv_co_getlength(bs->file->bs);
68
if (job == NULL) {
84
if (s->file_end < 0) {
69
blk_unref(blk);
85
return false;
70
return NULL;
86
}
71
diff --git a/job.c b/job.c
87
@@ -XXX,XX +XXX,XX @@ preallocate_co_truncate(BlockDriverState *bs, int64_t offset,
88
89
if (s->data_end >= 0 && offset > s->data_end) {
90
if (s->file_end < 0) {
91
- s->file_end = bdrv_getlength(bs->file->bs);
92
+ s->file_end = bdrv_co_getlength(bs->file->bs);
93
if (s->file_end < 0) {
94
error_setg(errp, "failed to get file length");
95
return s->file_end;
96
diff --git a/block/qed.c b/block/qed.c
72
index XXXXXXX..XXXXXXX 100644
97
index XXXXXXX..XXXXXXX 100644
73
--- a/job.c
98
--- a/block/qed.c
74
+++ b/job.c
99
+++ b/block/qed.c
75
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id)
100
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options,
76
return NULL;
101
}
77
}
102
78
103
/* Round down file size to the last cluster */
79
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
104
- file_size = bdrv_getlength(bs->file->bs);
80
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
105
+ file_size = bdrv_co_getlength(bs->file->bs);
81
+ Error **errp)
106
if (file_size < 0) {
82
{
107
error_setg(errp, "Failed to get file length");
83
Job *job;
108
return file_size;
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
--
109
--
94
2.13.6
110
2.38.1
95
96
diff view generated by jsdifflib
1
The transition to the READY state was still performed in the BlockJob
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
layer, in the same function that sent the BLOCK_JOB_READY QMP event.
3
2
4
This patch brings the state transition to the Job layer and implements
3
bdrv_get_allocated_file_size() is categorized as an I/O function, and it
5
the QMP event using a notifier called from the Job layer, like we
4
currently doesn't run in a coroutine. We should let it take a graph
6
already do for other events related to state transitions.
5
rdlock since it traverses the block nodes graph, which however is only
6
possible in a coroutine.
7
7
8
Therefore turn it into a co_wrapper to move the actual function into a
9
coroutine where the lock can be taken.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20230113204212.359076-10-kwolf@redhat.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
16
---
11
include/block/blockjob.h | 3 +++
17
include/block/block-io.h | 4 +++-
12
include/block/blockjob_int.h | 8 --------
18
include/block/block_int-common.h | 4 +++-
13
include/qemu/job.h | 9 ++++++---
19
block.c | 12 ++++++------
14
block/mirror.c | 6 +++---
20
block/file-posix.c | 14 +++++---------
15
blockjob.c | 33 ++++++++++++++++++---------------
21
block/file-win32.c | 10 ++++------
16
job.c | 16 +++++++++++++---
22
block/gluster.c | 11 ++++++-----
17
tests/test-bdrv-drain.c | 2 +-
23
block/nfs.c | 4 ++--
18
tests/test-blockjob.c | 2 +-
24
block/null.c | 7 ++++---
19
8 files changed, 45 insertions(+), 34 deletions(-)
25
block/qcow2-refcount.c | 2 +-
26
block/vmdk.c | 9 +++++----
27
10 files changed, 39 insertions(+), 38 deletions(-)
20
28
21
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
29
diff --git a/include/block/block-io.h b/include/block/block-io.h
22
index XXXXXXX..XXXXXXX 100644
30
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob.h
31
--- a/include/block/block-io.h
24
+++ b/include/block/blockjob.h
32
+++ b/include/block/block-io.h
25
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
33
@@ -XXX,XX +XXX,XX @@ int64_t co_wrapper_mixed bdrv_nb_sectors(BlockDriverState *bs);
26
/** Called when the job transitions to PENDING */
34
int64_t coroutine_fn bdrv_co_getlength(BlockDriverState *bs);
27
Notifier pending_notifier;
35
int64_t co_wrapper_mixed bdrv_getlength(BlockDriverState *bs);
28
36
29
+ /** Called when the job transitions to READY */
37
-int64_t bdrv_get_allocated_file_size(BlockDriverState *bs);
30
+ Notifier ready_notifier;
38
+int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs);
39
+int64_t co_wrapper bdrv_get_allocated_file_size(BlockDriverState *bs);
31
+
40
+
32
/** BlockDriverStates that are involved in this block job */
41
BlockMeasureInfo *bdrv_measure(BlockDriver *drv, QemuOpts *opts,
33
GSList *nodes;
42
BlockDriverState *in_bs, Error **errp);
34
} BlockJob;
43
void bdrv_get_geometry(BlockDriverState *bs, uint64_t *nb_sectors_ptr);
35
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
44
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
36
index XXXXXXX..XXXXXXX 100644
45
index XXXXXXX..XXXXXXX 100644
37
--- a/include/block/blockjob_int.h
46
--- a/include/block/block_int-common.h
38
+++ b/include/block/blockjob_int.h
47
+++ b/include/block/block_int-common.h
39
@@ -XXX,XX +XXX,XX @@ void block_job_drain(Job *job);
48
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
40
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
49
bool exact, PreallocMode prealloc,
50
BdrvRequestFlags flags, Error **errp);
51
int64_t coroutine_fn (*bdrv_co_getlength)(BlockDriverState *bs);
52
- int64_t (*bdrv_get_allocated_file_size)(BlockDriverState *bs);
53
+ int64_t coroutine_fn (*bdrv_co_get_allocated_file_size)(
54
+ BlockDriverState *bs);
55
+
56
BlockMeasureInfo *(*bdrv_measure)(QemuOpts *opts, BlockDriverState *in_bs,
57
Error **errp);
58
59
diff --git a/block.c b/block.c
60
index XXXXXXX..XXXXXXX 100644
61
--- a/block.c
62
+++ b/block.c
63
@@ -XXX,XX +XXX,XX @@ exit:
64
}
41
65
42
/**
66
/**
43
- * block_job_event_ready:
67
- * Implementation of BlockDriver.bdrv_get_allocated_file_size() that
44
- * @job: The job which is now ready to be completed.
68
+ * Implementation of BlockDriver.bdrv_co_get_allocated_file_size() that
45
- *
69
* sums the size of all data-bearing children. (This excludes backing
46
- * Send a BLOCK_JOB_READY event for the specified job.
70
* children.)
47
- */
71
*/
48
-void block_job_event_ready(BlockJob *job);
72
@@ -XXX,XX +XXX,XX @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs)
73
if (child->role & (BDRV_CHILD_DATA | BDRV_CHILD_METADATA |
74
BDRV_CHILD_FILTERED))
75
{
76
- child_size = bdrv_get_allocated_file_size(child->bs);
77
+ child_size = bdrv_co_get_allocated_file_size(child->bs);
78
if (child_size < 0) {
79
return child_size;
80
}
81
@@ -XXX,XX +XXX,XX @@ static int64_t bdrv_sum_allocated_file_size(BlockDriverState *bs)
82
* Length of a allocated file in bytes. Sparse files are counted by actual
83
* allocated space. Return < 0 if error or unknown.
84
*/
85
-int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
86
+int64_t coroutine_fn bdrv_co_get_allocated_file_size(BlockDriverState *bs)
87
{
88
BlockDriver *drv = bs->drv;
89
IO_CODE();
90
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
91
if (!drv) {
92
return -ENOMEDIUM;
93
}
94
- if (drv->bdrv_get_allocated_file_size) {
95
- return drv->bdrv_get_allocated_file_size(bs);
96
+ if (drv->bdrv_co_get_allocated_file_size) {
97
+ return drv->bdrv_co_get_allocated_file_size(bs);
98
}
99
100
if (drv->bdrv_file_open) {
101
@@ -XXX,XX +XXX,XX @@ int64_t bdrv_get_allocated_file_size(BlockDriverState *bs)
102
return -ENOTSUP;
103
} else if (drv->is_filter) {
104
/* Filter drivers default to the size of their filtered child */
105
- return bdrv_get_allocated_file_size(bdrv_filter_bs(bs));
106
+ return bdrv_co_get_allocated_file_size(bdrv_filter_bs(bs));
107
} else {
108
/* Other drivers default to summing their children's sizes */
109
return bdrv_sum_allocated_file_size(bs);
110
diff --git a/block/file-posix.c b/block/file-posix.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/block/file-posix.c
113
+++ b/block/file-posix.c
114
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
115
}
116
#endif
117
118
-static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
119
+static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs)
120
{
121
struct stat st;
122
BDRVRawState *s = bs->opaque;
123
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
124
.bdrv_co_truncate = raw_co_truncate,
125
.bdrv_co_getlength = raw_co_getlength,
126
.bdrv_get_info = raw_get_info,
127
- .bdrv_get_allocated_file_size
128
- = raw_get_allocated_file_size,
129
+ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
130
.bdrv_get_specific_stats = raw_get_specific_stats,
131
.bdrv_check_perm = raw_check_perm,
132
.bdrv_set_perm = raw_set_perm,
133
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
134
.bdrv_co_truncate = raw_co_truncate,
135
.bdrv_co_getlength = raw_co_getlength,
136
.bdrv_get_info = raw_get_info,
137
- .bdrv_get_allocated_file_size
138
- = raw_get_allocated_file_size,
139
+ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
140
.bdrv_get_specific_stats = hdev_get_specific_stats,
141
.bdrv_check_perm = raw_check_perm,
142
.bdrv_set_perm = raw_set_perm,
143
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
144
.bdrv_co_truncate = raw_co_truncate,
145
.bdrv_co_getlength = raw_co_getlength,
146
.has_variable_length = true,
147
- .bdrv_get_allocated_file_size
148
- = raw_get_allocated_file_size,
149
+ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
150
151
/* removable device support */
152
.bdrv_co_is_inserted = cdrom_co_is_inserted,
153
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
154
.bdrv_co_truncate = raw_co_truncate,
155
.bdrv_co_getlength = raw_co_getlength,
156
.has_variable_length = true,
157
- .bdrv_get_allocated_file_size
158
- = raw_get_allocated_file_size,
159
+ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
160
161
/* removable device support */
162
.bdrv_co_is_inserted = cdrom_co_is_inserted,
163
diff --git a/block/file-win32.c b/block/file-win32.c
164
index XXXXXXX..XXXXXXX 100644
165
--- a/block/file-win32.c
166
+++ b/block/file-win32.c
167
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn raw_co_getlength(BlockDriverState *bs)
168
return l.QuadPart;
169
}
170
171
-static int64_t raw_get_allocated_file_size(BlockDriverState *bs)
172
+static int64_t coroutine_fn raw_co_get_allocated_file_size(BlockDriverState *bs)
173
{
174
typedef DWORD (WINAPI * get_compressed_t)(const char *filename,
175
DWORD * high);
176
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
177
178
.bdrv_co_truncate = raw_co_truncate,
179
.bdrv_co_getlength = raw_co_getlength,
180
- .bdrv_get_allocated_file_size
181
- = raw_get_allocated_file_size,
182
+ .bdrv_co_get_allocated_file_size
183
+ = raw_co_get_allocated_file_size,
184
185
.create_opts = &raw_create_opts,
186
};
187
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
188
189
.bdrv_co_getlength = raw_co_getlength,
190
.has_variable_length = true,
49
-
191
-
50
-/**
192
- .bdrv_get_allocated_file_size
51
* block_job_error_action:
193
- = raw_get_allocated_file_size,
52
* @job: The job to signal an error for.
194
+ .bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
53
* @on_err: The error action setting.
195
};
54
diff --git a/include/qemu/job.h b/include/qemu/job.h
196
55
index XXXXXXX..XXXXXXX 100644
197
static void bdrv_file_init(void)
56
--- a/include/qemu/job.h
198
diff --git a/block/gluster.c b/block/gluster.c
57
+++ b/include/qemu/job.h
199
index XXXXXXX..XXXXXXX 100644
58
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
200
--- a/block/gluster.c
59
/** Notifiers called when the job transitions to PENDING */
201
+++ b/block/gluster.c
60
NotifierList on_pending;
202
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qemu_gluster_co_getlength(BlockDriverState *bs)
61
203
}
62
+ /** Notifiers called when the job transitions to READY */
204
}
63
+ NotifierList on_ready;
205
64
+
206
-static int64_t qemu_gluster_allocated_file_size(BlockDriverState *bs)
65
/** Element of the list of jobs */
207
+static int64_t coroutine_fn
66
QLIST_ENTRY(Job) job_list;
208
+qemu_gluster_co_get_allocated_file_size(BlockDriverState *bs)
67
209
{
68
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
210
BDRVGlusterState *s = bs->opaque;
69
/** The @job could not be started, free it. */
211
struct stat st;
70
void job_early_fail(Job *job);
212
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster = {
71
213
.bdrv_co_create = qemu_gluster_co_create,
72
+/** Moves the @job from RUNNING to READY */
214
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
73
+void job_transition_to_ready(Job *job);
215
.bdrv_co_getlength = qemu_gluster_co_getlength,
74
+
216
- .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
75
/**
217
+ .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size,
76
* @job: The job being completed.
218
.bdrv_co_truncate = qemu_gluster_co_truncate,
77
* @ret: The status code.
219
.bdrv_co_readv = qemu_gluster_co_readv,
78
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
220
.bdrv_co_writev = qemu_gluster_co_writev,
79
*/
221
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_tcp = {
80
int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
222
.bdrv_co_create = qemu_gluster_co_create,
81
223
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
82
-/* TODO To be removed from the public interface */
224
.bdrv_co_getlength = qemu_gluster_co_getlength,
83
-void job_state_transition(Job *job, JobStatus s1);
225
- .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
84
-
226
+ .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size,
227
.bdrv_co_truncate = qemu_gluster_co_truncate,
228
.bdrv_co_readv = qemu_gluster_co_readv,
229
.bdrv_co_writev = qemu_gluster_co_writev,
230
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_unix = {
231
.bdrv_co_create = qemu_gluster_co_create,
232
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
233
.bdrv_co_getlength = qemu_gluster_co_getlength,
234
- .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
235
+ .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size,
236
.bdrv_co_truncate = qemu_gluster_co_truncate,
237
.bdrv_co_readv = qemu_gluster_co_readv,
238
.bdrv_co_writev = qemu_gluster_co_writev,
239
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_gluster_rdma = {
240
.bdrv_co_create = qemu_gluster_co_create,
241
.bdrv_co_create_opts = qemu_gluster_co_create_opts,
242
.bdrv_co_getlength = qemu_gluster_co_getlength,
243
- .bdrv_get_allocated_file_size = qemu_gluster_allocated_file_size,
244
+ .bdrv_co_get_allocated_file_size = qemu_gluster_co_get_allocated_file_size,
245
.bdrv_co_truncate = qemu_gluster_co_truncate,
246
.bdrv_co_readv = qemu_gluster_co_readv,
247
.bdrv_co_writev = qemu_gluster_co_writev,
248
diff --git a/block/nfs.c b/block/nfs.c
249
index XXXXXXX..XXXXXXX 100644
250
--- a/block/nfs.c
251
+++ b/block/nfs.c
252
@@ -XXX,XX +XXX,XX @@ nfs_get_allocated_file_size_cb(int ret, struct nfs_context *nfs, void *data,
253
bdrv_wakeup(task->bs);
254
}
255
256
-static int64_t nfs_get_allocated_file_size(BlockDriverState *bs)
257
+static int64_t coroutine_fn nfs_co_get_allocated_file_size(BlockDriverState *bs)
258
{
259
NFSClient *client = bs->opaque;
260
NFSRPC task = {0};
261
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_nfs = {
262
.bdrv_has_zero_init = nfs_has_zero_init,
263
/* libnfs does not provide the allocated filesize of a file on win32. */
264
#if !defined(_WIN32)
265
- .bdrv_get_allocated_file_size = nfs_get_allocated_file_size,
266
+ .bdrv_co_get_allocated_file_size = nfs_co_get_allocated_file_size,
85
#endif
267
#endif
86
diff --git a/block/mirror.c b/block/mirror.c
268
.bdrv_co_truncate = nfs_file_co_truncate,
87
index XXXXXXX..XXXXXXX 100644
269
88
--- a/block/mirror.c
270
diff --git a/block/null.c b/block/null.c
89
+++ b/block/mirror.c
271
index XXXXXXX..XXXXXXX 100644
90
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
272
--- a/block/null.c
91
}
273
+++ b/block/null.c
92
274
@@ -XXX,XX +XXX,XX @@ static void null_refresh_filename(BlockDriverState *bs)
93
if (s->bdev_length == 0) {
275
bs->drv->format_name);
94
- /* Report BLOCK_JOB_READY and wait for complete. */
276
}
95
- block_job_event_ready(&s->common);
277
96
+ /* Transition to the READY state and wait for complete. */
278
-static int64_t null_allocated_file_size(BlockDriverState *bs)
97
+ job_transition_to_ready(&s->common.job);
279
+static int64_t coroutine_fn
98
s->synced = true;
280
+null_co_get_allocated_file_size(BlockDriverState *bs)
99
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
281
{
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;
282
return 0;
211
}
283
}
212
284
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_null_co = {
213
+void job_transition_to_ready(Job *job)
285
.bdrv_file_open = null_file_open,
214
+{
286
.bdrv_parse_filename = null_co_parse_filename,
215
+ job_state_transition(job, JOB_STATUS_READY);
287
.bdrv_co_getlength = null_co_getlength,
216
+ job_event_ready(job);
288
- .bdrv_get_allocated_file_size = null_allocated_file_size,
217
+}
289
+ .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size,
218
+
290
219
static void job_completed_txn_success(Job *job)
291
.bdrv_co_preadv = null_co_preadv,
220
{
292
.bdrv_co_pwritev = null_co_pwritev,
221
JobTxn *txn = job->txn;
293
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_null_aio = {
222
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
294
.bdrv_file_open = null_file_open,
223
index XXXXXXX..XXXXXXX 100644
295
.bdrv_parse_filename = null_aio_parse_filename,
224
--- a/tests/test-bdrv-drain.c
296
.bdrv_co_getlength = null_co_getlength,
225
+++ b/tests/test-bdrv-drain.c
297
- .bdrv_get_allocated_file_size = null_allocated_file_size,
226
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
298
+ .bdrv_co_get_allocated_file_size = null_co_get_allocated_file_size,
227
{
299
228
TestBlockJob *s = opaque;
300
.bdrv_aio_preadv = null_aio_preadv,
229
301
.bdrv_aio_pwritev = null_aio_pwritev,
230
- block_job_event_ready(&s->common);
302
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
231
+ job_transition_to_ready(&s->common.job);
303
index XXXXXXX..XXXXXXX 100644
232
while (!s->should_complete) {
304
--- a/block/qcow2-refcount.c
233
job_sleep_ns(&s->common.job, 100000);
305
+++ b/block/qcow2-refcount.c
234
}
306
@@ -XXX,XX +XXX,XX @@ int coroutine_fn qcow2_detect_metadata_preallocation(BlockDriverState *bs)
235
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
307
return file_length;
236
index XXXXXXX..XXXXXXX 100644
308
}
237
--- a/tests/test-blockjob.c
309
238
+++ b/tests/test-blockjob.c
310
- real_allocation = bdrv_get_allocated_file_size(bs->file->bs);
239
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
311
+ real_allocation = bdrv_co_get_allocated_file_size(bs->file->bs);
312
if (real_allocation < 0) {
313
return real_allocation;
314
}
315
diff --git a/block/vmdk.c b/block/vmdk.c
316
index XXXXXXX..XXXXXXX 100644
317
--- a/block/vmdk.c
318
+++ b/block/vmdk.c
319
@@ -XXX,XX +XXX,XX @@ static void vmdk_close(BlockDriverState *bs)
320
error_free(s->migration_blocker);
321
}
322
323
-static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
324
+static int64_t coroutine_fn
325
+vmdk_co_get_allocated_file_size(BlockDriverState *bs)
326
{
327
int i;
328
int64_t ret = 0;
329
int64_t r;
330
BDRVVmdkState *s = bs->opaque;
331
332
- ret = bdrv_get_allocated_file_size(bs->file->bs);
333
+ ret = bdrv_co_get_allocated_file_size(bs->file->bs);
334
if (ret < 0) {
335
return ret;
336
}
337
@@ -XXX,XX +XXX,XX @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
338
if (s->extents[i].file == bs->file) {
339
continue;
240
}
340
}
241
341
- r = bdrv_get_allocated_file_size(s->extents[i].file->bs);
242
if (!job_is_ready(&s->common.job) && s->should_converge) {
342
+ r = bdrv_co_get_allocated_file_size(s->extents[i].file->bs);
243
- block_job_event_ready(&s->common);
343
if (r < 0) {
244
+ job_transition_to_ready(&s->common.job);
344
return r;
245
}
345
}
246
346
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = {
247
job_sleep_ns(&s->common.job, 100000);
347
.bdrv_co_create_opts = vmdk_co_create_opts,
348
.bdrv_co_create = vmdk_co_create,
349
.bdrv_co_block_status = vmdk_co_block_status,
350
- .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
351
+ .bdrv_co_get_allocated_file_size = vmdk_co_get_allocated_file_size,
352
.bdrv_has_zero_init = vmdk_has_zero_init,
353
.bdrv_get_specific_info = vmdk_get_specific_info,
354
.bdrv_refresh_limits = vmdk_refresh_limits,
248
--
355
--
249
2.13.6
356
2.38.1
250
251
diff view generated by jsdifflib
1
This moves the finalisation of a single job from BlockJob to Job.
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
2
3
Some part of this code depends on job transactions, and job transactions
3
bdrv_get_info() is categorized as an I/O function, and it currently
4
call this code, we introduce some temporary calls from Job functions to
4
doesn't run in a coroutine. We should let it take a graph rdlock since
5
BlockJob ones. This will be fixed once transactions move to Job, too.
5
it traverses the block nodes graph, which however is only possible in a
6
coroutine.
6
7
8
Therefore turn it into a co_wrapper to move the actual function into a
9
coroutine where the lock can be taken.
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20230113204212.359076-11-kwolf@redhat.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
16
---
10
include/block/blockjob.h | 9 ---
17
include/block/block-io.h | 5 ++++-
11
include/block/blockjob_int.h | 36 -----------
18
include/block/block_int-common.h | 3 ++-
12
include/qemu/job.h | 53 +++++++++++++++-
19
block.c | 8 ++++----
13
block/backup.c | 22 +++----
20
block/blkio.c | 5 +++--
14
block/commit.c | 2 +-
21
block/crypto.c | 8 ++++----
15
block/mirror.c | 2 +-
22
block/file-posix.c | 7 ++++---
16
blockjob.c | 142 ++++++++-----------------------------------
23
block/io.c | 8 ++++----
17
job.c | 100 +++++++++++++++++++++++++++++-
24
block/iscsi.c | 7 ++++---
18
qemu-img.c | 2 +-
25
block/mirror.c | 2 +-
19
tests/test-blockjob.c | 10 +--
26
block/qcow.c | 5 +++--
20
10 files changed, 194 insertions(+), 184 deletions(-)
27
block/qcow2.c | 5 +++--
28
block/qed.c | 5 +++--
29
block/raw-format.c | 7 ++++---
30
block/rbd.c | 5 +++--
31
block/vdi.c | 7 ++++---
32
block/vhdx.c | 5 +++--
33
block/vmdk.c | 5 +++--
34
block/vpc.c | 5 +++--
35
18 files changed, 59 insertions(+), 43 deletions(-)
21
36
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
37
diff --git a/include/block/block-io.h b/include/block/block-io.h
23
index XXXXXXX..XXXXXXX 100644
38
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
39
--- a/include/block/block-io.h
25
+++ b/include/block/blockjob.h
40
+++ b/include/block/block-io.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
41
@@ -XXX,XX +XXX,XX @@ bool bdrv_supports_compressed_writes(BlockDriverState *bs);
27
/** Rate limiting data structure for implementing @speed. */
42
const char *bdrv_get_node_name(const BlockDriverState *bs);
28
RateLimit limit;
43
const char *bdrv_get_device_name(const BlockDriverState *bs);
29
44
const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
30
- /** The completion function that will be called when the job completes. */
45
-int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
31
- BlockCompletionFunc *cb;
46
+
32
-
47
+int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
33
/** Block other operations when block job is running */
48
+int co_wrapper_mixed bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
34
Error *blocker;
49
+
35
50
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs,
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
51
Error **errp);
37
/** BlockDriverStates that are involved in this block job */
52
BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
38
GSList *nodes;
53
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
39
54
index XXXXXXX..XXXXXXX 100644
40
- /** The opaque value that is passed to the completion function. */
55
--- a/include/block/block_int-common.h
41
- void *opaque;
56
+++ b/include/block/block_int-common.h
42
-
57
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
43
- /** ret code passed to block_job_completed. */
58
int64_t offset, int64_t bytes, QEMUIOVector *qiov,
44
- int ret;
59
size_t qiov_offset);
45
-
60
46
BlockJobTxn *txn;
61
- int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
47
QLIST_ENTRY(BlockJob) txn_list;
62
+ int coroutine_fn (*bdrv_co_get_info)(BlockDriverState *bs,
48
} BlockJob;
63
+ BlockDriverInfo *bdi);
49
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
64
50
index XXXXXXX..XXXXXXX 100644
65
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs,
51
--- a/include/block/blockjob_int.h
66
Error **errp);
52
+++ b/include/block/blockjob_int.h
67
diff --git a/block.c b/block.c
53
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
68
index XXXXXXX..XXXXXXX 100644
54
*/
69
--- a/block.c
55
int (*prepare)(BlockJob *job);
70
+++ b/block.c
56
71
@@ -XXX,XX +XXX,XX @@ void bdrv_get_backing_filename(BlockDriverState *bs,
57
- /**
72
pstrcpy(filename, filename_size, bs->backing_file);
58
- * If the callback is not NULL, it will be invoked when all the jobs
73
}
59
- * belonging to the same transaction complete; or upon this job's
74
60
- * completion if it is not in a transaction. Skipped if NULL.
75
-int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
61
- *
76
+int coroutine_fn bdrv_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
62
- * All jobs will complete with a call to either .commit() or .abort() but
77
{
63
- * never both.
78
int ret;
64
- */
79
BlockDriver *drv = bs->drv;
65
- void (*commit)(BlockJob *job);
80
@@ -XXX,XX +XXX,XX @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
66
-
81
if (!drv) {
67
- /**
82
return -ENOMEDIUM;
68
- * If the callback is not NULL, it will be invoked when any job in the
83
}
69
- * same transaction fails; or upon this job's failure (due to error or
84
- if (!drv->bdrv_get_info) {
70
- * cancellation) if it is not in a transaction. Skipped if NULL.
85
+ if (!drv->bdrv_co_get_info) {
71
- *
86
BlockDriverState *filtered = bdrv_filter_bs(bs);
72
- * All jobs will complete with a call to either .commit() or .abort() but
87
if (filtered) {
73
- * never both.
88
- return bdrv_get_info(filtered, bdi);
74
- */
89
+ return bdrv_co_get_info(filtered, bdi);
75
- void (*abort)(BlockJob *job);
90
}
76
-
91
return -ENOTSUP;
77
- /**
92
}
78
- * If the callback is not NULL, it will be invoked after a call to either
93
memset(bdi, 0, sizeof(*bdi));
79
- * .commit() or .abort(). Regardless of which callback is invoked after
94
- ret = drv->bdrv_get_info(bs, bdi);
80
- * completion, .clean() will always be called, even if the job does not
95
+ ret = drv->bdrv_co_get_info(bs, bdi);
81
- * belong to a transaction group.
96
if (ret < 0) {
82
- */
97
return ret;
83
- void (*clean)(BlockJob *job);
98
}
84
-
99
diff --git a/block/blkio.c b/block/blkio.c
85
/*
100
index XXXXXXX..XXXXXXX 100644
86
* If the callback is not NULL, it will be invoked before the job is
101
--- a/block/blkio.c
87
* resumed in a new AioContext. This is the place to move any resources
102
+++ b/block/blkio.c
88
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job);
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkio_truncate(BlockDriverState *bs, int64_t offset,
89
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
104
return 0;
90
105
}
106
107
-static int blkio_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
108
+static int coroutine_fn
109
+blkio_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
110
{
111
return 0;
112
}
113
@@ -XXX,XX +XXX,XX @@ static void blkio_refresh_limits(BlockDriverState *bs, Error **errp)
114
.bdrv_close = blkio_close, \
115
.bdrv_co_getlength = blkio_co_getlength, \
116
.bdrv_co_truncate = blkio_truncate, \
117
- .bdrv_get_info = blkio_get_info, \
118
+ .bdrv_co_get_info = blkio_co_get_info, \
119
.bdrv_attach_aio_context = blkio_attach_aio_context, \
120
.bdrv_detach_aio_context = blkio_detach_aio_context, \
121
.bdrv_co_pdiscard = blkio_co_pdiscard, \
122
diff --git a/block/crypto.c b/block/crypto.c
123
index XXXXXXX..XXXXXXX 100644
124
--- a/block/crypto.c
125
+++ b/block/crypto.c
126
@@ -XXX,XX +XXX,XX @@ fail:
127
return ret;
128
}
129
130
-static int block_crypto_get_info_luks(BlockDriverState *bs,
131
- BlockDriverInfo *bdi)
132
+static int coroutine_fn
133
+block_crypto_co_get_info_luks(BlockDriverState *bs, BlockDriverInfo *bdi)
134
{
135
BlockDriverInfo subbdi;
136
int ret;
137
138
- ret = bdrv_get_info(bs->file->bs, &subbdi);
139
+ ret = bdrv_co_get_info(bs->file->bs, &subbdi);
140
if (ret != 0) {
141
return ret;
142
}
143
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_crypto_luks = {
144
.bdrv_co_pwritev = block_crypto_co_pwritev,
145
.bdrv_co_getlength = block_crypto_co_getlength,
146
.bdrv_measure = block_crypto_measure,
147
- .bdrv_get_info = block_crypto_get_info_luks,
148
+ .bdrv_co_get_info = block_crypto_co_get_info_luks,
149
.bdrv_get_specific_info = block_crypto_get_specific_info_luks,
150
.bdrv_amend_options = block_crypto_amend_options_luks,
151
.bdrv_co_amend = block_crypto_co_amend_luks,
152
diff --git a/block/file-posix.c b/block/file-posix.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/file-posix.c
155
+++ b/block/file-posix.c
156
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_pwrite_zeroes(
157
return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false);
158
}
159
160
-static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
161
+static int coroutine_fn
162
+raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
163
{
164
return 0;
165
}
166
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
167
168
.bdrv_co_truncate = raw_co_truncate,
169
.bdrv_co_getlength = raw_co_getlength,
170
- .bdrv_get_info = raw_get_info,
171
+ .bdrv_co_get_info = raw_co_get_info,
172
.bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
173
.bdrv_get_specific_stats = raw_get_specific_stats,
174
.bdrv_check_perm = raw_check_perm,
175
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
176
177
.bdrv_co_truncate = raw_co_truncate,
178
.bdrv_co_getlength = raw_co_getlength,
179
- .bdrv_get_info = raw_get_info,
180
+ .bdrv_co_get_info = raw_co_get_info,
181
.bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
182
.bdrv_get_specific_stats = hdev_get_specific_stats,
183
.bdrv_check_perm = raw_check_perm,
184
diff --git a/block/io.c b/block/io.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/block/io.c
187
+++ b/block/io.c
188
@@ -XXX,XX +XXX,XX @@ BdrvTrackedRequest *coroutine_fn bdrv_co_get_self_request(BlockDriverState *bs)
91
/**
189
/**
92
- * block_job_early_fail:
190
* Round a region to cluster boundaries
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
};
167
@@ -XXX,XX +XXX,XX @@ typedef enum JobCreateFlags {
168
* @driver: The class object for the newly-created job.
169
* @ctx: The AioContext to run the job coroutine in.
170
* @flags: Creation flags for the job. See @JobCreateFlags.
171
+ * @cb: Completion function for the job.
172
+ * @opaque: Opaque pointer value passed to @cb.
173
* @errp: Error object.
174
*/
191
*/
175
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
192
-void bdrv_round_to_clusters(BlockDriverState *bs,
176
- int flags, Error **errp);
193
+void coroutine_fn bdrv_round_to_clusters(BlockDriverState *bs,
177
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
194
int64_t offset, int64_t bytes,
178
195
int64_t *cluster_offset,
179
/**
196
int64_t *cluster_bytes)
180
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
197
{
181
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id);
198
BlockDriverInfo bdi;
182
*/
199
IO_CODE();
183
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
200
- if (bdrv_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) {
184
201
+ if (bdrv_co_get_info(bs, &bdi) < 0 || bdi.cluster_size == 0) {
185
+/** The @job could not be started, free it. */
202
*cluster_offset = offset;
186
+void job_early_fail(Job *job);
203
*cluster_bytes = bytes;
187
+
204
} else {
188
+
205
@@ -XXX,XX +XXX,XX @@ void bdrv_round_to_clusters(BlockDriverState *bs,
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
}
206
}
210
}
207
}
211
208
212
-static void backup_commit(BlockJob *job)
209
-static int bdrv_get_cluster_size(BlockDriverState *bs)
213
+static void backup_commit(Job *job)
210
+static coroutine_fn int bdrv_get_cluster_size(BlockDriverState *bs)
214
{
211
{
215
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
212
BlockDriverInfo bdi;
216
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
213
int ret;
217
if (s->sync_bitmap) {
214
218
backup_cleanup_sync_bitmap(s, 0);
215
- ret = bdrv_get_info(bs, &bdi);
219
}
216
+ ret = bdrv_co_get_info(bs, &bdi);
220
}
217
if (ret < 0 || bdi.cluster_size == 0) {
221
218
return bs->bl.request_alignment;
222
-static void backup_abort(BlockJob *job)
219
} else {
223
+static void backup_abort(Job *job)
220
diff --git a/block/iscsi.c b/block/iscsi.c
224
{
221
index XXXXXXX..XXXXXXX 100644
225
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
222
--- a/block/iscsi.c
226
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
223
+++ b/block/iscsi.c
227
if (s->sync_bitmap) {
224
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn iscsi_co_truncate(BlockDriverState *bs, int64_t offset,
228
backup_cleanup_sync_bitmap(s, -1);
225
return 0;
229
}
226
}
230
}
227
231
228
-static int iscsi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
232
-static void backup_clean(BlockJob *job)
229
+static int coroutine_fn
233
+static void backup_clean(Job *job)
230
+iscsi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
234
{
231
{
235
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
232
IscsiLun *iscsilun = bs->opaque;
236
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
233
bdi->cluster_size = iscsilun->cluster_size;
237
assert(s->target);
234
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_iscsi = {
238
blk_unref(s->target);
235
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
239
s->target = NULL;
236
240
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
237
.bdrv_co_getlength = iscsi_co_getlength,
241
.free = block_job_free,
238
- .bdrv_get_info = iscsi_get_info,
242
.user_resume = block_job_user_resume,
239
+ .bdrv_co_get_info = iscsi_co_get_info,
243
.start = backup_run,
240
.bdrv_co_truncate = iscsi_co_truncate,
244
+ .commit = backup_commit,
241
.bdrv_refresh_limits = iscsi_refresh_limits,
245
+ .abort = backup_abort,
242
246
+ .clean = backup_clean,
243
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_iser = {
247
},
244
.bdrv_co_invalidate_cache = iscsi_co_invalidate_cache,
248
- .commit = backup_commit,
245
249
- .abort = backup_abort,
246
.bdrv_co_getlength = iscsi_co_getlength,
250
- .clean = backup_clean,
247
- .bdrv_get_info = iscsi_get_info,
251
.attached_aio_context = backup_attached_aio_context,
248
+ .bdrv_co_get_info = iscsi_co_get_info,
252
.drain = backup_drain,
249
.bdrv_co_truncate = iscsi_co_truncate,
253
};
250
.bdrv_refresh_limits = iscsi_refresh_limits,
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
251
278
diff --git a/block/mirror.c b/block/mirror.c
252
diff --git a/block/mirror.c b/block/mirror.c
279
index XXXXXXX..XXXXXXX 100644
253
index XXXXXXX..XXXXXXX 100644
280
--- a/block/mirror.c
254
--- a/block/mirror.c
281
+++ b/block/mirror.c
255
+++ b/block/mirror.c
256
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_run(Job *job, Error **errp)
257
*/
258
bdrv_get_backing_filename(target_bs, backing_filename,
259
sizeof(backing_filename));
260
- if (!bdrv_get_info(target_bs, &bdi) && bdi.cluster_size) {
261
+ if (!bdrv_co_get_info(target_bs, &bdi) && bdi.cluster_size) {
262
s->target_cluster_size = bdi.cluster_size;
263
} else {
264
s->target_cluster_size = BDRV_SECTOR_SIZE;
265
diff --git a/block/qcow.c b/block/qcow.c
266
index XXXXXXX..XXXXXXX 100644
267
--- a/block/qcow.c
268
+++ b/block/qcow.c
282
@@ -XXX,XX +XXX,XX @@ fail:
269
@@ -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;
270
return ret;
419
}
271
}
420
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
272
421
assert(job_is_cancelled(&other_job->job));
273
-static int qcow_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
422
block_job_finish_sync(other_job, NULL, NULL);
274
+static int coroutine_fn
423
}
275
+qcow_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
424
- block_job_finalize_single(other_job);
276
{
425
+ job_finalize_single(&other_job->job);
277
BDRVQcowState *s = bs->opaque;
426
aio_context_release(ctx);
278
bdi->cluster_size = s->cluster_size;
427
}
279
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qcow = {
428
280
429
@@ -XXX,XX +XXX,XX @@ static int block_job_needs_finalize(BlockJob *job)
281
.bdrv_make_empty = qcow_make_empty,
430
return !job->job.auto_finalize;
282
.bdrv_co_pwritev_compressed = qcow_co_pwritev_compressed,
431
}
283
- .bdrv_get_info = qcow_get_info,
432
284
+ .bdrv_co_get_info = qcow_co_get_info,
433
+static int block_job_finalize_single(BlockJob *job)
285
434
+{
286
.create_opts = &qcow_create_opts,
435
+ return job_finalize_single(&job->job);
287
.strong_runtime_opts = qcow_strong_runtime_opts,
436
+}
288
diff --git a/block/qcow2.c b/block/qcow2.c
437
+
289
index XXXXXXX..XXXXXXX 100644
438
static void block_job_do_finalize(BlockJob *job)
290
--- a/block/qcow2.c
439
{
291
+++ b/block/qcow2.c
440
int rc;
292
@@ -XXX,XX +XXX,XX @@ err:
441
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
293
return NULL;
442
if (!job_is_completed(&other_job->job)) {
294
}
443
return;
295
444
}
296
-static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
445
- assert(other_job->ret == 0);
297
+static int coroutine_fn
446
+ assert(other_job->job.ret == 0);
298
+qcow2_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
447
}
299
{
448
300
BDRVQcow2State *s = bs->opaque;
449
block_job_txn_apply(txn, block_job_transition_to_pending, false);
301
bdi->cluster_size = s->cluster_size;
450
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
302
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
451
return;
303
.bdrv_snapshot_list = qcow2_snapshot_list,
452
}
304
.bdrv_snapshot_load_tmp = qcow2_snapshot_load_tmp,
453
305
.bdrv_measure = qcow2_measure,
454
- block_job_do_dismiss(job);
306
- .bdrv_get_info = qcow2_get_info,
455
+ job_do_dismiss(&job->job);
307
+ .bdrv_co_get_info = qcow2_co_get_info,
456
*jobptr = NULL;
308
.bdrv_get_specific_info = qcow2_get_specific_info,
457
}
309
458
310
.bdrv_save_vmstate = qcow2_save_vmstate,
459
void block_job_cancel(BlockJob *job, bool force)
311
diff --git a/block/qed.c b/block/qed.c
460
{
312
index XXXXXXX..XXXXXXX 100644
461
if (job->job.status == JOB_STATUS_CONCLUDED) {
313
--- a/block/qed.c
462
- block_job_do_dismiss(job);
314
+++ b/block/qed.c
463
+ job_do_dismiss(&job->job);
315
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn bdrv_qed_co_getlength(BlockDriverState *bs)
464
return;
316
return s->header.image_size;
465
}
317
}
466
block_job_cancel_async(job, force);
318
467
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
319
-static int bdrv_qed_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
468
info->status = job->job.status;
320
+static int coroutine_fn
469
info->auto_finalize = job->job.auto_finalize;
321
+bdrv_qed_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
470
info->auto_dismiss = job->job.auto_dismiss;
322
{
471
- info->has_error = job->ret != 0;
323
BDRVQEDState *s = bs->opaque;
472
- info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
324
473
+ info->has_error = job->job.ret != 0;
325
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
474
+ info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
326
.bdrv_co_pwrite_zeroes = bdrv_qed_co_pwrite_zeroes,
327
.bdrv_co_truncate = bdrv_qed_co_truncate,
328
.bdrv_co_getlength = bdrv_qed_co_getlength,
329
- .bdrv_get_info = bdrv_qed_get_info,
330
+ .bdrv_co_get_info = bdrv_qed_co_get_info,
331
.bdrv_refresh_limits = bdrv_qed_refresh_limits,
332
.bdrv_change_backing_file = bdrv_qed_change_backing_file,
333
.bdrv_co_invalidate_cache = bdrv_qed_co_invalidate_cache,
334
diff --git a/block/raw-format.c b/block/raw-format.c
335
index XXXXXXX..XXXXXXX 100644
336
--- a/block/raw-format.c
337
+++ b/block/raw-format.c
338
@@ -XXX,XX +XXX,XX @@ static BlockMeasureInfo *raw_measure(QemuOpts *opts, BlockDriverState *in_bs,
475
return info;
339
return info;
476
}
340
}
477
341
478
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
342
-static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
479
return;
343
+static int coroutine_fn
480
}
344
+raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
481
345
{
482
- if (job->ret < 0) {
346
- return bdrv_get_info(bs->file->bs, bdi);
483
- msg = strerror(-job->ret);
347
+ return bdrv_co_get_info(bs->file->bs, bdi);
484
+ if (job->job.ret < 0) {
348
}
485
+ msg = strerror(-job->job.ret);
349
486
}
350
static void raw_refresh_limits(BlockDriverState *bs, Error **errp)
487
351
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
488
qapi_event_send_block_job_completed(job_type(&job->job),
352
.is_format = true,
489
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
353
.has_variable_length = true,
490
}
354
.bdrv_measure = &raw_measure,
491
355
- .bdrv_get_info = &raw_get_info,
492
job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
356
+ .bdrv_co_get_info = &raw_co_get_info,
493
- flags, errp);
357
.bdrv_refresh_limits = &raw_refresh_limits,
494
+ flags, cb, opaque, errp);
358
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
495
if (job == NULL) {
359
.bdrv_probe_geometry = &raw_probe_geometry,
496
blk_unref(blk);
360
diff --git a/block/rbd.c b/block/rbd.c
497
return NULL;
361
index XXXXXXX..XXXXXXX 100644
498
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
362
--- a/block/rbd.c
499
363
+++ b/block/rbd.c
500
job->driver = driver;
364
@@ -XXX,XX +XXX,XX @@ coroutine_fn qemu_rbd_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset,
501
job->blk = blk;
365
}
502
- job->cb = cb;
366
#endif
503
- job->opaque = opaque;
367
504
368
-static int qemu_rbd_getinfo(BlockDriverState *bs, BlockDriverInfo *bdi)
505
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
369
+static int coroutine_fn
506
job->finalize_completed_notifier.notify = block_job_event_completed;
370
+qemu_rbd_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
507
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
371
{
508
372
BDRVRBDState *s = bs->opaque;
509
block_job_set_speed(job, speed, &local_err);
373
bdi->cluster_size = s->object_size;
510
if (local_err) {
374
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_rbd = {
511
- block_job_early_fail(job);
375
.bdrv_co_create = qemu_rbd_co_create,
512
+ job_early_fail(&job->job);
376
.bdrv_co_create_opts = qemu_rbd_co_create_opts,
513
error_propagate(errp, local_err);
377
.bdrv_has_zero_init = bdrv_has_zero_init_1,
514
return NULL;
378
- .bdrv_get_info = qemu_rbd_getinfo,
515
}
379
+ .bdrv_co_get_info = qemu_rbd_co_get_info,
516
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
380
.bdrv_get_specific_info = qemu_rbd_get_specific_info,
517
return job;
381
.create_opts = &qemu_rbd_create_opts,
518
}
382
.bdrv_co_getlength = qemu_rbd_co_getlength,
519
383
diff --git a/block/vdi.c b/block/vdi.c
520
-void block_job_early_fail(BlockJob *job)
384
index XXXXXXX..XXXXXXX 100644
521
-{
385
--- a/block/vdi.c
522
- assert(job->job.status == JOB_STATUS_CREATED);
386
+++ b/block/vdi.c
523
- block_job_decommission(job);
387
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res,
524
-}
388
return 0;
525
-
389
}
526
void block_job_completed(BlockJob *job, int ret)
390
527
{
391
-static int vdi_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
528
assert(job && job->txn && !job_is_completed(&job->job));
392
+static int coroutine_fn
529
assert(blk_bs(job->blk)->job == job);
393
+vdi_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
530
- job->ret = ret;
394
{
531
- block_job_update_rc(job);
395
- /* TODO: vdi_get_info would be needed for machine snapshots.
532
- trace_block_job_completed(job, ret, job->ret);
396
+ /* TODO: vdi_co_get_info would be needed for machine snapshots.
533
- if (job->ret) {
397
vm_state_offset is still missing. */
534
+ job->job.ret = ret;
398
BDRVVdiState *s = (BDRVVdiState *)bs->opaque;
535
+ job_update_rc(&job->job);
399
logout("\n");
536
+ trace_block_job_completed(job, ret, job->job.ret);
400
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = {
537
+ if (job->job.ret) {
401
.bdrv_co_pwritev = vdi_co_pwritev,
538
block_job_completed_txn_abort(job);
402
#endif
539
} else {
403
540
block_job_completed_txn_success(job);
404
- .bdrv_get_info = vdi_get_info,
541
diff --git a/job.c b/job.c
405
+ .bdrv_co_get_info = vdi_co_get_info,
542
index XXXXXXX..XXXXXXX 100644
406
543
--- a/job.c
407
.is_format = true,
544
+++ b/job.c
408
.create_opts = &vdi_create_opts,
545
@@ -XXX,XX +XXX,XX @@ void job_state_transition(Job *job, JobStatus s1)
409
diff --git a/block/vhdx.c b/block/vhdx.c
546
{
410
index XXXXXXX..XXXXXXX 100644
547
JobStatus s0 = job->status;
411
--- a/block/vhdx.c
548
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
412
+++ b/block/vhdx.c
549
- trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
413
@@ -XXX,XX +XXX,XX @@ static void vhdx_block_translate(BDRVVHDXState *s, int64_t sector_num,
550
+ trace_job_state_transition(job, job->ret,
414
}
551
JobSTT[s0][s1] ? "allowed" : "disallowed",
415
552
JobStatus_str(s0), JobStatus_str(s1));
416
553
assert(JobSTT[s0][s1]);
417
-static int vhdx_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
554
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
418
+static int coroutine_fn
555
}
419
+vhdx_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
556
420
{
557
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
421
BDRVVHDXState *s = bs->opaque;
558
- int flags, Error **errp)
422
559
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp)
423
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vhdx = {
560
{
424
.bdrv_co_writev = vhdx_co_writev,
561
Job *job;
425
.bdrv_co_create = vhdx_co_create,
562
426
.bdrv_co_create_opts = vhdx_co_create_opts,
563
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
427
- .bdrv_get_info = vhdx_get_info,
564
job->pause_count = 1;
428
+ .bdrv_co_get_info = vhdx_co_get_info,
565
job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
429
.bdrv_co_check = vhdx_co_check,
566
job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
430
.bdrv_has_zero_init = vhdx_has_zero_init,
567
+ job->cb = cb;
431
568
+ job->opaque = opaque;
432
diff --git a/block/vmdk.c b/block/vmdk.c
569
433
index XXXXXXX..XXXXXXX 100644
570
notifier_list_init(&job->on_finalize_cancelled);
434
--- a/block/vmdk.c
571
notifier_list_init(&job->on_finalize_completed);
435
+++ b/block/vmdk.c
572
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
436
@@ -XXX,XX +XXX,XX @@ static bool vmdk_extents_type_eq(const VmdkExtent *a, const VmdkExtent *b)
573
job_resume(job);
437
(a->flat || a->cluster_sectors == b->cluster_sectors);
574
}
438
}
575
439
576
+void job_do_dismiss(Job *job)
440
-static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
577
+{
441
+static int coroutine_fn
578
+ assert(job);
442
+vmdk_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
579
+ job->busy = false;
443
{
580
+ job->paused = false;
444
int i;
581
+ job->deferred_to_main_loop = true;
445
BDRVVmdkState *s = bs->opaque;
582
+
446
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vmdk = {
583
+ /* TODO Don't assume it's a BlockJob */
447
.bdrv_has_zero_init = vmdk_has_zero_init,
584
+ block_job_txn_del_job((BlockJob*) job);
448
.bdrv_get_specific_info = vmdk_get_specific_info,
585
+
449
.bdrv_refresh_limits = vmdk_refresh_limits,
586
+ job_state_transition(job, JOB_STATUS_NULL);
450
- .bdrv_get_info = vmdk_get_info,
587
+ job_unref(job);
451
+ .bdrv_co_get_info = vmdk_co_get_info,
588
+}
452
.bdrv_gather_child_options = vmdk_gather_child_options,
589
+
453
590
+void job_early_fail(Job *job)
454
.is_format = true,
591
+{
455
diff --git a/block/vpc.c b/block/vpc.c
592
+ assert(job->status == JOB_STATUS_CREATED);
456
index XXXXXXX..XXXXXXX 100644
593
+ job_do_dismiss(job);
457
--- a/block/vpc.c
594
+}
458
+++ b/block/vpc.c
595
+
459
@@ -XXX,XX +XXX,XX @@ fail:
596
+static void job_conclude(Job *job)
460
return ret;
597
+{
461
}
598
+ job_state_transition(job, JOB_STATUS_CONCLUDED);
462
599
+ if (job->auto_dismiss || !job_started(job)) {
463
-static int vpc_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
600
+ job_do_dismiss(job);
464
+static int coroutine_fn
601
+ }
465
+vpc_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
602
+}
466
{
603
+
467
BDRVVPCState *s = (BDRVVPCState *)bs->opaque;
604
+void job_update_rc(Job *job)
468
605
+{
469
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vpc = {
606
+ if (!job->ret && job_is_cancelled(job)) {
470
.bdrv_co_pwritev = vpc_co_pwritev,
607
+ job->ret = -ECANCELED;
471
.bdrv_co_block_status = vpc_co_block_status,
608
+ }
472
609
+ if (job->ret) {
473
- .bdrv_get_info = vpc_get_info,
610
+ job_state_transition(job, JOB_STATUS_ABORTING);
474
+ .bdrv_co_get_info = vpc_co_get_info,
611
+ }
475
612
+}
476
.is_format = true,
613
+
477
.create_opts = &vpc_create_opts,
614
+static void job_commit(Job *job)
615
+{
616
+ assert(!job->ret);
617
+ if (job->driver->commit) {
618
+ job->driver->commit(job);
619
+ }
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
--
478
--
718
2.13.6
479
2.38.1
719
720
diff view generated by jsdifflib
1
There is nothing block layer specific about block_job_sleep_ns(), so
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
move the function to Job.
2
3
3
bdrv_eject() is categorized as an I/O function, and it currently
4
doesn't run in a coroutine. We should let it take a graph rdlock since
5
it traverses the block nodes graph, which however is only possible in a
6
coroutine.
7
8
The only caller of this function is blk_eject(). Therefore make
9
blk_eject() a co_wrapper, so that it always creates a new coroutine, and
10
then make bdrv_eject() coroutine_fn where the lock can be taken.
11
12
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: John Snow <jsnow@redhat.com>
14
Message-Id: <20230113204212.359076-12-kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
17
---
8
include/block/blockjob_int.h | 11 -----------
18
include/block/block-io.h | 3 ++-
9
include/qemu/job.h | 19 ++++++++++++++++++-
19
include/block/block_int-common.h | 2 +-
10
block/backup.c | 2 +-
20
include/sysemu/block-backend-io.h | 4 +++-
11
block/commit.c | 2 +-
21
block.c | 6 +++---
12
block/mirror.c | 4 ++--
22
block/block-backend.c | 4 ++--
13
block/stream.c | 2 +-
23
block/copy-on-read.c | 6 +++---
14
blockjob.c | 27 ---------------------------
24
block/file-posix.c | 8 ++++----
15
job.c | 32 ++++++++++++++++++++++++++++++++
25
block/filter-compress.c | 7 ++++---
16
tests/test-bdrv-drain.c | 8 ++++----
26
block/raw-format.c | 6 +++---
17
tests/test-blockjob-txn.c | 2 +-
27
9 files changed, 25 insertions(+), 21 deletions(-)
18
tests/test-blockjob.c | 2 +-
28
19
11 files changed, 61 insertions(+), 50 deletions(-)
29
diff --git a/include/block/block-io.h b/include/block/block-io.h
20
30
index XXXXXXX..XXXXXXX 100644
21
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
31
--- a/include/block/block-io.h
22
index XXXXXXX..XXXXXXX 100644
32
+++ b/include/block/block-io.h
23
--- a/include/block/blockjob_int.h
33
@@ -XXX,XX +XXX,XX @@ bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs);
24
+++ b/include/block/blockjob_int.h
34
bool co_wrapper bdrv_is_inserted(BlockDriverState *bs);
25
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
35
26
void block_job_free(Job *job);
36
void bdrv_lock_medium(BlockDriverState *bs, bool locked);
27
37
-void bdrv_eject(BlockDriverState *bs, bool eject_flag);
38
+void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag);
39
+
40
const char *bdrv_get_format_name(BlockDriverState *bs);
41
42
bool bdrv_supports_compressed_writes(BlockDriverState *bs);
43
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
44
index XXXXXXX..XXXXXXX 100644
45
--- a/include/block/block_int-common.h
46
+++ b/include/block/block_int-common.h
47
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
48
49
/* removable device specific */
50
bool coroutine_fn (*bdrv_co_is_inserted)(BlockDriverState *bs);
51
- void (*bdrv_eject)(BlockDriverState *bs, bool eject_flag);
52
+ void coroutine_fn (*bdrv_co_eject)(BlockDriverState *bs, bool eject_flag);
53
void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
54
55
/* to control generic scsi devices */
56
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
57
index XXXXXXX..XXXXXXX 100644
58
--- a/include/sysemu/block-backend-io.h
59
+++ b/include/sysemu/block-backend-io.h
60
@@ -XXX,XX +XXX,XX @@ bool co_wrapper_mixed blk_is_inserted(BlockBackend *blk);
61
62
bool blk_is_available(BlockBackend *blk);
63
void blk_lock_medium(BlockBackend *blk, bool locked);
64
-void blk_eject(BlockBackend *blk, bool eject_flag);
65
+
66
+void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag);
67
+void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag);
68
69
int64_t coroutine_fn blk_co_getlength(BlockBackend *blk);
70
int64_t co_wrapper_mixed blk_getlength(BlockBackend *blk);
71
diff --git a/block.c b/block.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/block.c
74
+++ b/block.c
75
@@ -XXX,XX +XXX,XX @@ bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs)
28
/**
76
/**
29
- * block_job_sleep_ns:
77
* If eject_flag is TRUE, eject the media. Otherwise, close the tray
30
- * @job: The job that calls the function.
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
+ *
62
+ * Continue the specified job by entering the coroutine.
63
+ */
64
+void job_enter(Job *job);
65
+
66
+/**
67
* @job: The job that is ready to pause.
68
*
69
* Pause now if job_pause() has been called. Jobs that perform lots of I/O
70
@@ -XXX,XX +XXX,XX @@ void job_start(Job *job);
71
*/
78
*/
72
void coroutine_fn job_pause_point(Job *job);
79
-void bdrv_eject(BlockDriverState *bs, bool eject_flag)
73
80
+void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag)
74
+/**
81
{
75
+ * @job: The job that calls the function.
82
BlockDriver *drv = bs->drv;
76
+ * @ns: How many nanoseconds to stop for.
83
IO_CODE();
77
+ *
84
78
+ * Put the job to sleep (assuming that it wasn't canceled) for @ns
85
- if (drv && drv->bdrv_eject) {
79
+ * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately
86
- drv->bdrv_eject(bs, eject_flag);
80
+ * interrupt the wait.
87
+ if (drv && drv->bdrv_co_eject) {
81
+ */
88
+ drv->bdrv_co_eject(bs, eject_flag);
82
+void coroutine_fn job_sleep_ns(Job *job, int64_t ns);
83
+
84
85
/** Returns the JobType of a given Job. */
86
JobType job_type(const Job *job);
87
diff --git a/block/backup.c b/block/backup.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/block/backup.c
90
+++ b/block/backup.c
91
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
92
* return. Without a yield, the VM would not reboot. */
93
delay_ns = block_job_ratelimit_get_delay(&job->common, job->bytes_read);
94
job->bytes_read = 0;
95
- block_job_sleep_ns(&job->common, delay_ns);
96
+ job_sleep_ns(&job->common.job, delay_ns);
97
98
if (job_is_cancelled(&job->common.job)) {
99
return true;
100
diff --git a/block/commit.c b/block/commit.c
101
index XXXXXXX..XXXXXXX 100644
102
--- a/block/commit.c
103
+++ b/block/commit.c
104
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
105
/* Note that even when no rate limit is applied we need to yield
106
* with no pending I/O here so that bdrv_drain_all() returns.
107
*/
108
- block_job_sleep_ns(&s->common, delay_ns);
109
+ job_sleep_ns(&s->common.job, delay_ns);
110
if (job_is_cancelled(&s->common.job)) {
111
break;
112
}
113
diff --git a/block/mirror.c b/block/mirror.c
114
index XXXXXXX..XXXXXXX 100644
115
--- a/block/mirror.c
116
+++ b/block/mirror.c
117
@@ -XXX,XX +XXX,XX @@ static void mirror_throttle(MirrorBlockJob *s)
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 {
124
job_pause_point(&s->common.job);
125
}
89
}
126
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
90
}
127
cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0);
91
128
}
92
diff --git a/block/block-backend.c b/block/block-backend.c
129
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
93
index XXXXXXX..XXXXXXX 100644
130
- block_job_sleep_ns(&s->common, delay_ns);
94
--- a/block/block-backend.c
131
+ job_sleep_ns(&s->common.job, delay_ns);
95
+++ b/block/block-backend.c
132
if (job_is_cancelled(&s->common.job) &&
96
@@ -XXX,XX +XXX,XX @@ void blk_lock_medium(BlockBackend *blk, bool locked)
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
}
158
159
static void block_job_attached_aio_context(AioContext *new_context,
160
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
161
return job->driver;
162
}
163
164
-static void block_job_sleep_timer_cb(void *opaque)
165
-{
166
- BlockJob *job = opaque;
167
-
168
- block_job_enter(job);
169
-}
170
-
171
static void block_job_decommission(BlockJob *job)
172
{
173
assert(job);
174
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
175
job->opaque = opaque;
176
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
177
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
178
- aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer,
179
- QEMU_CLOCK_REALTIME, SCALE_NS,
180
- block_job_sleep_timer_cb, job);
181
182
error_setg(&job->blocker, "block device is in use by block job: %s",
183
job_type_str(&job->job));
184
@@ -XXX,XX +XXX,XX @@ void block_job_enter(BlockJob *job)
185
job_enter_cond(&job->job, NULL);
186
}
187
188
-void block_job_sleep_ns(BlockJob *job, int64_t ns)
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
{
206
assert(job->job.busy);
207
diff --git a/job.c b/job.c
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
+}
221
+
222
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
223
Error **errp)
224
{
225
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
226
job->pause_count = 1;
227
228
job_state_transition(job, JOB_STATUS_CREATED);
229
+ aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
230
+ QEMU_CLOCK_REALTIME, SCALE_NS,
231
+ job_sleep_timer_cb, job);
232
233
QLIST_INSERT_HEAD(&jobs, job, job_list);
234
235
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
236
{
237
if (--job->refcnt == 0) {
238
assert(job->status == JOB_STATUS_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
+}
251
+
252
/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
253
* Reentering the job coroutine with block_job_enter() before the timer has
254
* expired is allowed and cancels the timer.
255
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job)
256
}
97
}
257
}
98
}
258
99
259
+void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
100
-void blk_eject(BlockBackend *blk, bool eject_flag)
260
+{
101
+void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag)
261
+ assert(job->busy);
102
{
262
+
103
BlockDriverState *bs = blk_bs(blk);
263
+ /* Check cancellation *before* setting busy = false, too! */
104
char *id;
264
+ if (job_is_cancelled(job)) {
105
IO_CODE();
265
+ return;
106
266
+ }
107
if (bs) {
267
+
108
- bdrv_eject(bs, eject_flag);
268
+ if (!job_should_pause(job)) {
109
+ bdrv_co_eject(bs, eject_flag);
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
}
110
}
289
111
290
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
112
/* Whether or not we ejected on the backend,
291
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
113
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
292
114
index XXXXXXX..XXXXXXX 100644
293
g_assert_cmpint(job->job.pause_count, ==, 0);
115
--- a/block/copy-on-read.c
294
g_assert_false(job->job.paused);
116
+++ b/block/copy-on-read.c
295
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
117
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn cor_co_pwritev_compressed(BlockDriverState *bs,
296
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
118
}
297
119
298
do_drain_begin(drain_type, src);
120
299
121
-static void cor_eject(BlockDriverState *bs, bool eject_flag)
300
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
122
+static void coroutine_fn cor_co_eject(BlockDriverState *bs, bool eject_flag)
301
123
{
302
g_assert_cmpint(job->job.pause_count, ==, 0);
124
- bdrv_eject(bs->file->bs, eject_flag);
303
g_assert_false(job->job.paused);
125
+ bdrv_co_eject(bs->file->bs, eject_flag);
304
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
126
}
305
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
127
306
128
307
do_drain_begin(drain_type, target);
129
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
308
130
.bdrv_co_pdiscard = cor_co_pdiscard,
309
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
131
.bdrv_co_pwritev_compressed = cor_co_pwritev_compressed,
310
132
311
g_assert_cmpint(job->job.pause_count, ==, 0);
133
- .bdrv_eject = cor_eject,
312
g_assert_false(job->job.paused);
134
+ .bdrv_co_eject = cor_co_eject,
313
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
135
.bdrv_lock_medium = cor_lock_medium,
314
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
136
315
137
.has_variable_length = true,
316
ret = block_job_complete_sync(job, &error_abort);
138
diff --git a/block/file-posix.c b/block/file-posix.c
317
g_assert_cmpint(ret, ==, 0);
139
index XXXXXXX..XXXXXXX 100644
318
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
140
--- a/block/file-posix.c
319
index XXXXXXX..XXXXXXX 100644
141
+++ b/block/file-posix.c
320
--- a/tests/test-blockjob-txn.c
142
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs)
321
+++ b/tests/test-blockjob-txn.c
143
return ret == CDS_DISC_OK;
322
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
144
}
323
145
324
while (s->iterations--) {
146
-static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
325
if (s->use_timer) {
147
+static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag)
326
- block_job_sleep_ns(job, 0);
148
{
327
+ job_sleep_ns(&job->job, 0);
149
BDRVRawState *s = bs->opaque;
328
} else {
150
329
block_job_yield(job);
151
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
330
}
152
331
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
153
/* removable device support */
332
index XXXXXXX..XXXXXXX 100644
154
.bdrv_co_is_inserted = cdrom_co_is_inserted,
333
--- a/tests/test-blockjob.c
155
- .bdrv_eject = cdrom_eject,
334
+++ b/tests/test-blockjob.c
156
+ .bdrv_co_eject = cdrom_co_eject,
335
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
157
.bdrv_lock_medium = cdrom_lock_medium,
336
block_job_event_ready(&s->common);
158
337
}
159
/* generic scsi device */
338
160
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn cdrom_co_is_inserted(BlockDriverState *bs)
339
- block_job_sleep_ns(&s->common, 100000);
161
return raw_co_getlength(bs) > 0;
340
+ job_sleep_ns(&s->common.job, 100000);
162
}
341
}
163
342
164
-static void cdrom_eject(BlockDriverState *bs, bool eject_flag)
343
defer:
165
+static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag)
166
{
167
BDRVRawState *s = bs->opaque;
168
169
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
170
171
/* removable device support */
172
.bdrv_co_is_inserted = cdrom_co_is_inserted,
173
- .bdrv_eject = cdrom_eject,
174
+ .bdrv_co_eject = cdrom_co_eject,
175
.bdrv_lock_medium = cdrom_lock_medium,
176
};
177
#endif /* __FreeBSD__ */
178
diff --git a/block/filter-compress.c b/block/filter-compress.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/filter-compress.c
181
+++ b/block/filter-compress.c
182
@@ -XXX,XX +XXX,XX @@ static void compress_refresh_limits(BlockDriverState *bs, Error **errp)
183
}
184
185
186
-static void compress_eject(BlockDriverState *bs, bool eject_flag)
187
+static void coroutine_fn
188
+compress_co_eject(BlockDriverState *bs, bool eject_flag)
189
{
190
- bdrv_eject(bs->file->bs, eject_flag);
191
+ bdrv_co_eject(bs->file->bs, eject_flag);
192
}
193
194
195
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_compress = {
196
.bdrv_co_pdiscard = compress_co_pdiscard,
197
.bdrv_refresh_limits = compress_refresh_limits,
198
199
- .bdrv_eject = compress_eject,
200
+ .bdrv_co_eject = compress_co_eject,
201
.bdrv_lock_medium = compress_lock_medium,
202
203
.has_variable_length = true,
204
diff --git a/block/raw-format.c b/block/raw-format.c
205
index XXXXXXX..XXXXXXX 100644
206
--- a/block/raw-format.c
207
+++ b/block/raw-format.c
208
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset,
209
return bdrv_co_truncate(bs->file, offset, exact, prealloc, flags, errp);
210
}
211
212
-static void raw_eject(BlockDriverState *bs, bool eject_flag)
213
+static void coroutine_fn raw_co_eject(BlockDriverState *bs, bool eject_flag)
214
{
215
- bdrv_eject(bs->file->bs, eject_flag);
216
+ bdrv_co_eject(bs->file->bs, eject_flag);
217
}
218
219
static void raw_lock_medium(BlockDriverState *bs, bool locked)
220
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
221
.bdrv_refresh_limits = &raw_refresh_limits,
222
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
223
.bdrv_probe_geometry = &raw_probe_geometry,
224
- .bdrv_eject = &raw_eject,
225
+ .bdrv_co_eject = &raw_co_eject,
226
.bdrv_lock_medium = &raw_lock_medium,
227
.bdrv_co_ioctl = &raw_co_ioctl,
228
.create_opts = &raw_create_opts,
344
--
229
--
345
2.13.6
230
2.38.1
346
347
diff view generated by jsdifflib
1
This adds a QMP event that is emitted whenever a job transitions from
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
one status to another.
2
3
3
bdrv_lock_medium() is categorized as an I/O function, and it currently
4
doesn't run in a coroutine. We should let it take a graph rdlock since
5
it traverses the block nodes graph, which however is only possible in a
6
coroutine.
7
8
The only caller of this function is blk_lock_medium(). Therefore make
9
blk_lock_medium() a co_wrapper, so that it always creates a new
10
coroutine, and then make bdrv_lock_medium() a coroutine_fn where the
11
lock can be taken.
12
13
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
15
Message-Id: <20230113204212.359076-13-kwolf@redhat.com>
16
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
18
---
7
qapi/job.json | 14 ++++
19
include/block/block-io.h | 2 +-
8
job.c | 10 +++
20
include/block/block_int-common.h | 2 +-
9
tests/qemu-iotests/030 | 17 +++-
21
include/sysemu/block-backend-io.h | 4 +++-
10
tests/qemu-iotests/040 | 2 +
22
block.c | 6 +++---
11
tests/qemu-iotests/041 | 17 +++-
23
block/block-backend.c | 4 ++--
12
tests/qemu-iotests/094.out | 7 ++
24
block/copy-on-read.c | 6 +++---
13
tests/qemu-iotests/095 | 2 +-
25
block/file-posix.c | 8 ++++----
14
tests/qemu-iotests/095.out | 6 ++
26
block/filter-compress.c | 7 ++++---
15
tests/qemu-iotests/109 | 2 +-
27
block/raw-format.c | 6 +++---
16
tests/qemu-iotests/109.out | 178 ++++++++++++++++++++++++++++++++++++------
28
9 files changed, 24 insertions(+), 21 deletions(-)
17
tests/qemu-iotests/124 | 8 ++
29
18
tests/qemu-iotests/127.out | 7 ++
30
diff --git a/include/block/block-io.h b/include/block/block-io.h
19
tests/qemu-iotests/141 | 13 ++-
31
index XXXXXXX..XXXXXXX 100644
20
tests/qemu-iotests/141.out | 29 +++++++
32
--- a/include/block/block-io.h
21
tests/qemu-iotests/144 | 2 +-
33
+++ b/include/block/block-io.h
22
tests/qemu-iotests/144.out | 7 ++
34
@@ -XXX,XX +XXX,XX @@ int bdrv_get_flags(BlockDriverState *bs);
23
tests/qemu-iotests/156 | 2 +-
35
bool coroutine_fn bdrv_co_is_inserted(BlockDriverState *bs);
24
tests/qemu-iotests/156.out | 7 ++
36
bool co_wrapper bdrv_is_inserted(BlockDriverState *bs);
25
tests/qemu-iotests/185 | 12 +--
37
26
tests/qemu-iotests/185.out | 10 +++
38
-void bdrv_lock_medium(BlockDriverState *bs, bool locked);
27
tests/qemu-iotests/191 | 4 +-
39
+void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked);
28
tests/qemu-iotests/191.out | 132 +++++++++++++++++++++++++++++++
40
void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag);
29
tests/qemu-iotests/iotests.py | 5 ++
41
30
23 files changed, 449 insertions(+), 44 deletions(-)
42
const char *bdrv_get_format_name(BlockDriverState *bs);
31
43
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
32
diff --git a/qapi/job.json b/qapi/job.json
44
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
45
--- a/include/block/block_int-common.h
34
--- a/qapi/job.json
46
+++ b/include/block/block_int-common.h
35
+++ b/qapi/job.json
47
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
36
@@ -XXX,XX +XXX,XX @@
48
/* removable device specific */
37
{ 'enum': 'JobVerb',
49
bool coroutine_fn (*bdrv_co_is_inserted)(BlockDriverState *bs);
38
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
50
void coroutine_fn (*bdrv_co_eject)(BlockDriverState *bs, bool eject_flag);
39
'finalize' ] }
51
- void (*bdrv_lock_medium)(BlockDriverState *bs, bool locked);
52
+ void coroutine_fn (*bdrv_co_lock_medium)(BlockDriverState *bs, bool locked);
53
54
/* to control generic scsi devices */
55
BlockAIOCB *(*bdrv_aio_ioctl)(BlockDriverState *bs,
56
diff --git a/include/sysemu/block-backend-io.h b/include/sysemu/block-backend-io.h
57
index XXXXXXX..XXXXXXX 100644
58
--- a/include/sysemu/block-backend-io.h
59
+++ b/include/sysemu/block-backend-io.h
60
@@ -XXX,XX +XXX,XX @@ bool coroutine_fn blk_co_is_inserted(BlockBackend *blk);
61
bool co_wrapper_mixed blk_is_inserted(BlockBackend *blk);
62
63
bool blk_is_available(BlockBackend *blk);
64
-void blk_lock_medium(BlockBackend *blk, bool locked);
40
+
65
+
41
+##
66
+void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked);
42
+# @JOB_STATUS_CHANGE:
67
+void co_wrapper blk_lock_medium(BlockBackend *blk, bool locked);
43
+#
68
44
+# Emitted when a job transitions to a different status.
69
void coroutine_fn blk_co_eject(BlockBackend *blk, bool eject_flag);
45
+#
70
void co_wrapper blk_eject(BlockBackend *blk, bool eject_flag);
46
+# @id: The job identifier
71
diff --git a/block.c b/block.c
47
+# @status: The new job status
72
index XXXXXXX..XXXXXXX 100644
48
+#
73
--- a/block.c
49
+# Since: 2.13
74
+++ b/block.c
50
+##
75
@@ -XXX,XX +XXX,XX @@ void coroutine_fn bdrv_co_eject(BlockDriverState *bs, bool eject_flag)
51
+{ 'event': 'JOB_STATUS_CHANGE',
76
* Lock or unlock the media (if it is locked, the user won't be able
52
+ 'data': { 'id': 'str',
77
* to eject it manually).
53
+ 'status': 'JobStatus' } }
78
*/
54
diff --git a/job.c b/job.c
79
-void bdrv_lock_medium(BlockDriverState *bs, bool locked)
55
index XXXXXXX..XXXXXXX 100644
80
+void coroutine_fn bdrv_co_lock_medium(BlockDriverState *bs, bool locked)
56
--- a/job.c
81
{
57
+++ b/job.c
82
BlockDriver *drv = bs->drv;
58
@@ -XXX,XX +XXX,XX @@
83
IO_CODE();
59
#include "qemu/id.h"
84
trace_bdrv_lock_medium(bs, locked);
60
#include "qemu/main-loop.h"
85
61
#include "trace-root.h"
86
- if (drv && drv->bdrv_lock_medium) {
62
+#include "qapi/qapi-events-job.h"
87
- drv->bdrv_lock_medium(bs, locked);
63
88
+ if (drv && drv->bdrv_co_lock_medium) {
64
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
89
+ drv->bdrv_co_lock_medium(bs, locked);
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
}
90
}
1145
}
91
}
1146
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
92
1147
"seconds": TIMESTAMP,
93
diff --git a/block/block-backend.c b/block/block-backend.c
1148
"microseconds": TIMESTAMP
94
index XXXXXXX..XXXXXXX 100644
1149
},
95
--- a/block/block-backend.c
1150
+ "event": "JOB_STATUS_CHANGE",
96
+++ b/block/block-backend.c
1151
+ "data": {
97
@@ -XXX,XX +XXX,XX @@ bool blk_is_available(BlockBackend *blk)
1152
+ "status": "waiting",
98
return blk_is_inserted(blk) && !blk_dev_is_tray_open(blk);
1153
+ "id": "commit0"
99
}
1154
+ }
100
1155
+}
101
-void blk_lock_medium(BlockBackend *blk, bool locked)
1156
+{
102
+void coroutine_fn blk_co_lock_medium(BlockBackend *blk, bool locked)
1157
+ "timestamp": {
103
{
1158
+ "seconds": TIMESTAMP,
104
BlockDriverState *bs = blk_bs(blk);
1159
+ "microseconds": TIMESTAMP
105
IO_CODE();
1160
+ },
106
1161
+ "event": "JOB_STATUS_CHANGE",
107
if (bs) {
1162
+ "data": {
108
- bdrv_lock_medium(bs, locked);
1163
+ "status": "pending",
109
+ bdrv_co_lock_medium(bs, locked);
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
}
110
}
1178
}
111
}
1179
+{
112
1180
+ "timestamp": {
113
diff --git a/block/copy-on-read.c b/block/copy-on-read.c
1181
+ "seconds": TIMESTAMP,
114
index XXXXXXX..XXXXXXX 100644
1182
+ "microseconds": TIMESTAMP
115
--- a/block/copy-on-read.c
1183
+ },
116
+++ b/block/copy-on-read.c
1184
+ "event": "JOB_STATUS_CHANGE",
117
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cor_co_eject(BlockDriverState *bs, bool eject_flag)
1185
+ "data": {
118
}
1186
+ "status": "concluded",
119
1187
+ "id": "commit0"
120
1188
+ }
121
-static void cor_lock_medium(BlockDriverState *bs, bool locked)
1189
+}
122
+static void coroutine_fn cor_co_lock_medium(BlockDriverState *bs, bool locked)
1190
+{
123
{
1191
+ "timestamp": {
124
- bdrv_lock_medium(bs->file->bs, locked);
1192
+ "seconds": TIMESTAMP,
125
+ bdrv_co_lock_medium(bs->file->bs, locked);
1193
+ "microseconds": TIMESTAMP
126
}
1194
+ },
127
1195
+ "event": "JOB_STATUS_CHANGE",
128
1196
+ "data": {
129
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_copy_on_read = {
1197
+ "status": "null",
130
.bdrv_co_pwritev_compressed = cor_co_pwritev_compressed,
1198
+ "id": "commit0"
131
1199
+ }
132
.bdrv_co_eject = cor_co_eject,
1200
+}
133
- .bdrv_lock_medium = cor_lock_medium,
1201
134
+ .bdrv_co_lock_medium = cor_co_lock_medium,
1202
=== Check that both top and top2 point to base now ===
135
1203
136
.has_variable_length = true,
1204
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
137
.is_filter = true,
1205
=== Perform commit job ===
138
diff --git a/block/file-posix.c b/block/file-posix.c
1206
139
index XXXXXXX..XXXXXXX 100644
1207
{
140
--- a/block/file-posix.c
1208
+ "timestamp": {
141
+++ b/block/file-posix.c
1209
+ "seconds": TIMESTAMP,
142
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag)
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
}
143
}
1232
}
144
}
1233
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
145
1234
"seconds": TIMESTAMP,
146
-static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
1235
"microseconds": TIMESTAMP
147
+static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked)
1236
},
148
{
1237
+ "event": "JOB_STATUS_CHANGE",
149
BDRVRawState *s = bs->opaque;
1238
+ "data": {
150
1239
+ "status": "waiting",
151
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
1240
+ "id": "commit0"
152
/* removable device support */
1241
+ }
153
.bdrv_co_is_inserted = cdrom_co_is_inserted,
1242
+}
154
.bdrv_co_eject = cdrom_co_eject,
1243
+{
155
- .bdrv_lock_medium = cdrom_lock_medium,
1244
+ "timestamp": {
156
+ .bdrv_co_lock_medium = cdrom_co_lock_medium,
1245
+ "seconds": TIMESTAMP,
157
1246
+ "microseconds": TIMESTAMP
158
/* generic scsi device */
1247
+ },
159
.bdrv_co_ioctl = hdev_co_ioctl,
1248
+ "event": "JOB_STATUS_CHANGE",
160
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cdrom_co_eject(BlockDriverState *bs, bool eject_flag)
1249
+ "data": {
161
cdrom_reopen(bs);
1250
+ "status": "pending",
162
}
1251
+ "id": "commit0"
163
1252
+ }
164
-static void cdrom_lock_medium(BlockDriverState *bs, bool locked)
1253
+}
165
+static void coroutine_fn cdrom_co_lock_medium(BlockDriverState *bs, bool locked)
1254
+{
166
{
1255
+ "timestamp": {
167
BDRVRawState *s = bs->opaque;
1256
+ "seconds": TIMESTAMP,
168
1257
+ "microseconds": TIMESTAMP
169
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_cdrom = {
1258
+ },
170
/* removable device support */
1259
"event": "BLOCK_JOB_COMPLETED",
171
.bdrv_co_is_inserted = cdrom_co_is_inserted,
1260
"data": {
172
.bdrv_co_eject = cdrom_co_eject,
1261
"device": "commit0",
173
- .bdrv_lock_medium = cdrom_lock_medium,
1262
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
174
+ .bdrv_co_lock_medium = cdrom_co_lock_medium,
1263
"type": "commit"
175
};
1264
}
176
#endif /* __FreeBSD__ */
1265
}
177
1266
+{
178
diff --git a/block/filter-compress.c b/block/filter-compress.c
1267
+ "timestamp": {
179
index XXXXXXX..XXXXXXX 100644
1268
+ "seconds": TIMESTAMP,
180
--- a/block/filter-compress.c
1269
+ "microseconds": TIMESTAMP
181
+++ b/block/filter-compress.c
1270
+ },
182
@@ -XXX,XX +XXX,XX @@ compress_co_eject(BlockDriverState *bs, bool eject_flag)
1271
+ "event": "JOB_STATUS_CHANGE",
183
}
1272
+ "data": {
184
1273
+ "status": "concluded",
185
1274
+ "id": "commit0"
186
-static void compress_lock_medium(BlockDriverState *bs, bool locked)
1275
+ }
187
+static void coroutine_fn
1276
+}
188
+compress_co_lock_medium(BlockDriverState *bs, bool locked)
1277
+{
189
{
1278
+ "timestamp": {
190
- bdrv_lock_medium(bs->file->bs, locked);
1279
+ "seconds": TIMESTAMP,
191
+ bdrv_co_lock_medium(bs->file->bs, locked);
1280
+ "microseconds": TIMESTAMP
192
}
1281
+ },
193
1282
+ "event": "JOB_STATUS_CHANGE",
194
1283
+ "data": {
195
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_compress = {
1284
+ "status": "null",
196
.bdrv_refresh_limits = compress_refresh_limits,
1285
+ "id": "commit0"
197
1286
+ }
198
.bdrv_co_eject = compress_co_eject,
1287
+}
199
- .bdrv_lock_medium = compress_lock_medium,
1288
200
+ .bdrv_co_lock_medium = compress_co_lock_medium,
1289
=== Check that both top and top2 point to base now ===
201
1290
202
.has_variable_length = true,
1291
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
203
.is_filter = true,
1292
index XXXXXXX..XXXXXXX 100644
204
diff --git a/block/raw-format.c b/block/raw-format.c
1293
--- a/tests/qemu-iotests/iotests.py
205
index XXXXXXX..XXXXXXX 100644
1294
+++ b/tests/qemu-iotests/iotests.py
206
--- a/block/raw-format.c
1295
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
207
+++ b/block/raw-format.c
1296
self.assert_qmp(event, 'data/device', drive)
208
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn raw_co_eject(BlockDriverState *bs, bool eject_flag)
1297
result = event
209
bdrv_co_eject(bs->file->bs, eject_flag);
1298
cancelled = True
210
}
1299
+ elif event['event'] == 'JOB_STATUS_CHANGE':
211
1300
+ self.assert_qmp(event, 'data/id', drive)
212
-static void raw_lock_medium(BlockDriverState *bs, bool locked)
1301
+
213
+static void coroutine_fn raw_co_lock_medium(BlockDriverState *bs, bool locked)
1302
214
{
1303
self.assert_no_active_block_jobs()
215
- bdrv_lock_medium(bs->file->bs, locked);
1304
return result
216
+ bdrv_co_lock_medium(bs->file->bs, locked);
1305
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
217
}
1306
self.assert_qmp(event, 'data/offset', event['data']['len'])
218
1307
self.assert_no_active_block_jobs()
219
static int coroutine_fn raw_co_ioctl(BlockDriverState *bs,
1308
return event
220
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_raw = {
1309
+ elif event['event'] == 'JOB_STATUS_CHANGE':
221
.bdrv_probe_blocksizes = &raw_probe_blocksizes,
1310
+ self.assert_qmp(event, 'data/id', drive)
222
.bdrv_probe_geometry = &raw_probe_geometry,
1311
223
.bdrv_co_eject = &raw_co_eject,
1312
def wait_ready(self, drive='drive0'):
224
- .bdrv_lock_medium = &raw_lock_medium,
1313
'''Wait until a block job BLOCK_JOB_READY event'''
225
+ .bdrv_co_lock_medium = &raw_co_lock_medium,
226
.bdrv_co_ioctl = &raw_co_ioctl,
227
.create_opts = &raw_create_opts,
228
.bdrv_has_zero_init = &raw_has_zero_init,
1314
--
229
--
1315
2.13.6
230
2.38.1
1316
1317
diff view generated by jsdifflib
1
This is the first step towards creating an infrastructure for generic
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
background jobs that aren't tied to a block device. For now, Job only
3
stores its ID and JobDriver, the rest stays in BlockJob.
4
2
5
The following patches will move over more parts of BlockJob to Job if
3
bdrv_debug_event() is categorized as an I/O function, and it currently
6
they are meaningful outside the context of a block job.
4
doesn't run in a coroutine. We should let it take a graph rdlock since
5
it traverses the block nodes graph, which however is only possible in a
6
coroutine.
7
7
8
BlockJob.driver is now redundant, but this patch leaves it around to
8
Therefore turn it into a co_wrapper_mixed to move the actual function
9
avoid unnecessary churn. The next patches will get rid of almost all of
9
into a coroutine where the lock can be taken.
10
its uses anyway so that it can be removed later with much less churn.
11
10
11
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Message-Id: <20230113204212.359076-14-kwolf@redhat.com>
14
Reviewed-by: John Snow <jsnow@redhat.com>
14
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
16
---
16
include/block/blockjob.h | 9 +++----
17
include/block/block-io.h | 5 ++++-
17
include/block/blockjob_int.h | 4 +--
18
include/block/block_int-common.h | 3 ++-
18
include/qemu/job.h | 60 ++++++++++++++++++++++++++++++++++++++++++++
19
block.c | 6 +++---
19
block/backup.c | 4 ++-
20
block/blkdebug.c | 5 +++--
20
block/commit.c | 4 ++-
21
block/io.c | 22 +++++++++++-----------
21
block/mirror.c | 10 +++++---
22
5 files changed, 23 insertions(+), 18 deletions(-)
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
23
34
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
24
diff --git a/include/block/block-io.h b/include/block/block-io.h
35
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/blockjob.h
26
--- a/include/block/block-io.h
37
+++ b/include/block/blockjob.h
27
+++ b/include/block/block-io.h
38
@@ -XXX,XX +XXX,XX @@
28
@@ -XXX,XX +XXX,XX @@ void *qemu_try_blockalign0(BlockDriverState *bs, size_t size);
39
#ifndef BLOCKJOB_H
29
void bdrv_enable_copy_on_read(BlockDriverState *bs);
40
#define BLOCKJOB_H
30
void bdrv_disable_copy_on_read(BlockDriverState *bs);
41
31
42
+#include "qemu/job.h"
32
-void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event);
43
#include "block/block.h"
33
+void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs,
44
#include "qemu/ratelimit.h"
34
+ BlkdebugEvent event);
45
35
+void co_wrapper_mixed bdrv_debug_event(BlockDriverState *bs,
46
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJobTxn BlockJobTxn;
36
+ BlkdebugEvent event);
47
* Long-running operation on a BlockDriverState.
37
48
*/
38
#define BLKDBG_EVENT(child, evt) \
49
typedef struct BlockJob {
39
do { \
50
+ /** Data belonging to the generic Job infrastructure */
40
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
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
41
index XXXXXXX..XXXXXXX 100644
70
--- a/include/block/blockjob_int.h
42
--- a/include/block/block_int-common.h
71
+++ b/include/block/blockjob_int.h
43
+++ b/include/block/block_int-common.h
72
@@ -XXX,XX +XXX,XX @@
44
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
73
* A class type for block job driver.
45
int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_check)(
74
*/
46
BlockDriverState *bs, BdrvCheckResult *result, BdrvCheckMode fix);
75
struct BlockJobDriver {
47
76
- /** Derived BlockJob struct size */
48
- void (*bdrv_debug_event)(BlockDriverState *bs, BlkdebugEvent event);
77
- size_t instance_size;
49
+ void coroutine_fn (*bdrv_co_debug_event)(BlockDriverState *bs,
78
+ /** Generic JobDriver callbacks and settings */
50
+ BlkdebugEvent event);
79
+ JobDriver job_driver;
51
80
52
/* io queue for linux-aio */
81
/** String describing the operation, part of query-block-jobs QMP API */
53
void coroutine_fn (*bdrv_co_io_plug)(BlockDriverState *bs);
82
BlockJobType job_type;
54
diff --git a/block.c b/block.c
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
55
index XXXXXXX..XXXXXXX 100644
151
--- a/block/backup.c
56
--- a/block.c
152
+++ b/block/backup.c
57
+++ b/block.c
153
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
58
@@ -XXX,XX +XXX,XX @@ BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
59
return drv->bdrv_get_specific_stats(bs);
154
}
60
}
155
61
156
static const BlockJobDriver backup_job_driver = {
62
-void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
157
- .instance_size = sizeof(BackupBlockJob),
63
+void coroutine_fn bdrv_co_debug_event(BlockDriverState *bs, BlkdebugEvent event)
158
+ .job_driver = {
64
{
159
+ .instance_size = sizeof(BackupBlockJob),
65
IO_CODE();
160
+ },
66
- if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
161
.job_type = BLOCK_JOB_TYPE_BACKUP,
67
+ if (!bs || !bs->drv || !bs->drv->bdrv_co_debug_event) {
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;
68
return;
190
}
69
}
191
70
192
@@ -XXX,XX +XXX,XX @@ static void mirror_drain(BlockJob *job)
71
- bs->drv->bdrv_debug_event(bs, event);
72
+ bs->drv->bdrv_co_debug_event(bs, event);
193
}
73
}
194
74
195
static const BlockJobDriver mirror_job_driver = {
75
static BlockDriverState *bdrv_find_debug_node(BlockDriverState *bs)
196
- .instance_size = sizeof(MirrorBlockJob),
76
diff --git a/block/blkdebug.c b/block/blkdebug.c
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
77
index XXXXXXX..XXXXXXX 100644
216
--- a/block/stream.c
78
--- a/block/blkdebug.c
217
+++ b/block/stream.c
79
+++ b/block/blkdebug.c
218
@@ -XXX,XX +XXX,XX @@ out:
80
@@ -XXX,XX +XXX,XX @@ static void process_rule(BlockDriverState *bs, struct BlkdebugRule *rule,
81
}
219
}
82
}
220
83
221
static const BlockJobDriver stream_job_driver = {
84
-static void blkdebug_debug_event(BlockDriverState *bs, BlkdebugEvent event)
222
- .instance_size = sizeof(StreamBlockJob),
85
+static void coroutine_fn
223
+ .job_driver = {
86
+blkdebug_co_debug_event(BlockDriverState *bs, BlkdebugEvent event)
224
+ .instance_size = sizeof(StreamBlockJob),
87
{
225
+ },
88
BDRVBlkdebugState *s = bs->opaque;
226
.job_type = BLOCK_JOB_TYPE_STREAM,
89
struct BlkdebugRule *rule, *next;
227
.start = stream_run,
90
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_blkdebug = {
228
};
91
.bdrv_co_pdiscard = blkdebug_co_pdiscard,
229
diff --git a/blockjob.c b/blockjob.c
92
.bdrv_co_block_status = blkdebug_co_block_status,
93
94
- .bdrv_debug_event = blkdebug_debug_event,
95
+ .bdrv_co_debug_event = blkdebug_co_debug_event,
96
.bdrv_debug_breakpoint = blkdebug_debug_breakpoint,
97
.bdrv_debug_remove_breakpoint
98
= blkdebug_debug_remove_breakpoint,
99
diff --git a/block/io.c b/block/io.c
230
index XXXXXXX..XXXXXXX 100644
100
index XXXXXXX..XXXXXXX 100644
231
--- a/blockjob.c
101
--- a/block/io.c
232
+++ b/blockjob.c
102
+++ b/block/io.c
233
@@ -XXX,XX +XXX,XX @@
103
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child,
234
#include "qapi/qapi-events-block-core.h"
104
goto err;
235
#include "qapi/qmp/qerror.h"
105
}
236
#include "qemu/coroutine.h"
106
237
-#include "qemu/id.h"
107
- bdrv_debug_event(bs, BLKDBG_COR_WRITE);
238
#include "qemu/timer.h"
108
+ bdrv_co_debug_event(bs, BLKDBG_COR_WRITE);
239
109
if (drv->bdrv_co_pwrite_zeroes &&
240
/* Right now, this mutex is only needed to synchronize accesses to job->busy
110
buffer_is_zero(bounce_buffer, pnum)) {
241
@@ -XXX,XX +XXX,XX @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
111
/* FIXME: Should we (perhaps conditionally) be setting
242
return 0;
112
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child,
113
qemu_iovec_init_buf(&local_qiov, pad->buf, bytes);
114
115
if (pad->head) {
116
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
117
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD);
118
}
119
if (pad->merge_reads && pad->tail) {
120
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
121
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
122
}
123
ret = bdrv_aligned_preadv(child, req, req->overlap_offset, bytes,
124
align, &local_qiov, 0, 0);
125
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child,
126
return ret;
127
}
128
if (pad->head) {
129
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
130
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_HEAD);
131
}
132
if (pad->merge_reads && pad->tail) {
133
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
134
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
135
}
136
137
if (pad->merge_reads) {
138
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child,
139
if (pad->tail) {
140
qemu_iovec_init_buf(&local_qiov, pad->tail_buf, align);
141
142
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
143
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL);
144
ret = bdrv_aligned_preadv(
145
child, req,
146
req->overlap_offset + req->overlap_bytes - align,
147
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int bdrv_padding_rmw_read(BdrvChild *child,
148
if (ret < 0) {
149
return ret;
150
}
151
- bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
152
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_RMW_AFTER_TAIL);
243
}
153
}
244
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
154
245
- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv));
155
zero_mem:
246
+ job->job.id, BlockJobStatus_str(job->status),
156
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
247
+ BlockJobVerb_str(bv));
157
if (ret < 0) {
248
return -EPERM;
158
/* Do nothing, write notifier decided to fail this request */
249
}
159
} else if (flags & BDRV_REQ_ZERO_WRITE) {
250
160
- bdrv_debug_event(bs, BLKDBG_PWRITEV_ZERO);
251
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
161
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_ZERO);
252
BlockJob *job;
162
ret = bdrv_co_do_pwrite_zeroes(bs, offset, bytes, flags);
253
163
} else if (flags & BDRV_REQ_WRITE_COMPRESSED) {
254
QLIST_FOREACH(job, &block_jobs, job_list) {
164
ret = bdrv_driver_pwritev_compressed(bs, offset, bytes,
255
- if (job->id && !strcmp(id, job->id)) {
165
qiov, qiov_offset);
256
+ if (job->job.id && !strcmp(id, job->job.id)) {
166
} else if (bytes <= max_transfer) {
257
return job;
167
- bdrv_debug_event(bs, BLKDBG_PWRITEV);
168
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV);
169
ret = bdrv_driver_pwritev(bs, offset, bytes, qiov, qiov_offset, flags);
170
} else {
171
- bdrv_debug_event(bs, BLKDBG_PWRITEV);
172
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV);
173
while (bytes_remaining) {
174
int num = MIN(bytes_remaining, max_transfer);
175
int local_flags = flags;
176
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_aligned_pwritev(BdrvChild *child,
177
bytes_remaining -= num;
258
}
178
}
259
}
179
}
260
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
180
- bdrv_debug_event(bs, BLKDBG_PWRITEV_DONE);
261
block_job_detach_aio_context, job);
181
+ bdrv_co_debug_event(bs, BLKDBG_PWRITEV_DONE);
262
blk_unref(job->blk);
182
263
error_free(job->blocker);
183
if (ret >= 0) {
264
- g_free(job->id);
184
ret = 0;
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
459
index XXXXXXX..XXXXXXX 100644
460
--- a/tests/test-bdrv-drain.c
461
+++ b/tests/test-bdrv-drain.c
462
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
463
}
464
465
BlockJobDriver test_job_driver = {
466
- .instance_size = sizeof(TestBlockJob),
467
+ .job_driver = {
468
+ .instance_size = sizeof(TestBlockJob),
469
+ },
470
.start = test_job_start,
471
.complete = test_job_complete,
472
};
473
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
474
index XXXXXXX..XXXXXXX 100644
475
--- a/tests/test-blockjob-txn.c
476
+++ b/tests/test-blockjob-txn.c
477
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
478
}
479
480
static const BlockJobDriver test_block_job_driver = {
481
- .instance_size = sizeof(TestBlockJob),
482
+ .job_driver = {
483
+ .instance_size = sizeof(TestBlockJob),
484
+ },
485
.start = test_block_job_run,
486
};
487
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
489
index XXXXXXX..XXXXXXX 100644
490
--- a/tests/test-blockjob.c
491
+++ b/tests/test-blockjob.c
492
@@ -XXX,XX +XXX,XX @@
493
#include "sysemu/block-backend.h"
494
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
}
517
518
static const BlockJobDriver test_cancel_driver = {
519
- .instance_size = sizeof(CancelJob),
520
+ .job_driver = {
521
+ .instance_size = sizeof(CancelJob),
522
+ },
523
.start = cancel_job_start,
524
.complete = cancel_job_complete,
525
};
526
diff --git a/MAINTAINERS b/MAINTAINERS
527
index XXXXXXX..XXXXXXX 100644
528
--- a/MAINTAINERS
529
+++ b/MAINTAINERS
530
@@ -XXX,XX +XXX,XX @@ L: qemu-block@nongnu.org
531
S: Supported
532
F: blockjob.c
533
F: include/block/blockjob.h
534
+F: job.c
535
+F: include/block/job.h
536
F: block/backup.c
537
F: block/commit.c
538
F: block/stream.c
539
diff --git a/Makefile.objs b/Makefile.objs
540
index XXXXXXX..XXXXXXX 100644
541
--- a/Makefile.objs
542
+++ b/Makefile.objs
543
@@ -XXX,XX +XXX,XX @@ chardev-obj-y = chardev/
544
# block-obj-y is code used by both qemu system emulation and qemu-img
545
546
block-obj-y += nbd/
547
-block-obj-y += block.o blockjob.o
548
+block-obj-y += block.o blockjob.o job.o
549
block-obj-y += block/ scsi/
550
block-obj-y += qemu-io-cmds.o
551
block-obj-$(CONFIG_REPLICATION) += replication.o
552
--
185
--
553
2.13.6
186
2.38.1
554
555
diff view generated by jsdifflib
1
block_job_finish_sync() doesn't contain anything block job specific any
1
From: Emanuele Giuseppe Esposito <eesposit@redhat.com>
2
more, so it can be moved to Job.
3
2
3
Since these functions always run in coroutine context, adjust
4
their name to include "_co_", just like all other BlockDriver callbacks.
5
6
No functional change intended.
7
8
Signed-off-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
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>
10
Message-Id: <20230113204212.359076-15-kwolf@redhat.com>
11
Reviewed-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
13
---
7
include/qemu/job.h | 9 +++++++++
14
include/block/block_int-common.h | 4 ++--
8
block/commit.c | 6 +++---
15
block/io.c | 8 ++++----
9
blockjob.c | 55 +++++++++---------------------------------------------
16
block/qcow2.c | 12 ++++++------
10
job.c | 28 +++++++++++++++++++++++++++
17
3 files changed, 12 insertions(+), 12 deletions(-)
11
4 files changed, 49 insertions(+), 49 deletions(-)
12
18
13
diff --git a/include/qemu/job.h b/include/qemu/job.h
19
diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h
14
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qemu/job.h
21
--- a/include/block/block_int-common.h
16
+++ b/include/qemu/job.h
22
+++ b/include/block/block_int-common.h
17
@@ -XXX,XX +XXX,XX @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
23
@@ -XXX,XX +XXX,XX @@ struct BlockDriver {
18
*/
24
Error **errp);
19
void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
25
BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
20
26
21
+/**
27
- int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_save_vmstate)(
22
+ * Synchronously finishes the given @job. If @finish is given, it is called to
28
+ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_save_vmstate)(
23
+ * trigger completion or cancellation of the job.
29
BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
24
+ *
30
25
+ * Returns 0 if the job is successfully completed, -ECANCELED if the job was
31
- int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_load_vmstate)(
26
+ * cancelled before completing, and -errno in other error cases.
32
+ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_load_vmstate)(
27
+ */
33
BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos);
28
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
34
29
+
35
/* removable device specific */
30
/* TODO To be removed from the public interface */
36
diff --git a/block/io.c b/block/io.c
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
37
index XXXXXXX..XXXXXXX 100644
35
--- a/block/commit.c
38
--- a/block/io.c
36
+++ b/block/commit.c
39
+++ b/block/io.c
37
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
40
@@ -XXX,XX +XXX,XX @@ bdrv_co_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
38
blk_unref(s->top);
41
39
42
bdrv_inc_in_flight(bs);
40
/* If there is more than one reference to the job (e.g. if called from
43
41
- * block_job_finish_sync()), block_job_completed() won't free it and
44
- if (drv->bdrv_load_vmstate) {
42
- * therefore the blockers on the intermediate nodes remain. This would
45
- ret = drv->bdrv_load_vmstate(bs, qiov, pos);
43
- * cause bdrv_set_backing_hd() to fail. */
46
+ if (drv->bdrv_co_load_vmstate) {
44
+ * job_finish_sync()), block_job_completed() won't free it and therefore
47
+ ret = drv->bdrv_co_load_vmstate(bs, qiov, pos);
45
+ * the blockers on the intermediate nodes remain. This would cause
48
} else if (child_bs) {
46
+ * bdrv_set_backing_hd() to fail. */
49
ret = bdrv_co_readv_vmstate(child_bs, qiov, pos);
47
block_job_remove_all_bdrv(bjob);
50
} else {
48
51
@@ -XXX,XX +XXX,XX @@ bdrv_co_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos)
49
block_job_completed(&s->common, ret);
52
50
diff --git a/blockjob.c b/blockjob.c
53
bdrv_inc_in_flight(bs);
54
55
- if (drv->bdrv_save_vmstate) {
56
- ret = drv->bdrv_save_vmstate(bs, qiov, pos);
57
+ if (drv->bdrv_co_save_vmstate) {
58
+ ret = drv->bdrv_co_save_vmstate(bs, qiov, pos);
59
} else if (child_bs) {
60
ret = bdrv_co_writev_vmstate(child_bs, qiov, pos);
61
} else {
62
diff --git a/block/qcow2.c b/block/qcow2.c
51
index XXXXXXX..XXXXXXX 100644
63
index XXXXXXX..XXXXXXX 100644
52
--- a/blockjob.c
64
--- a/block/qcow2.c
53
+++ b/blockjob.c
65
+++ b/block/qcow2.c
54
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
66
@@ -XXX,XX +XXX,XX @@ static int64_t qcow2_check_vmstate_request(BlockDriverState *bs,
55
return rc;
67
return pos;
56
}
68
}
57
69
58
-static int block_job_finish_sync(BlockJob *job,
70
-static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs,
59
- void (*finish)(BlockJob *, Error **errp),
71
- QEMUIOVector *qiov, int64_t pos)
60
- Error **errp)
72
+static coroutine_fn int qcow2_co_save_vmstate(BlockDriverState *bs,
61
-{
73
+ QEMUIOVector *qiov, int64_t pos)
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
{
74
{
94
AioContext *ctx;
75
int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos);
95
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
76
if (offset < 0) {
96
ctx = blk_get_aio_context(other_job->blk);
77
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_save_vmstate(BlockDriverState *bs,
97
if (!job_is_completed(&other_job->job)) {
78
return bs->drv->bdrv_co_pwritev_part(bs, offset, qiov->size, qiov, 0, 0);
98
assert(job_is_cancelled(&other_job->job));
99
- block_job_finish_sync(other_job, NULL, NULL);
100
+ job_finish_sync(&other_job->job, NULL, NULL);
101
}
102
job_finalize_single(&other_job->job);
103
aio_context_release(ctx);
104
@@ -XXX,XX +XXX,XX @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
105
}
79
}
106
80
107
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
81
-static coroutine_fn int qcow2_load_vmstate(BlockDriverState *bs,
108
- * used with block_job_finish_sync() without the need for (rather nasty)
82
- QEMUIOVector *qiov, int64_t pos)
109
- * function pointer casts there. */
83
+static coroutine_fn int qcow2_co_load_vmstate(BlockDriverState *bs,
110
-static void block_job_cancel_err(BlockJob *job, Error **errp)
84
+ QEMUIOVector *qiov, int64_t pos)
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
{
85
{
115
- block_job_cancel(job, false);
86
int64_t offset = qcow2_check_vmstate_request(bs, qiov, pos);
116
+ BlockJob *bjob = container_of(job, BlockJob, job);
87
if (offset < 0) {
117
+ assert(is_block_job(job));
88
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_qcow2 = {
118
+ block_job_cancel(bjob, false);
89
.bdrv_co_get_info = qcow2_co_get_info,
119
}
90
.bdrv_get_specific_info = qcow2_get_specific_info,
120
91
121
int block_job_cancel_sync(BlockJob *job)
92
- .bdrv_save_vmstate = qcow2_save_vmstate,
122
{
93
- .bdrv_load_vmstate = qcow2_load_vmstate,
123
- return block_job_finish_sync(job, &block_job_cancel_err, NULL);
94
+ .bdrv_co_save_vmstate = qcow2_co_save_vmstate,
124
+ return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
95
+ .bdrv_co_load_vmstate = qcow2_co_load_vmstate,
125
}
96
126
97
.is_format = true,
127
void block_job_cancel_sync_all(void)
98
.supports_backing = true,
128
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
129
}
130
}
131
132
-static void block_job_complete(BlockJob *job, Error **errp)
133
-{
134
- job_complete(&job->job, errp);
135
-}
136
-
137
int block_job_complete_sync(BlockJob *job, Error **errp)
138
{
139
- return block_job_finish_sync(job, &block_job_complete, errp);
140
+ return job_finish_sync(&job->job, job_complete, errp);
141
}
142
143
void block_job_progress_update(BlockJob *job, uint64_t done)
144
diff --git a/job.c b/job.c
145
index XXXXXXX..XXXXXXX 100644
146
--- a/job.c
147
+++ b/job.c
148
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque)
149
aio_bh_schedule_oneshot(qemu_get_aio_context(),
150
job_defer_to_main_loop_bh, data);
151
}
152
+
153
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
154
+{
155
+ Error *local_err = NULL;
156
+ int ret;
157
+
158
+ job_ref(job);
159
+
160
+ if (finish) {
161
+ finish(job, &local_err);
162
+ }
163
+ if (local_err) {
164
+ error_propagate(errp, local_err);
165
+ job_unref(job);
166
+ return -EBUSY;
167
+ }
168
+ /* job_drain calls job_enter, and it should be enough to induce progress
169
+ * until the job completes or moves to the main thread. */
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
--
99
--
181
2.13.6
100
2.38.1
182
183
diff view generated by jsdifflib
1
This adds QMP commands that control the transition between states of the
1
From: Philippe Mathieu-Daudé <philmd@linaro.org>
2
job lifecycle.
3
2
3
The inlined nbd_readXX() functions call beXX_to_cpu(), themselves
4
declared in <qemu/bswap.h>. This fixes when refactoring:
5
6
In file included from ../../block/nbd.c:44:
7
include/block/nbd.h: In function 'nbd_read16':
8
include/block/nbd.h:383:12: error: implicit declaration of function 'be16_to_cpu' [-Werror=implicit-function-declaration]
9
383 | *val = be##bits##_to_cpu(*val); \
10
| ^~
11
include/block/nbd.h:387:1: note: in expansion of macro 'DEF_NBD_READ_N'
12
387 | DEF_NBD_READ_N(16) /* Defines nbd_read16(). */
13
| ^~~~~~~~~~~~~~
14
15
Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
16
Message-Id: <20221125175328.48539-1-philmd@linaro.org>
17
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@yandex-team.ru>
18
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
20
---
6
qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++++++++
21
include/block/nbd.h | 1 +
7
job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
22
1 file changed, 1 insertion(+)
8
MAINTAINERS | 1 +
9
Makefile.objs | 1 +
10
trace-events | 9 ++++
11
5 files changed, 244 insertions(+)
12
create mode 100644 job-qmp.c
13
23
14
diff --git a/qapi/job.json b/qapi/job.json
24
diff --git a/include/block/nbd.h b/include/block/nbd.h
15
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/job.json
26
--- a/include/block/nbd.h
17
+++ b/qapi/job.json
27
+++ b/include/block/nbd.h
18
@@ -XXX,XX +XXX,XX @@
28
@@ -XXX,XX +XXX,XX @@
19
{ 'event': 'JOB_STATUS_CHANGE',
29
#include "io/channel-socket.h"
20
'data': { 'id': 'str',
30
#include "crypto/tlscreds.h"
21
'status': 'JobStatus' } }
31
#include "qapi/error.h"
22
+
32
+#include "qemu/bswap.h"
23
+##
33
24
+# @job-pause:
34
extern const BlockExportDriver blk_exp_nbd;
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
123
index XXXXXXX..XXXXXXX
124
--- /dev/null
125
+++ b/job-qmp.c
126
@@ -XXX,XX +XXX,XX @@
127
+/*
128
+ * QMP interface for background jobs
129
+ *
130
+ * Copyright (c) 2011 IBM Corp.
131
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
132
+ *
133
+ * 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
135
+ * in the Software without restriction, including without limitation the rights
136
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
137
+ * copies of the Software, and to permit persons to whom the Software is
138
+ * furnished to do so, subject to the following conditions:
139
+ *
140
+ * The above copyright notice and this permission notice shall be included in
141
+ * all copies or substantial portions of the Software.
142
+ *
143
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
144
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
145
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
146
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
147
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
148
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
149
+ * THE SOFTWARE.
150
+ */
151
+
152
+#include "qemu/osdep.h"
153
+#include "qemu-common.h"
154
+#include "qemu/job.h"
155
+#include "qapi/qapi-commands-job.h"
156
+#include "qapi/error.h"
157
+#include "trace-root.h"
158
+
159
+/* Get a job using its ID and acquire its AioContext */
160
+static Job *find_job(const char *id, AioContext **aio_context, Error **errp)
161
+{
162
+ Job *job;
163
+
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
+}
177
+
178
+void qmp_job_cancel(const char *id, Error **errp)
179
+{
180
+ AioContext *aio_context;
181
+ Job *job = find_job(id, &aio_context, errp);
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
+}
191
+
192
+void qmp_job_pause(const char *id, Error **errp)
193
+{
194
+ AioContext *aio_context;
195
+ Job *job = find_job(id, &aio_context, errp);
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
+}
205
+
206
+void qmp_job_resume(const char *id, Error **errp)
207
+{
208
+ AioContext *aio_context;
209
+ Job *job = find_job(id, &aio_context, errp);
210
+
211
+ if (!job) {
212
+ return;
213
+ }
214
+
215
+ trace_qmp_job_resume(job);
216
+ job_user_resume(job, errp);
217
+ aio_context_release(aio_context);
218
+}
219
+
220
+void qmp_job_complete(const char *id, Error **errp)
221
+{
222
+ AioContext *aio_context;
223
+ Job *job = find_job(id, &aio_context, errp);
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
+}
233
+
234
+void qmp_job_finalize(const char *id, Error **errp)
235
+{
236
+ AioContext *aio_context;
237
+ Job *job = find_job(id, &aio_context, errp);
238
+
239
+ if (!job) {
240
+ return;
241
+ }
242
+
243
+ trace_qmp_job_finalize(job);
244
+ job_finalize(job, errp);
245
+ aio_context_release(aio_context);
246
+}
247
+
248
+void qmp_job_dismiss(const char *id, Error **errp)
249
+{
250
+ AioContext *aio_context;
251
+ Job *job = find_job(id, &aio_context, errp);
252
+
253
+ if (!job) {
254
+ return;
255
+ }
256
+
257
+ trace_qmp_job_dismiss(job);
258
+ job_dismiss(&job, errp);
259
+ aio_context_release(aio_context);
260
+}
261
diff --git a/MAINTAINERS b/MAINTAINERS
262
index XXXXXXX..XXXXXXX 100644
263
--- a/MAINTAINERS
264
+++ b/MAINTAINERS
265
@@ -XXX,XX +XXX,XX @@ S: Supported
266
F: blockjob.c
267
F: include/block/blockjob.h
268
F: job.c
269
+F: job-qmp.c
270
F: include/block/job.h
271
F: block/backup.c
272
F: block/commit.c
273
diff --git a/Makefile.objs b/Makefile.objs
274
index XXXXXXX..XXXXXXX 100644
275
--- a/Makefile.objs
276
+++ b/Makefile.objs
277
@@ -XXX,XX +XXX,XX @@ io-obj-y = io/
278
ifeq ($(CONFIG_SOFTMMU),y)
279
common-obj-y = blockdev.o blockdev-nbd.o block/
280
common-obj-y += bootdevice.o iothread.o
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
35
305
--
36
--
306
2.13.6
37
2.38.1
307
38
308
39
diff view generated by jsdifflib
1
block_job_event_pending() doesn't only send a QMP event, but it also
1
From: Hanna Reitz <hreitz@redhat.com>
2
transitions to the PENDING state. Split the function so that we get one
3
part only sending the event (like other block_job_event_* functions) and
4
another part that does the state transition.
5
2
3
When a block driver supports obtaining format-specific information, but
4
that object only contains optional fields, it is possible that none of
5
them are present, so that dump_qobject() (called by
6
bdrv_image_info_specific_dump()) will not print anything.
7
8
The callers of bdrv_image_info_specific_dump() put a header above this
9
information ("Format specific information:\n"), which will look strange
10
when there is nothing below. Modify bdrv_image_info_specific_dump() to
11
print this header instead of its callers, and only if there is indeed
12
something to be printed.
13
14
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
15
Message-Id: <20220620162704.80987-2-hreitz@redhat.com>
16
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
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
blockjob.c | 27 ++++++++++++++++++---------
19
include/block/qapi.h | 3 ++-
10
1 file changed, 18 insertions(+), 9 deletions(-)
20
block/qapi.c | 41 +++++++++++++++++++++++++++++++++++++----
21
qemu-io-cmds.c | 4 ++--
22
3 files changed, 41 insertions(+), 7 deletions(-)
11
23
12
diff --git a/blockjob.c b/blockjob.c
24
diff --git a/include/block/qapi.h b/include/block/qapi.h
13
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
14
--- a/blockjob.c
26
--- a/include/block/qapi.h
15
+++ b/blockjob.c
27
+++ b/include/block/qapi.h
16
@@ -XXX,XX +XXX,XX @@
28
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
17
29
Error **errp);
18
static void block_job_event_cancelled(BlockJob *job);
30
19
static void block_job_event_completed(BlockJob *job, const char *msg);
31
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
20
-static int block_job_event_pending(BlockJob *job);
32
-void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec);
21
+static void block_job_event_pending(BlockJob *job);
33
+void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
22
34
+ const char *prefix);
23
/* Transactional group of block jobs */
35
void bdrv_image_info_dump(ImageInfo *info);
24
struct BlockJobTxn {
36
#endif
25
@@ -XXX,XX +XXX,XX @@ static void block_job_do_finalize(BlockJob *job)
37
diff --git a/block/qapi.c b/block/qapi.c
38
index XXXXXXX..XXXXXXX 100644
39
--- a/block/qapi.c
40
+++ b/block/qapi.c
41
@@ -XXX,XX +XXX,XX @@ static void dump_qdict(int indentation, QDict *dict)
26
}
42
}
27
}
43
}
28
44
29
+static int block_job_transition_to_pending(BlockJob *job)
45
-void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec)
46
+/*
47
+ * Return whether dumping the given QObject with dump_qobject() would
48
+ * yield an empty dump, i.e. not print anything.
49
+ */
50
+static bool qobject_is_empty_dump(const QObject *obj)
30
+{
51
+{
31
+ job_state_transition(&job->job, JOB_STATUS_PENDING);
52
+ switch (qobject_type(obj)) {
32
+ if (!job->job.auto_finalize) {
53
+ case QTYPE_QNUM:
33
+ block_job_event_pending(job);
54
+ case QTYPE_QSTRING:
55
+ case QTYPE_QBOOL:
56
+ return false;
57
+
58
+ case QTYPE_QDICT:
59
+ return qdict_size(qobject_to(QDict, obj)) == 0;
60
+
61
+ case QTYPE_QLIST:
62
+ return qlist_empty(qobject_to(QList, obj));
63
+
64
+ default:
65
+ abort();
34
+ }
66
+ }
35
+ return 0;
36
+}
67
+}
37
+
68
+
38
static void block_job_completed_txn_success(BlockJob *job)
69
+/**
70
+ * Dumps the given ImageInfoSpecific object in a human-readable form,
71
+ * prepending an optional prefix if the dump is not empty.
72
+ */
73
+void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
74
+ const char *prefix)
39
{
75
{
40
BlockJobTxn *txn = job->txn;
76
QObject *obj, *data;
41
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
77
Visitor *v = qobject_output_visitor_new(&obj);
42
assert(other_job->ret == 0);
78
@@ -XXX,XX +XXX,XX @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec)
79
visit_type_ImageInfoSpecific(v, NULL, &info_spec, &error_abort);
80
visit_complete(v, &obj);
81
data = qdict_get(qobject_to(QDict, obj), "data");
82
- dump_qobject(1, data);
83
+ if (!qobject_is_empty_dump(data)) {
84
+ if (prefix) {
85
+ qemu_printf("%s", prefix);
86
+ }
87
+ dump_qobject(1, data);
88
+ }
89
qobject_unref(obj);
90
visit_free(v);
91
}
92
@@ -XXX,XX +XXX,XX @@ void bdrv_image_info_dump(ImageInfo *info)
43
}
93
}
44
94
45
- block_job_txn_apply(txn, block_job_event_pending, false);
95
if (info->format_specific) {
46
+ block_job_txn_apply(txn, block_job_transition_to_pending, false);
96
- qemu_printf("Format specific information:\n");
47
97
- bdrv_image_info_specific_dump(info->format_specific);
48
/* If no jobs need manual finalization, automatically do so */
98
+ bdrv_image_info_specific_dump(info->format_specific,
49
if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
99
+ "Format specific information:\n");
50
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
100
}
51
&error_abort);
52
}
101
}
53
102
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
54
-static int block_job_event_pending(BlockJob *job)
103
index XXXXXXX..XXXXXXX 100644
55
+static void block_job_event_pending(BlockJob *job)
104
--- a/qemu-io-cmds.c
56
{
105
+++ b/qemu-io-cmds.c
57
- job_state_transition(&job->job, JOB_STATUS_PENDING);
106
@@ -XXX,XX +XXX,XX @@ static int info_f(BlockBackend *blk, int argc, char **argv)
58
- if (!job->job.auto_finalize && !block_job_is_internal(job)) {
107
return -EIO;
59
- qapi_event_send_block_job_pending(job_type(&job->job),
60
- job->job.id,
61
- &error_abort);
62
+ if (block_job_is_internal(job)) {
63
+ return;
64
}
108
}
65
- return 0;
109
if (spec_info) {
66
+
110
- printf("Format specific information:\n");
67
+ qapi_event_send_block_job_pending(job_type(&job->job),
111
- bdrv_image_info_specific_dump(spec_info);
68
+ job->job.id,
112
+ bdrv_image_info_specific_dump(spec_info,
69
+ &error_abort);
113
+ "Format specific information:\n");
70
}
114
qapi_free_ImageInfoSpecific(spec_info);
71
115
}
72
/*
116
73
--
117
--
74
2.13.6
118
2.38.1
75
76
diff view generated by jsdifflib
1
QAPI types aren't externally visible, so we can rename them without
1
From: Hanna Reitz <hreitz@redhat.com>
2
causing problems. Before we add a job type to Job, rename the enum
3
so it can be used for more than just block jobs.
4
2
3
Add some (optional) information that the file driver can provide for
4
image files, namely the extent size hint.
5
6
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
7
Message-Id: <20220620162704.80987-3-hreitz@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
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>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
---
10
---
10
qapi/block-core.json | 14 +++++++-------
11
qapi/block-core.json | 26 ++++++++++++++++++++++++--
11
include/block/blockjob_int.h | 2 +-
12
block/file-posix.c | 30 ++++++++++++++++++++++++++++++
12
block/backup.c | 2 +-
13
2 files changed, 54 insertions(+), 2 deletions(-)
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
14
19
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
20
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
22
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
23
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
24
'data': ['top', 'full', 'none', 'incremental'] }
20
'*encryption-format': 'RbdImageEncryptionFormat'
25
21
} }
22
23
+##
24
+# @ImageInfoSpecificFile:
25
+#
26
+# @extent-size-hint: Extent size hint (if available)
27
+#
28
+# Since: 8.0
29
+##
30
+{ 'struct': 'ImageInfoSpecificFile',
31
+ 'data': {
32
+ '*extent-size-hint': 'size'
33
+ } }
34
+
26
##
35
##
27
-# @BlockJobType:
36
# @ImageInfoSpecificKind:
28
+# @JobType:
29
#
37
#
30
-# Type of a block job.
38
# @luks: Since 2.7
31
+# Type of a background job.
39
# @rbd: Since 6.1
32
#
40
+# @file: Since 8.0
33
# @commit: block commit job type, see "block-commit"
34
#
35
@@ -XXX,XX +XXX,XX @@
36
#
41
#
37
# Since: 1.7
42
# Since: 1.7
38
##
43
##
39
-{ 'enum': 'BlockJobType',
44
{ 'enum': 'ImageInfoSpecificKind',
40
+{ 'enum': 'JobType',
45
- 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd' ] }
41
'data': ['commit', 'stream', 'mirror', 'backup'] }
46
+ 'data': [ 'qcow2', 'vmdk', 'luks', 'rbd', 'file' ] }
42
47
43
##
48
##
49
# @ImageInfoSpecificQCow2Wrapper:
44
@@ -XXX,XX +XXX,XX @@
50
@@ -XXX,XX +XXX,XX @@
51
{ 'struct': 'ImageInfoSpecificRbdWrapper',
52
'data': { 'data': 'ImageInfoSpecificRbd' } }
53
54
+##
55
+# @ImageInfoSpecificFileWrapper:
56
+#
57
+# Since: 8.0
58
+##
59
+{ 'struct': 'ImageInfoSpecificFileWrapper',
60
+ 'data': { 'data': 'ImageInfoSpecificFile' } }
61
+
62
##
63
# @ImageInfoSpecific:
45
#
64
#
65
@@ -XXX,XX +XXX,XX @@
66
'qcow2': 'ImageInfoSpecificQCow2Wrapper',
67
'vmdk': 'ImageInfoSpecificVmdkWrapper',
68
'luks': 'ImageInfoSpecificLUKSWrapper',
69
- 'rbd': 'ImageInfoSpecificRbdWrapper'
70
+ 'rbd': 'ImageInfoSpecificRbdWrapper',
71
+ 'file': 'ImageInfoSpecificFileWrapper'
72
} }
73
46
##
74
##
47
{ 'event': 'BLOCK_JOB_COMPLETED',
75
diff --git a/block/file-posix.c b/block/file-posix.c
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
76
index XXXXXXX..XXXXXXX 100644
82
--- a/include/block/blockjob_int.h
77
--- a/block/file-posix.c
83
+++ b/include/block/blockjob_int.h
78
+++ b/block/file-posix.c
84
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
79
@@ -XXX,XX +XXX,XX @@ raw_co_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
85
JobDriver job_driver;
80
return 0;
86
81
}
87
/** String describing the operation, part of query-block-jobs QMP API */
82
88
- BlockJobType job_type;
83
+static ImageInfoSpecific *raw_get_specific_info(BlockDriverState *bs,
89
+ JobType job_type;
84
+ Error **errp)
90
85
+{
91
/** Mandatory: Entrypoint for the Coroutine. */
86
+ BDRVRawState *s = bs->opaque;
92
CoroutineEntry *start;
87
+ ImageInfoSpecificFile *file_info = g_new0(ImageInfoSpecificFile, 1);
93
diff --git a/block/backup.c b/block/backup.c
88
+ ImageInfoSpecific *spec_info = g_new(ImageInfoSpecific, 1);
94
index XXXXXXX..XXXXXXX 100644
89
+
95
--- a/block/backup.c
90
+ *spec_info = (ImageInfoSpecific){
96
+++ b/block/backup.c
91
+ .type = IMAGE_INFO_SPECIFIC_KIND_FILE,
97
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
92
+ .u.file.data = file_info,
98
.job_driver = {
93
+ };
99
.instance_size = sizeof(BackupBlockJob),
94
+
100
},
95
+#ifdef FS_IOC_FSGETXATTR
101
- .job_type = BLOCK_JOB_TYPE_BACKUP,
96
+ {
102
+ .job_type = JOB_TYPE_BACKUP,
97
+ struct fsxattr attr;
103
.start = backup_run,
98
+ int ret;
104
.commit = backup_commit,
99
+
105
.abort = backup_abort,
100
+ ret = ioctl(s->fd, FS_IOC_FSGETXATTR, &attr);
106
diff --git a/block/commit.c b/block/commit.c
101
+ if (!ret && attr.fsx_extsize != 0) {
107
index XXXXXXX..XXXXXXX 100644
102
+ file_info->has_extent_size_hint = true;
108
--- a/block/commit.c
103
+ file_info->extent_size_hint = attr.fsx_extsize;
109
+++ b/block/commit.c
104
+ }
110
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
105
+ }
111
.job_driver = {
106
+#endif
112
.instance_size = sizeof(CommitBlockJob),
107
+
113
},
108
+ return spec_info;
114
- .job_type = BLOCK_JOB_TYPE_COMMIT,
109
+}
115
+ .job_type = JOB_TYPE_COMMIT,
110
+
116
.start = commit_run,
111
static BlockStatsSpecificFile get_blockstats_specific_file(BlockDriverState *bs)
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
155
index XXXXXXX..XXXXXXX 100644
156
--- a/blockjob.c
157
+++ b/blockjob.c
158
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
159
{
112
{
160
BlockJob *job = c->opaque;
113
BDRVRawState *s = bs->opaque;
161
return g_strdup_printf("%s job '%s'",
114
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_file = {
162
- BlockJobType_str(job->driver->job_type),
115
.bdrv_co_truncate = raw_co_truncate,
163
+ JobType_str(job->driver->job_type),
116
.bdrv_co_getlength = raw_co_getlength,
164
job->job.id);
117
.bdrv_co_get_info = raw_co_get_info,
165
}
118
+ .bdrv_get_specific_info = raw_get_specific_info,
166
119
.bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
167
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
120
.bdrv_get_specific_stats = raw_get_specific_stats,
168
return NULL;
121
.bdrv_check_perm = raw_check_perm,
169
}
122
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_host_device = {
170
info = g_new0(BlockJobInfo, 1);
123
.bdrv_co_truncate = raw_co_truncate,
171
- info->type = g_strdup(BlockJobType_str(job->driver->job_type));
124
.bdrv_co_getlength = raw_co_getlength,
172
+ info->type = g_strdup(JobType_str(job->driver->job_type));
125
.bdrv_co_get_info = raw_co_get_info,
173
info->device = g_strdup(job->job.id);
126
+ .bdrv_get_specific_info = raw_get_specific_info,
174
info->len = job->len;
127
.bdrv_co_get_allocated_file_size = raw_co_get_allocated_file_size,
175
info->busy = atomic_read(&job->busy);
128
.bdrv_get_specific_stats = hdev_get_specific_stats,
176
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
129
.bdrv_check_perm = raw_check_perm,
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);
183
bs->job = job;
184
185
--
130
--
186
2.13.6
131
2.38.1
187
188
diff view generated by jsdifflib
1
This adds a separate schema file for all job-related definitions that
1
From: Hanna Reitz <hreitz@redhat.com>
2
aren't tied to the block layer.
3
2
4
For a start, move the enums JobType, JobStatus and JobVerb.
3
VMDK's implementation of .bdrv_get_specific_info() returns information
4
about its extent files, ostensibly in the form of ImageInfo objects.
5
However, it does not get this information through
6
bdrv_query_image_info(), but fills only a select few fields with custom
7
information that does not always match the fields' purposes.
5
8
9
For example, @format, which is supposed to be a block driver name, is
10
filled with the extent type, e.g. SPARSE or FLAT.
11
12
In ImageInfo, @compressed shows whether the data that can be seen in the
13
image is stored in compressed form or not. For example, a compressed
14
qcow2 image will store compressed data in its data file, but when
15
accessing the qcow2 node, you will see normal data. This is not how
16
VMDK uses the @compressed field for its extent files: Instead, it
17
signifies whether accessing the extent file will yield compressed data
18
(which the VMDK driver then (de-)compresses).
19
20
Create a new structure to represent the extent information. This allows
21
us to clarify the fields' meanings, and it clearly shows that these are
22
not complete ImageInfo objects. (That is, if a user wants an extent
23
file's ImageInfo object, they will need to query it separately, and will
24
not get it from ImageInfoSpecificVmdk.extents.)
25
26
Note that this removes the last use of ['ImageInfo'] (i.e. an array of
27
ImageInfo objects), so the QAPI generator will no longer generate
28
ImageInfoList by default. However, we use it in qemu-img.c, so we need
29
to create a dummy object to force the generate to create that type,
30
similarly to DummyForceArrays in machine.json (introduced in commit
31
9f08c8ec73878122ad4b061ed334f0437afaaa32 ("qapi: Lazy creation of array
32
types")).
33
34
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
35
Message-Id: <20220620162704.80987-4-hreitz@redhat.com>
36
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
37
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
38
---
9
qapi/block-core.json | 90 +-----------------------------------------------
39
qapi/block-core.json | 38 +++++++++++++++++++++++++++++++++++++-
10
qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
40
block/vmdk.c | 8 ++++----
11
qapi/qapi-schema.json | 1 +
41
2 files changed, 41 insertions(+), 5 deletions(-)
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
42
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
43
diff --git a/qapi/block-core.json b/qapi/block-core.json
19
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
45
--- a/qapi/block-core.json
21
+++ b/qapi/block-core.json
46
+++ b/qapi/block-core.json
22
@@ -XXX,XX +XXX,XX @@
47
@@ -XXX,XX +XXX,XX @@
23
48
'create-type': 'str',
24
{ 'include': 'common.json' }
49
'cid': 'int',
25
{ 'include': 'crypto.json' }
50
'parent-cid': 'int',
26
+{ 'include': 'job.json' }
51
- 'extents': ['ImageInfo']
27
{ 'include': 'sockets.json' }
52
+ 'extents': ['VmdkExtentInfo']
53
+ } }
54
+
55
+##
56
+# @VmdkExtentInfo:
57
+#
58
+# Information about a VMDK extent file
59
+#
60
+# @filename: Name of the extent file
61
+#
62
+# @format: Extent type (e.g. FLAT or SPARSE)
63
+#
64
+# @virtual-size: Number of bytes covered by this extent
65
+#
66
+# @cluster-size: Cluster size in bytes (for non-flat extents)
67
+#
68
+# @compressed: Whether this extent contains compressed data
69
+#
70
+# Since: 8.0
71
+##
72
+{ 'struct': 'VmdkExtentInfo',
73
+ 'data': {
74
+ 'filename': 'str',
75
+ 'format': 'str',
76
+ 'virtual-size': 'int',
77
+ '*cluster-size': 'int',
78
+ '*compressed': 'bool'
79
} }
28
80
29
##
81
##
30
@@ -XXX,XX +XXX,XX @@
82
@@ -XXX,XX +XXX,XX @@
31
'data': ['top', 'full', 'none', 'incremental'] }
83
'data': { 'device': 'str', '*id': 'str', '*name': 'str'},
32
84
'returns': 'SnapshotInfo',
33
##
85
'allow-preconfig': true }
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
+
86
+
134
+##
87
+##
135
+# == Background jobs
88
+# @DummyBlockCoreForceArrays:
89
+#
90
+# Not used by QMP; hack to let us use ImageInfoList internally
91
+#
92
+# Since: 8.0
136
+##
93
+##
137
+
94
+{ 'struct': 'DummyBlockCoreForceArrays',
138
+##
95
+ 'data': { 'unused-image-info': ['ImageInfo'] } }
139
+# @JobType:
96
diff --git a/block/vmdk.c b/block/vmdk.c
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
97
index XXXXXXX..XXXXXXX 100644
228
--- a/qapi/qapi-schema.json
98
--- a/block/vmdk.c
229
+++ b/qapi/qapi-schema.json
99
+++ b/block/vmdk.c
230
@@ -XXX,XX +XXX,XX @@
100
@@ -XXX,XX +XXX,XX @@ static int vmdk_has_zero_init(BlockDriverState *bs)
231
{ 'include': 'crypto.json' }
101
return 1;
232
{ 'include': 'block.json' }
102
}
233
{ 'include': 'char.json' }
103
234
+{ 'include': 'job.json' }
104
-static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
235
{ 'include': 'net.json' }
105
+static VmdkExtentInfo *vmdk_get_extent_info(VmdkExtent *extent)
236
{ 'include': 'rocker.json' }
106
{
237
{ 'include': 'tpm.json' }
107
- ImageInfo *info = g_new0(ImageInfo, 1);
238
diff --git a/MAINTAINERS b/MAINTAINERS
108
+ VmdkExtentInfo *info = g_new0(VmdkExtentInfo, 1);
239
index XXXXXXX..XXXXXXX 100644
109
240
--- a/MAINTAINERS
110
bdrv_refresh_filename(extent->file->bs);
241
+++ b/MAINTAINERS
111
- *info = (ImageInfo){
242
@@ -XXX,XX +XXX,XX @@ F: block/backup.c
112
+ *info = (VmdkExtentInfo){
243
F: block/commit.c
113
.filename = g_strdup(extent->file->bs->filename),
244
F: block/stream.c
114
.format = g_strdup(extent->type),
245
F: block/mirror.c
115
.virtual_size = extent->sectors * BDRV_SECTOR_SIZE,
246
+F: qapi/job.json
116
@@ -XXX,XX +XXX,XX @@ static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs,
247
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
117
int i;
248
118
BDRVVmdkState *s = bs->opaque;
249
Block QAPI, monitor, command line
119
ImageInfoSpecific *spec_info = g_new0(ImageInfoSpecific, 1);
250
diff --git a/Makefile b/Makefile
120
- ImageInfoList **tail;
251
index XXXXXXX..XXXXXXX 100644
121
+ VmdkExtentInfoList **tail;
252
--- a/Makefile
122
253
+++ b/Makefile
123
*spec_info = (ImageInfoSpecific){
254
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
124
.type = IMAGE_INFO_SPECIFIC_KIND_VMDK,
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
--
125
--
363
2.13.6
126
2.38.1
364
365
diff view generated by jsdifflib
1
This moves BlockJob.status and the closely related functions
1
From: Hanna Reitz <hreitz@redhat.com>
2
(block_)job_state_transition() and (block_)job_apply_verb to Job. The
2
3
two QAPI enums are renamed to JobStatus and JobVerb.
3
ImageInfo sometimes contains flat information, and sometimes it does
4
4
not. Split off a BlockNodeInfo struct, which only contains information
5
about a single node and has no link to the backing image.
6
7
We do this so we can extend BlockNodeInfo to a BlockGraphInfo struct,
8
which has links to all child nodes, not just the backing node. It would
9
be strange to base BlockGraphInfo on ImageInfo, because then this
10
extended struct would have two links to the backing node (one in
11
BlockGraphInfo as one of all the child links, and one in ImageInfo).
12
13
Furthermore, it is quite common to ignore the backing-image field
14
altogether: bdrv_query_image_info() does not set it, and
15
bdrv_image_info_dump() does not evaluate it. That signals that we
16
should have different structs for describing a single node and one that
17
has a link to the backing image.
18
19
Still, bdrv_query_image_info() and bdrv_image_info_dump() are not
20
changed too much in this patch. Follow-up patches will handle them.
21
22
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
23
Message-Id: <20220620162704.80987-5-hreitz@redhat.com>
24
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
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
---
26
---
10
qapi/block-core.json | 16 ++++----
27
qapi/block-core.json | 24 +++++++++----
11
include/block/blockjob.h | 3 --
28
include/block/qapi.h | 3 ++
12
include/qemu/job.h | 13 ++++++
29
block/qapi.c | 86 ++++++++++++++++++++++++++++++++------------
13
blockjob.c | 102 +++++++++++------------------------------------
30
3 files changed, 85 insertions(+), 28 deletions(-)
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
31
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
32
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
34
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
35
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
36
@@ -XXX,XX +XXX,XX @@
25
'data': ['commit', 'stream', 'mirror', 'backup'] }
37
} }
26
38
27
##
39
##
28
-# @BlockJobVerb:
40
-# @ImageInfo:
29
+# @JobVerb:
41
+# @BlockNodeInfo:
30
#
42
#
31
-# Represents command verbs that can be applied to a blockjob.
43
# Information about a QEMU image file
32
+# Represents command verbs that can be applied to a job.
33
#
34
# @cancel: see @block-job-cancel
35
#
44
#
36
@@ -XXX,XX +XXX,XX @@
45
@@ -XXX,XX +XXX,XX @@
37
#
46
#
38
# Since: 2.12
47
# @snapshots: list of VM snapshots
48
#
49
-# @backing-image: info of the backing image (since 1.6)
50
-#
51
# @format-specific: structure supplying additional format-specific
52
# information (since 1.7)
53
#
54
-# Since: 1.3
55
+# Since: 8.0
39
##
56
##
40
-{ 'enum': 'BlockJobVerb',
57
-{ 'struct': 'ImageInfo',
41
+{ 'enum': 'JobVerb',
58
+{ 'struct': 'BlockNodeInfo',
42
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
59
'data': {'filename': 'str', 'format': 'str', '*dirty-flag': 'bool',
43
'finalize' ] }
60
'*actual-size': 'int', 'virtual-size': 'int',
44
61
'*cluster-size': 'int', '*encrypted': 'bool', '*compressed': 'bool',
62
'*backing-filename': 'str', '*full-backing-filename': 'str',
63
'*backing-filename-format': 'str', '*snapshots': ['SnapshotInfo'],
64
- '*backing-image': 'ImageInfo',
65
'*format-specific': 'ImageInfoSpecific' } }
66
67
+##
68
+# @ImageInfo:
69
+#
70
+# Information about a QEMU image file, and potentially its backing image
71
+#
72
+# @backing-image: info of the backing image
73
+#
74
+# Since: 1.3
75
+##
76
+{ 'struct': 'ImageInfo',
77
+ 'base': 'BlockNodeInfo',
78
+ 'data': {
79
+ '*backing-image': 'ImageInfo'
80
+ } }
81
+
45
##
82
##
46
-# @BlockJobStatus:
83
# @ImageCheck:
47
+# @JobStatus:
84
#
48
#
85
diff --git a/include/block/qapi.h b/include/block/qapi.h
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
86
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob.h
87
--- a/include/block/qapi.h
84
+++ b/include/block/blockjob.h
88
+++ b/include/block/qapi.h
85
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
89
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
86
*/
90
int bdrv_query_snapshot_info_list(BlockDriverState *bs,
87
QEMUTimer sleep_timer;
91
SnapshotInfoList **p_list,
88
92
Error **errp);
89
- /** Current state; See @BlockJobStatus for details. */
93
+void bdrv_query_block_node_info(BlockDriverState *bs,
90
- BlockJobStatus status;
94
+ BlockNodeInfo **p_info,
95
+ Error **errp);
96
void bdrv_query_image_info(BlockDriverState *bs,
97
ImageInfo **p_info,
98
Error **errp);
99
diff --git a/block/qapi.c b/block/qapi.c
100
index XXXXXXX..XXXXXXX 100644
101
--- a/block/qapi.c
102
+++ b/block/qapi.c
103
@@ -XXX,XX +XXX,XX @@ int bdrv_query_snapshot_info_list(BlockDriverState *bs,
104
}
105
106
/**
107
- * bdrv_query_image_info:
108
- * @bs: block device to examine
109
- * @p_info: location to store image information
110
- * @errp: location to store error information
111
- *
112
- * Store "flat" image information in @p_info.
113
- *
114
- * "Flat" means it does *not* query backing image information,
115
- * i.e. (*pinfo)->has_backing_image will be set to false and
116
- * (*pinfo)->backing_image to NULL even when the image does in fact have
117
- * a backing image.
118
- *
119
- * @p_info will be set only on success. On error, store error in @errp.
120
+ * Helper function for other query info functions. Store information about @bs
121
+ * in @info, setting @errp on error.
122
*/
123
-void bdrv_query_image_info(BlockDriverState *bs,
124
- ImageInfo **p_info,
125
- Error **errp)
126
+static void bdrv_do_query_node_info(BlockDriverState *bs,
127
+ BlockNodeInfo *info,
128
+ Error **errp)
129
{
130
int64_t size;
131
const char *backing_filename;
132
BlockDriverInfo bdi;
133
int ret;
134
Error *err = NULL;
135
- ImageInfo *info;
136
137
aio_context_acquire(bdrv_get_aio_context(bs));
138
139
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
140
141
bdrv_refresh_filename(bs);
142
143
- info = g_new0(ImageInfo, 1);
144
info->filename = g_strdup(bs->filename);
145
info->format = g_strdup(bdrv_get_format_name(bs));
146
info->virtual_size = size;
147
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
148
info->format_specific = bdrv_get_specific_info(bs, &err);
149
if (err) {
150
error_propagate(errp, err);
151
- qapi_free_ImageInfo(info);
152
goto out;
153
}
154
backing_filename = bs->backing_file;
155
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
156
break;
157
default:
158
error_propagate(errp, err);
159
- qapi_free_ImageInfo(info);
160
goto out;
161
}
162
163
- *p_info = info;
91
-
164
-
92
/** True if this job should automatically finalize itself */
165
out:
93
bool auto_finalize;
166
aio_context_release(bdrv_get_aio_context(bs));
94
167
}
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
168
113
+/**
169
+/**
114
+ * Check whether the verb @verb can be applied to @job in its current state.
170
+ * bdrv_query_block_node_info:
115
+ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM
171
+ * @bs: block node to examine
116
+ * returned.
172
+ * @p_info: location to store node information
173
+ * @errp: location to store error information
174
+ *
175
+ * Store image information about @bs in @p_info.
176
+ *
177
+ * @p_info will be set only on success. On error, store error in @errp.
117
+ */
178
+ */
118
+int job_apply_verb(Job *job, JobVerb verb, Error **errp);
179
+void bdrv_query_block_node_info(BlockDriverState *bs,
119
+
180
+ BlockNodeInfo **p_info,
120
+/* TODO To be removed from the public interface */
181
+ Error **errp)
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
+{
182
+{
422
+ JobStatus s0 = job->status;
183
+ BlockNodeInfo *info;
423
+ assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
184
+ ERRP_GUARD();
424
+ trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
185
+
425
+ JobSTT[s0][s1] ? "allowed" : "disallowed",
186
+ info = g_new0(BlockNodeInfo, 1);
426
+ JobStatus_str(s0), JobStatus_str(s1));
187
+ bdrv_do_query_node_info(bs, info, errp);
427
+ assert(JobSTT[s0][s1]);
188
+ if (*errp) {
428
+ job->status = s1;
189
+ qapi_free_BlockNodeInfo(info);
190
+ return;
191
+ }
192
+
193
+ *p_info = info;
429
+}
194
+}
430
+
195
+
431
+int job_apply_verb(Job *job, JobVerb verb, Error **errp)
196
+/**
197
+ * bdrv_query_image_info:
198
+ * @bs: block node to examine
199
+ * @p_info: location to store image information
200
+ * @errp: location to store error information
201
+ *
202
+ * Store "flat" image information in @p_info.
203
+ *
204
+ * "Flat" means it does *not* query backing image information,
205
+ * i.e. (*pinfo)->has_backing_image will be set to false and
206
+ * (*pinfo)->backing_image to NULL even when the image does in fact have
207
+ * a backing image.
208
+ *
209
+ * @p_info will be set only on success. On error, store error in @errp.
210
+ */
211
+void bdrv_query_image_info(BlockDriverState *bs,
212
+ ImageInfo **p_info,
213
+ Error **errp)
432
+{
214
+{
433
+ JobStatus s0 = job->status;
215
+ ImageInfo *info;
434
+ assert(verb >= 0 && verb <= JOB_VERB__MAX);
216
+ ERRP_GUARD();
435
+ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
217
+
436
+ JobVerbTable[verb][s0] ? "allowed" : "prohibited");
218
+ info = g_new0(ImageInfo, 1);
437
+ if (JobVerbTable[verb][s0]) {
219
+ bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp);
438
+ return 0;
220
+ if (*errp) {
221
+ qapi_free_ImageInfo(info);
222
+ return;
439
+ }
223
+ }
440
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
224
+
441
+ job->id, JobStatus_str(s0), JobVerb_str(verb));
225
+ *p_info = info;
442
+ return -EPERM;
443
+}
226
+}
444
+
227
+
445
JobType job_type(const Job *job)
228
/* @p_info will be set only on success. */
446
{
229
static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
447
return job->driver->job_type;
230
Error **errp)
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
--
231
--
622
2.13.6
232
2.38.1
623
624
diff view generated by jsdifflib
1
Clarify that len is just an estimation of the end value of offset, and
1
From: Hanna Reitz <hreitz@redhat.com>
2
that offset increases monotonically while len can change arbitrarily.
2
3
3
qemu-img info never uses ImageInfo's backing-image field, because it
4
While touching the documentation of offset, move it directly after len
4
opens the backing chain one by one with BDRV_O_NO_BACKING, and prints
5
to match the order of the declaration below.
5
all backing chain nodes' information consecutively. Use BlockNodeInfo
6
6
to make it clear that we only print information about a single node, and
7
that we are not using the backing-image field.
8
9
Notably, bdrv_image_info_dump() does not evaluate the backing-image
10
field, so we can easily make it take a BlockNodeInfo pointer (and
11
consequentially rename it to bdrv_node_info_dump()). It makes more
12
sense this way, because again, the interface now makes it syntactically
13
clear that backing-image is ignored by this function.
14
15
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
16
Message-Id: <20220620162704.80987-6-hreitz@redhat.com>
17
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
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
---
19
---
11
qapi/block-core.json | 9 ++++++---
20
qapi/block-core.json | 4 +--
12
1 file changed, 6 insertions(+), 3 deletions(-)
21
include/block/qapi.h | 2 +-
22
block/monitor/block-hmp-cmds.c | 2 +-
23
block/qapi.c | 2 +-
24
qemu-img.c | 48 +++++++++++++++++-----------------
25
5 files changed, 29 insertions(+), 29 deletions(-)
13
26
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
27
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
index XXXXXXX..XXXXXXX 100644
28
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
29
--- a/qapi/block-core.json
17
+++ b/qapi/block-core.json
30
+++ b/qapi/block-core.json
18
@@ -XXX,XX +XXX,XX @@
31
@@ -XXX,XX +XXX,XX @@
19
# @device: The job identifier. Originally the device name but other
32
##
20
# values are allowed since QEMU 2.7
33
# @DummyBlockCoreForceArrays:
21
#
34
#
22
-# @len: the maximum progress value
35
-# Not used by QMP; hack to let us use ImageInfoList internally
23
+# @len: Estimated @offset value at the completion of the job. This value can
36
+# Not used by QMP; hack to let us use BlockNodeInfoList internally
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
#
37
#
30
# @busy: false if the job is known to be in a quiescent state, with
38
# Since: 8.0
31
# no pending I/O. Since 1.3.
39
##
32
@@ -XXX,XX +XXX,XX @@
40
{ 'struct': 'DummyBlockCoreForceArrays',
33
# @paused: whether the job is paused or, if @busy is true, will
41
- 'data': { 'unused-image-info': ['ImageInfo'] } }
34
# pause itself as soon as possible. Since 1.3.
42
+ 'data': { 'unused-block-node-info': ['BlockNodeInfo'] } }
35
#
43
diff --git a/include/block/qapi.h b/include/block/qapi.h
36
-# @offset: the current progress value
44
index XXXXXXX..XXXXXXX 100644
37
-#
45
--- a/include/block/qapi.h
38
# @speed: the rate limit, bytes per second
46
+++ b/include/block/qapi.h
39
#
47
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
40
# @io-status: the status of the job (since 1.3)
48
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
49
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
50
const char *prefix);
51
-void bdrv_image_info_dump(ImageInfo *info);
52
+void bdrv_node_info_dump(BlockNodeInfo *info);
53
#endif
54
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/block/monitor/block-hmp-cmds.c
57
+++ b/block/monitor/block-hmp-cmds.c
58
@@ -XXX,XX +XXX,XX @@ static void print_block_info(Monitor *mon, BlockInfo *info,
59
monitor_printf(mon, "\nImages:\n");
60
image_info = inserted->image;
61
while (1) {
62
- bdrv_image_info_dump(image_info);
63
+ bdrv_node_info_dump(qapi_ImageInfo_base(image_info));
64
if (image_info->backing_image) {
65
image_info = image_info->backing_image;
66
} else {
67
diff --git a/block/qapi.c b/block/qapi.c
68
index XXXXXXX..XXXXXXX 100644
69
--- a/block/qapi.c
70
+++ b/block/qapi.c
71
@@ -XXX,XX +XXX,XX @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
72
visit_free(v);
73
}
74
75
-void bdrv_image_info_dump(ImageInfo *info)
76
+void bdrv_node_info_dump(BlockNodeInfo *info)
77
{
78
char *size_buf, *dsize_buf;
79
if (!info->has_actual_size) {
80
diff --git a/qemu-img.c b/qemu-img.c
81
index XXXXXXX..XXXXXXX 100644
82
--- a/qemu-img.c
83
+++ b/qemu-img.c
84
@@ -XXX,XX +XXX,XX @@ static void dump_snapshots(BlockDriverState *bs)
85
g_free(sn_tab);
86
}
87
88
-static void dump_json_image_info_list(ImageInfoList *list)
89
+static void dump_json_block_node_info_list(BlockNodeInfoList *list)
90
{
91
GString *str;
92
QObject *obj;
93
Visitor *v = qobject_output_visitor_new(&obj);
94
95
- visit_type_ImageInfoList(v, NULL, &list, &error_abort);
96
+ visit_type_BlockNodeInfoList(v, NULL, &list, &error_abort);
97
visit_complete(v, &obj);
98
str = qobject_to_json_pretty(obj, true);
99
assert(str != NULL);
100
@@ -XXX,XX +XXX,XX @@ static void dump_json_image_info_list(ImageInfoList *list)
101
g_string_free(str, true);
102
}
103
104
-static void dump_json_image_info(ImageInfo *info)
105
+static void dump_json_block_node_info(BlockNodeInfo *info)
106
{
107
GString *str;
108
QObject *obj;
109
Visitor *v = qobject_output_visitor_new(&obj);
110
111
- visit_type_ImageInfo(v, NULL, &info, &error_abort);
112
+ visit_type_BlockNodeInfo(v, NULL, &info, &error_abort);
113
visit_complete(v, &obj);
114
str = qobject_to_json_pretty(obj, true);
115
assert(str != NULL);
116
@@ -XXX,XX +XXX,XX @@ static void dump_json_image_info(ImageInfo *info)
117
g_string_free(str, true);
118
}
119
120
-static void dump_human_image_info_list(ImageInfoList *list)
121
+static void dump_human_image_info_list(BlockNodeInfoList *list)
122
{
123
- ImageInfoList *elem;
124
+ BlockNodeInfoList *elem;
125
bool delim = false;
126
127
for (elem = list; elem; elem = elem->next) {
128
@@ -XXX,XX +XXX,XX @@ static void dump_human_image_info_list(ImageInfoList *list)
129
}
130
delim = true;
131
132
- bdrv_image_info_dump(elem->value);
133
+ bdrv_node_info_dump(elem->value);
134
}
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
138
}
139
140
/**
141
- * Open an image file chain and return an ImageInfoList
142
+ * Open an image file chain and return an BlockNodeInfoList
143
*
144
* @filename: topmost image filename
145
* @fmt: topmost image format (may be NULL to autodetect)
146
* @chain: true - enumerate entire backing file chain
147
* false - only topmost image file
148
*
149
- * Returns a list of ImageInfo objects or NULL if there was an error opening an
150
- * image file. If there was an error a message will have been printed to
151
- * stderr.
152
+ * Returns a list of BlockNodeInfo objects or NULL if there was an error
153
+ * opening an image file. If there was an error a message will have been
154
+ * printed to stderr.
155
*/
156
-static ImageInfoList *collect_image_info_list(bool image_opts,
157
- const char *filename,
158
- const char *fmt,
159
- bool chain, bool force_share)
160
+static BlockNodeInfoList *collect_image_info_list(bool image_opts,
161
+ const char *filename,
162
+ const char *fmt,
163
+ bool chain, bool force_share)
164
{
165
- ImageInfoList *head = NULL;
166
- ImageInfoList **tail = &head;
167
+ BlockNodeInfoList *head = NULL;
168
+ BlockNodeInfoList **tail = &head;
169
GHashTable *filenames;
170
Error *err = NULL;
171
172
@@ -XXX,XX +XXX,XX @@ static ImageInfoList *collect_image_info_list(bool image_opts,
173
while (filename) {
174
BlockBackend *blk;
175
BlockDriverState *bs;
176
- ImageInfo *info;
177
+ BlockNodeInfo *info;
178
179
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
180
error_report("Backing file '%s' creates an infinite loop.",
181
@@ -XXX,XX +XXX,XX @@ static ImageInfoList *collect_image_info_list(bool image_opts,
182
}
183
bs = blk_bs(blk);
184
185
- bdrv_query_image_info(bs, &info, &err);
186
+ bdrv_query_block_node_info(bs, &info, &err);
187
if (err) {
188
error_report_err(err);
189
blk_unref(blk);
190
@@ -XXX,XX +XXX,XX @@ static ImageInfoList *collect_image_info_list(bool image_opts,
191
return head;
192
193
err:
194
- qapi_free_ImageInfoList(head);
195
+ qapi_free_BlockNodeInfoList(head);
196
g_hash_table_destroy(filenames);
197
return NULL;
198
}
199
@@ -XXX,XX +XXX,XX @@ static int img_info(int argc, char **argv)
200
OutputFormat output_format = OFORMAT_HUMAN;
201
bool chain = false;
202
const char *filename, *fmt, *output;
203
- ImageInfoList *list;
204
+ BlockNodeInfoList *list;
205
bool image_opts = false;
206
bool force_share = false;
207
208
@@ -XXX,XX +XXX,XX @@ static int img_info(int argc, char **argv)
209
break;
210
case OFORMAT_JSON:
211
if (chain) {
212
- dump_json_image_info_list(list);
213
+ dump_json_block_node_info_list(list);
214
} else {
215
- dump_json_image_info(list->value);
216
+ dump_json_block_node_info(list->value);
217
}
218
break;
219
}
220
221
- qapi_free_ImageInfoList(list);
222
+ qapi_free_BlockNodeInfoList(list);
223
return 0;
224
}
225
41
--
226
--
42
2.13.6
227
2.38.1
43
44
diff view generated by jsdifflib
1
This moves the job list from BlockJob to Job. Now we can check for
1
From: Hanna Reitz <hreitz@redhat.com>
2
duplicate IDs in job_create().
3
2
3
There is no real reason why bdrv_query_image_info() should generally not
4
recurse. The ImageInfo struct has a pointer to the backing image, so it
5
should generally be filled, unless the caller explicitly opts out.
6
7
This moves the recursing code from bdrv_block_device_info() into
8
bdrv_query_image_info().
9
10
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
11
Message-Id: <20220620162704.80987-7-hreitz@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
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
---
14
---
8
include/block/blockjob.h | 3 ---
15
include/block/qapi.h | 2 +
9
include/qemu/job.h | 19 +++++++++++++++++++
16
block/qapi.c | 92 +++++++++++++++++++++++++++-----------------
10
blockjob.c | 46 ++++++++++++++++++++++++----------------------
17
2 files changed, 58 insertions(+), 36 deletions(-)
11
job.c | 31 +++++++++++++++++++++++++++++++
12
4 files changed, 74 insertions(+), 25 deletions(-)
13
18
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
19
diff --git a/include/block/qapi.h b/include/block/qapi.h
15
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
21
--- a/include/block/qapi.h
17
+++ b/include/block/blockjob.h
22
+++ b/include/block/qapi.h
18
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
23
@@ -XXX,XX +XXX,XX @@ void bdrv_query_block_node_info(BlockDriverState *bs,
19
*/
24
Error **errp);
20
bool deferred_to_main_loop;
25
void bdrv_query_image_info(BlockDriverState *bs,
21
26
ImageInfo **p_info,
22
- /** Element of the list of block jobs */
27
+ bool flat,
23
- QLIST_ENTRY(BlockJob) job_list;
28
+ bool skip_implicit_filters,
24
-
29
Error **errp);
25
/** Status that is published by the query-block-jobs QMP API */
30
26
BlockDeviceIoStatus iostatus;
31
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
27
32
diff --git a/block/qapi.c b/block/qapi.c
28
diff --git a/include/qemu/job.h b/include/qemu/job.h
29
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
30
--- a/include/qemu/job.h
34
--- a/block/qapi.c
31
+++ b/include/qemu/job.h
35
+++ b/block/qapi.c
32
@@ -XXX,XX +XXX,XX @@
36
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
33
#define JOB_H
37
Error **errp)
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
+
45
+ /** Element of the list of jobs */
46
+ QLIST_ENTRY(Job) job_list;
47
} Job;
48
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
+
62
+/**
63
+ * Get the job identified by @id (which must not be %NULL).
64
+ *
65
+ * Returns the requested job, or %NULL if it doesn't exist.
66
+ */
67
+Job *job_get(const char *id);
68
+
69
#endif
70
diff --git a/blockjob.c b/blockjob.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/blockjob.c
73
+++ b/blockjob.c
74
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
75
int refcnt;
76
};
77
78
-static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
79
-
80
/*
81
* The block job API is composed of two categories of functions.
82
*
83
@@ -XXX,XX +XXX,XX @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
84
* blockjob_int.h.
85
*/
86
87
-BlockJob *block_job_next(BlockJob *job)
88
+static bool is_block_job(Job *job)
89
{
38
{
90
- if (!job) {
39
ImageInfo **p_image_info;
91
- return QLIST_FIRST(&block_jobs);
40
+ ImageInfo *backing_info;
92
- }
41
BlockDriverState *bs0, *backing;
93
- return QLIST_NEXT(job, job_list);
42
BlockDeviceInfo *info;
94
+ return job_type(job) == JOB_TYPE_BACKUP ||
43
+ ERRP_GUARD();
95
+ job_type(job) == JOB_TYPE_COMMIT ||
44
96
+ job_type(job) == JOB_TYPE_MIRROR ||
45
if (!bs->drv) {
97
+ job_type(job) == JOB_TYPE_STREAM;
46
error_setg(errp, "Block device %s is ejected", bs->node_name);
98
+}
47
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk,
99
+
48
bs0 = bs;
100
+BlockJob *block_job_next(BlockJob *bjob)
49
p_image_info = &info->image;
101
+{
50
info->backing_file_depth = 0;
102
+ Job *job = bjob ? &bjob->job : NULL;
51
- while (1) {
103
+
52
- Error *local_err = NULL;
104
+ do {
53
- bdrv_query_image_info(bs0, p_image_info, &local_err);
105
+ job = job_next(job);
54
- if (local_err) {
106
+ } while (job && !is_block_job(job));
55
- error_propagate(errp, local_err);
107
+
56
- qapi_free_BlockDeviceInfo(info);
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;
57
- return NULL;
154
- }
58
- }
59
-
60
- /* stop gathering data for flat output */
61
- if (flat) {
62
- break;
63
- }
64
65
- if (bs0->drv && bdrv_filter_or_cow_child(bs0)) {
66
- /*
67
- * Put any filtered child here (for backwards compatibility to when
68
- * we put bs0->backing here, which might be any filtered child).
69
- */
70
- info->backing_file_depth++;
71
- bs0 = bdrv_filter_or_cow_bs(bs0);
72
- p_image_info = &((*p_image_info)->backing_image);
73
- } else {
74
- break;
75
- }
76
+ /*
77
+ * Skip automatically inserted nodes that the user isn't aware of for
78
+ * query-block (blk != NULL), but not for query-named-block-nodes
79
+ */
80
+ bdrv_query_image_info(bs0, p_image_info, flat, blk != NULL, errp);
81
+ if (*errp) {
82
+ qapi_free_BlockDeviceInfo(info);
83
+ return NULL;
84
+ }
85
86
- /* Skip automatically inserted nodes that the user isn't aware of for
87
- * query-block (blk != NULL), but not for query-named-block-nodes */
88
- if (blk) {
89
- bs0 = bdrv_skip_implicit_filters(bs0);
90
- }
91
+ backing_info = info->image->backing_image;
92
+ while (backing_info) {
93
+ info->backing_file_depth++;
94
+ backing_info = backing_info->backing_image;
155
}
95
}
156
96
157
blk = blk_new(perm, shared_perm);
97
return info;
158
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
98
@@ -XXX,XX +XXX,XX @@ void bdrv_query_block_node_info(BlockDriverState *bs,
159
return NULL;
99
* bdrv_query_image_info:
160
}
100
* @bs: block node to examine
161
101
* @p_info: location to store image information
162
+ assert(is_block_job(&job->job));
102
+ * @flat: skip backing node information
163
+
103
+ * @skip_implicit_filters: skip implicit filters in the backing chain
164
job->driver = driver;
104
* @errp: location to store error information
165
job->blk = blk;
105
*
166
job->cb = cb;
106
- * Store "flat" image information in @p_info.
167
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
107
+ * Store image information in @p_info, potentially recursively covering the
168
108
+ * backing chain.
169
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
109
*
170
110
- * "Flat" means it does *not* query backing image information,
171
- QLIST_INSERT_HEAD(&block_jobs, job, job_list);
111
- * i.e. (*pinfo)->has_backing_image will be set to false and
172
-
112
- * (*pinfo)->backing_image to NULL even when the image does in fact have
173
blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
113
- * a backing image.
174
block_job_detach_aio_context, job);
114
+ * If @flat is true, do not query backing image information, i.e.
175
115
+ * (*p_info)->has_backing_image will be set to false and
176
diff --git a/job.c b/job.c
116
+ * (*p_info)->backing_image to NULL even when the image does in fact have a
177
index XXXXXXX..XXXXXXX 100644
117
+ * backing image.
178
--- a/job.c
118
+ *
179
+++ b/job.c
119
+ * If @skip_implicit_filters is true, implicit filter nodes in the backing chain
180
@@ -XXX,XX +XXX,XX @@
120
+ * will be skipped when querying backing image information.
181
#include "qemu/job.h"
121
+ * (@skip_implicit_filters is ignored when @flat is true.)
182
#include "qemu/id.h"
122
*
183
123
* @p_info will be set only on success. On error, store error in @errp.
184
+static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
124
*/
185
+
125
void bdrv_query_image_info(BlockDriverState *bs,
186
JobType job_type(const Job *job)
126
ImageInfo **p_info,
127
+ bool flat,
128
+ bool skip_implicit_filters,
129
Error **errp)
187
{
130
{
188
return job->driver->job_type;
131
ImageInfo *info;
189
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job)
132
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
190
return JobType_str(job_type(job));
133
info = g_new0(ImageInfo, 1);
191
}
134
bdrv_do_query_node_info(bs, qapi_ImageInfo_base(info), errp);
192
135
if (*errp) {
193
+Job *job_next(Job *job)
136
- qapi_free_ImageInfo(info);
194
+{
137
- return;
195
+ if (!job) {
138
+ goto fail;
196
+ return QLIST_FIRST(&jobs);
197
+ }
198
+ return QLIST_NEXT(job, job_list);
199
+}
200
+
201
+Job *job_get(const char *id)
202
+{
203
+ Job *job;
204
+
205
+ QLIST_FOREACH(job, &jobs, job_list) {
206
+ if (job->id && !strcmp(id, job->id)) {
207
+ return job;
208
+ }
209
+ }
139
+ }
210
+
140
+
211
+ return NULL;
141
+ if (!flat) {
212
+}
142
+ BlockDriverState *backing;
213
+
143
+
214
void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
144
+ /*
215
{
145
+ * Use any filtered child here (for backwards compatibility to when
216
Job *job;
146
+ * we always took bs->backing, which might be any filtered child).
217
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
147
+ */
218
error_setg(errp, "Invalid job ID '%s'", job_id);
148
+ backing = bdrv_filter_or_cow_bs(bs);
219
return NULL;
149
+ if (skip_implicit_filters) {
220
}
150
+ backing = bdrv_skip_implicit_filters(backing);
221
+ if (job_get(job_id)) {
151
+ }
222
+ error_setg(errp, "Job ID '%s' already in use", job_id);
152
+
223
+ return NULL;
153
+ if (backing) {
154
+ bdrv_query_image_info(backing, &info->backing_image, false,
155
+ skip_implicit_filters, errp);
156
+ if (*errp) {
157
+ goto fail;
158
+ }
224
+ }
159
+ }
225
}
160
}
226
161
227
job = g_malloc0(driver->instance_size);
162
*p_info = info;
228
job->driver = driver;
163
+ return;
229
job->id = g_strdup(job_id);
230
231
+ QLIST_INSERT_HEAD(&jobs, job, job_list);
232
+
164
+
233
return job;
165
+fail:
166
+ assert(*errp);
167
+ qapi_free_ImageInfo(info);
234
}
168
}
235
169
236
void job_delete(Job *job)
170
/* @p_info will be set only on success. */
237
{
238
+ QLIST_REMOVE(job, job_list);
239
+
240
g_free(job->id);
241
g_free(job);
242
}
243
--
171
--
244
2.13.6
172
2.38.1
245
246
diff view generated by jsdifflib
1
Commit 0ec4dfb8d changed block-job_pause/resume so that they return an
1
From: Hanna Reitz <hreitz@redhat.com>
2
error if they don't do anything because the job is already
3
paused/running. It forgot to update the documentation, so do that now.
4
2
3
Introduce a new QAPI type BlockGraphInfo and an associated
4
bdrv_query_block_graph_info() function that recursively gathers
5
BlockNodeInfo objects through a block graph.
6
7
A follow-up patch is going to make "qemu-img info" use this to print
8
information about all nodes that are (usually implicitly) opened for a
9
given image file.
10
11
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
12
Message-Id: <20220620162704.80987-8-hreitz@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
15
---
9
qapi/block-core.json | 5 ++---
16
qapi/block-core.json | 35 ++++++++++++++++++++++++++++++++
10
1 file changed, 2 insertions(+), 3 deletions(-)
17
include/block/qapi.h | 3 +++
18
block/qapi.c | 48 ++++++++++++++++++++++++++++++++++++++++++++
19
3 files changed, 86 insertions(+)
11
20
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
23
--- a/qapi/block-core.json
15
+++ b/qapi/block-core.json
24
+++ b/qapi/block-core.json
16
@@ -XXX,XX +XXX,XX @@
25
@@ -XXX,XX +XXX,XX @@
26
'*backing-image': 'ImageInfo'
27
} }
28
29
+##
30
+# @BlockChildInfo:
31
+#
32
+# Information about all nodes in the block graph starting at some node,
33
+# annotated with information about that node in relation to its parent.
34
+#
35
+# @name: Child name of the root node in the BlockGraphInfo struct, in its role
36
+# as the child of some undescribed parent node
37
+#
38
+# @info: Block graph information starting at this node
39
+#
40
+# Since: 8.0
41
+##
42
+{ 'struct': 'BlockChildInfo',
43
+ 'data': {
44
+ 'name': 'str',
45
+ 'info': 'BlockGraphInfo'
46
+ } }
47
+
48
+##
49
+# @BlockGraphInfo:
50
+#
51
+# Information about all nodes in a block (sub)graph in the form of BlockNodeInfo
52
+# data.
53
+# The base BlockNodeInfo struct contains the information for the (sub)graph's
54
+# root node.
55
+#
56
+# @children: Array of links to this node's child nodes' information
57
+#
58
+# Since: 8.0
59
+##
60
+{ 'struct': 'BlockGraphInfo',
61
+ 'base': 'BlockNodeInfo',
62
+ 'data': { 'children': ['BlockChildInfo'] } }
63
+
64
##
65
# @ImageCheck:
17
#
66
#
18
# This command returns immediately after marking the active background block
67
diff --git a/include/block/qapi.h b/include/block/qapi.h
19
# operation for pausing. It is an error to call this command if no
68
index XXXXXXX..XXXXXXX 100644
20
-# operation is in progress. Pausing an already paused job has no cumulative
69
--- a/include/block/qapi.h
21
-# effect; a single block-job-resume command will resume the job.
70
+++ b/include/block/qapi.h
22
+# operation is in progress or if the job is already paused.
71
@@ -XXX,XX +XXX,XX @@ void bdrv_query_image_info(BlockDriverState *bs,
23
#
72
bool flat,
24
# The operation will pause as soon as possible. No event is emitted when
73
bool skip_implicit_filters,
25
# the operation is actually paused. Cancelling a paused job automatically
74
Error **errp);
26
@@ -XXX,XX +XXX,XX @@
75
+void bdrv_query_block_graph_info(BlockDriverState *bs,
27
#
76
+ BlockGraphInfo **p_info,
28
# This command returns immediately after resuming a paused background block
77
+ Error **errp);
29
# operation. It is an error to call this command if no operation is in
78
30
-# progress. Resuming an already running job is not an error.
79
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
31
+# progress or if the job is not paused.
80
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
32
#
81
diff --git a/block/qapi.c b/block/qapi.c
33
# This command also clears the error status of the job.
82
index XXXXXXX..XXXXXXX 100644
34
#
83
--- a/block/qapi.c
84
+++ b/block/qapi.c
85
@@ -XXX,XX +XXX,XX @@ fail:
86
qapi_free_ImageInfo(info);
87
}
88
89
+/**
90
+ * bdrv_query_block_graph_info:
91
+ * @bs: root node to start from
92
+ * @p_info: location to store image information
93
+ * @errp: location to store error information
94
+ *
95
+ * Store image information about the graph starting from @bs in @p_info.
96
+ *
97
+ * @p_info will be set only on success. On error, store error in @errp.
98
+ */
99
+void bdrv_query_block_graph_info(BlockDriverState *bs,
100
+ BlockGraphInfo **p_info,
101
+ Error **errp)
102
+{
103
+ BlockGraphInfo *info;
104
+ BlockChildInfoList **children_list_tail;
105
+ BdrvChild *c;
106
+ ERRP_GUARD();
107
+
108
+ info = g_new0(BlockGraphInfo, 1);
109
+ bdrv_do_query_node_info(bs, qapi_BlockGraphInfo_base(info), errp);
110
+ if (*errp) {
111
+ goto fail;
112
+ }
113
+
114
+ children_list_tail = &info->children;
115
+
116
+ QLIST_FOREACH(c, &bs->children, next) {
117
+ BlockChildInfo *c_info;
118
+
119
+ c_info = g_new0(BlockChildInfo, 1);
120
+ QAPI_LIST_APPEND(children_list_tail, c_info);
121
+
122
+ c_info->name = g_strdup(c->name);
123
+ bdrv_query_block_graph_info(c->bs, &c_info->info, errp);
124
+ if (*errp) {
125
+ goto fail;
126
+ }
127
+ }
128
+
129
+ *p_info = info;
130
+ return;
131
+
132
+fail:
133
+ assert(*errp != NULL);
134
+ qapi_free_BlockGraphInfo(info);
135
+}
136
+
137
/* @p_info will be set only on success. */
138
static void bdrv_query_info(BlockBackend *blk, BlockInfo **p_info,
139
Error **errp)
35
--
140
--
36
2.13.6
141
2.38.1
37
38
diff view generated by jsdifflib
1
This moves reference counting from BlockJob to Job.
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
In order to keep calling the BlockJob cleanup code when the job is
3
In order to let qemu-img info present a block graph, add a parameter to
4
deleted via job_unref(), introduce a new JobDriver.free callback. Every
4
bdrv_node_info_dump() and bdrv_image_info_specific_dump() so that the
5
block job must use block_job_free() for this callback, this is asserted
5
information of nodes below the root level can be given an indentation.
6
in block_job_create().
7
6
7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20220620162704.80987-9-hreitz@redhat.com>
9
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
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
---
11
---
12
include/block/blockjob.h | 21 -------------------
12
include/block/qapi.h | 5 ++--
13
include/block/blockjob_int.h | 7 +++++++
13
block/monitor/block-hmp-cmds.c | 2 +-
14
include/qemu/job.h | 19 ++++++++++++++++--
14
block/qapi.c | 47 +++++++++++++++++++---------------
15
block/backup.c | 1 +
15
qemu-img.c | 2 +-
16
block/commit.c | 1 +
16
qemu-io-cmds.c | 3 ++-
17
block/mirror.c | 2 ++
17
5 files changed, 34 insertions(+), 25 deletions(-)
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
18
27
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
19
diff --git a/include/block/qapi.h b/include/block/qapi.h
28
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/blockjob.h
21
--- a/include/block/qapi.h
30
+++ b/include/block/blockjob.h
22
+++ b/include/block/qapi.h
31
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
23
@@ -XXX,XX +XXX,XX @@ void bdrv_query_block_graph_info(BlockDriverState *bs,
32
/** The opaque value that is passed to the completion function. */
24
33
void *opaque;
25
void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
34
26
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
35
- /** Reference count of the block job */
27
- const char *prefix);
36
- int refcnt;
28
-void bdrv_node_info_dump(BlockNodeInfo *info);
37
-
29
+ const char *prefix,
38
/** True when job has reported completion by calling block_job_completed. */
30
+ int indentation);
39
bool completed;
31
+void bdrv_node_info_dump(BlockNodeInfo *info, int indentation);
40
32
#endif
41
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
33
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
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
34
index XXXXXXX..XXXXXXX 100644
68
--- a/include/block/blockjob_int.h
35
--- a/block/monitor/block-hmp-cmds.c
69
+++ b/include/block/blockjob_int.h
36
+++ b/block/monitor/block-hmp-cmds.c
70
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
37
@@ -XXX,XX +XXX,XX @@ static void print_block_info(Monitor *mon, BlockInfo *info,
71
BlockCompletionFunc *cb, void *opaque, Error **errp);
38
monitor_printf(mon, "\nImages:\n");
72
39
image_info = inserted->image;
73
/**
40
while (1) {
74
+ * block_job_free:
41
- bdrv_node_info_dump(qapi_ImageInfo_base(image_info));
75
+ * Callback to be used for JobDriver.free in all block jobs. Frees block job
42
+ bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0);
76
+ * specific resources in @job.
43
if (image_info->backing_image) {
77
+ */
44
image_info = image_info->backing_image;
78
+void block_job_free(Job *job);
45
} else {
46
diff --git a/block/qapi.c b/block/qapi.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block/qapi.c
49
+++ b/block/qapi.c
50
@@ -XXX,XX +XXX,XX @@ static bool qobject_is_empty_dump(const QObject *obj)
51
* prepending an optional prefix if the dump is not empty.
52
*/
53
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
54
- const char *prefix)
55
+ const char *prefix,
56
+ int indentation)
57
{
58
QObject *obj, *data;
59
Visitor *v = qobject_output_visitor_new(&obj);
60
@@ -XXX,XX +XXX,XX @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
61
data = qdict_get(qobject_to(QDict, obj), "data");
62
if (!qobject_is_empty_dump(data)) {
63
if (prefix) {
64
- qemu_printf("%s", prefix);
65
+ qemu_printf("%*s%s", indentation * 4, "", prefix);
66
}
67
- dump_qobject(1, data);
68
+ dump_qobject(indentation + 1, data);
69
}
70
qobject_unref(obj);
71
visit_free(v);
72
}
73
74
-void bdrv_node_info_dump(BlockNodeInfo *info)
75
+void bdrv_node_info_dump(BlockNodeInfo *info, int indentation)
76
{
77
char *size_buf, *dsize_buf;
78
+ g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, "");
79
+
79
+
80
+/**
80
if (!info->has_actual_size) {
81
* block_job_sleep_ns:
81
dsize_buf = g_strdup("unavailable");
82
* @job: The job that calls the function.
82
} else {
83
* @ns: How many nanoseconds to stop for.
83
dsize_buf = size_to_str(info->actual_size);
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
}
84
}
246
85
size_buf = size_to_str(info->virtual_size);
247
- block_job_unref(job);
86
- qemu_printf("image: %s\n"
248
+ job_unref(&job->job);
87
- "file format: %s\n"
249
}
88
- "virtual size: %s (%" PRId64 " bytes)\n"
250
89
- "disk size: %s\n",
251
static char *child_job_get_parent_desc(BdrvChild *c)
90
- info->filename, info->format, size_buf,
252
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
91
- info->virtual_size,
253
job->deferred_to_main_loop = true;
92
- dsize_buf);
254
block_job_txn_del_job(job);
93
+ qemu_printf("%simage: %s\n"
255
job_state_transition(&job->job, JOB_STATUS_NULL);
94
+ "%sfile format: %s\n"
256
- block_job_unref(job);
95
+ "%svirtual size: %s (%" PRId64 " bytes)\n"
257
+ job_unref(&job->job);
96
+ "%sdisk size: %s\n",
258
}
97
+ ind_s, info->filename,
259
98
+ ind_s, info->format,
260
static void block_job_do_dismiss(BlockJob *job)
99
+ ind_s, size_buf, info->virtual_size,
261
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
100
+ ind_s, dsize_buf);
262
101
g_free(size_buf);
263
assert(blk_bs(job->blk)->job == job);
102
g_free(dsize_buf);
264
103
265
- block_job_ref(job);
104
if (info->has_encrypted && info->encrypted) {
266
+ job_ref(&job->job);
105
- qemu_printf("encrypted: yes\n");
267
106
+ qemu_printf("%sencrypted: yes\n", ind_s);
268
if (finish) {
269
finish(job, &local_err);
270
}
107
}
271
if (local_err) {
108
272
error_propagate(errp, local_err);
109
if (info->has_cluster_size) {
273
- block_job_unref(job);
110
- qemu_printf("cluster_size: %" PRId64 "\n",
274
+ job_unref(&job->job);
111
- info->cluster_size);
275
return -EBUSY;
112
+ qemu_printf("%scluster_size: %" PRId64 "\n",
113
+ ind_s, info->cluster_size);
276
}
114
}
277
/* block_job_drain calls block_job_enter, and it should be enough to
115
278
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
116
if (info->has_dirty_flag && info->dirty_flag) {
279
aio_poll(qemu_get_aio_context(), true);
117
- qemu_printf("cleanly shut down: no\n");
118
+ qemu_printf("%scleanly shut down: no\n", ind_s);
280
}
119
}
281
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
120
282
- block_job_unref(job);
121
if (info->backing_filename) {
283
+ job_unref(&job->job);
122
- qemu_printf("backing file: %s", info->backing_filename);
284
return ret;
123
+ qemu_printf("%sbacking file: %s", ind_s, info->backing_filename);
285
}
124
if (!info->full_backing_filename) {
286
125
qemu_printf(" (cannot determine actual path)");
287
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
126
} else if (strcmp(info->backing_filename,
127
@@ -XXX,XX +XXX,XX @@ void bdrv_node_info_dump(BlockNodeInfo *info)
128
}
129
qemu_printf("\n");
130
if (info->backing_filename_format) {
131
- qemu_printf("backing file format: %s\n",
132
- info->backing_filename_format);
133
+ qemu_printf("%sbacking file format: %s\n",
134
+ ind_s, info->backing_filename_format);
135
}
288
}
136
}
289
137
290
assert(is_block_job(&job->job));
138
if (info->has_snapshots) {
291
+ assert(job->job.driver->free == &block_job_free);
139
SnapshotInfoList *elem;
292
140
293
job->driver = driver;
141
- qemu_printf("Snapshot list:\n");
294
job->blk = blk;
142
+ qemu_printf("%sSnapshot list:\n", ind_s);
295
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
143
+ qemu_printf("%s", ind_s);
296
job->busy = false;
144
bdrv_snapshot_dump(NULL);
297
job->paused = true;
145
qemu_printf("\n");
298
job->pause_count = 1;
146
299
- job->refcnt = 1;
147
@@ -XXX,XX +XXX,XX @@ void bdrv_node_info_dump(BlockNodeInfo *info)
300
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
148
301
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
149
pstrcpy(sn.id_str, sizeof(sn.id_str), elem->value->id);
302
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
150
pstrcpy(sn.name, sizeof(sn.name), elem->value->name);
303
diff --git a/job.c b/job.c
151
+ qemu_printf("%s", ind_s);
304
index XXXXXXX..XXXXXXX 100644
152
bdrv_snapshot_dump(&sn);
305
--- a/job.c
153
qemu_printf("\n");
306
+++ b/job.c
154
}
307
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
155
@@ -XXX,XX +XXX,XX @@ void bdrv_node_info_dump(BlockNodeInfo *info)
308
job = g_malloc0(driver->instance_size);
156
309
job->driver = driver;
157
if (info->format_specific) {
310
job->id = g_strdup(job_id);
158
bdrv_image_info_specific_dump(info->format_specific,
311
+ job->refcnt = 1;
159
- "Format specific information:\n");
312
160
+ "Format specific information:\n",
313
job_state_transition(job, JOB_STATUS_CREATED);
161
+ indentation);
314
162
}
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
}
163
}
343
diff --git a/qemu-img.c b/qemu-img.c
164
diff --git a/qemu-img.c b/qemu-img.c
344
index XXXXXXX..XXXXXXX 100644
165
index XXXXXXX..XXXXXXX 100644
345
--- a/qemu-img.c
166
--- a/qemu-img.c
346
+++ b/qemu-img.c
167
+++ b/qemu-img.c
347
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
168
@@ -XXX,XX +XXX,XX @@ static void dump_human_image_info_list(BlockNodeInfoList *list)
348
int ret = 0;
169
}
349
170
delim = true;
350
aio_context_acquire(aio_context);
171
351
- block_job_ref(job);
172
- bdrv_node_info_dump(elem->value);
352
+ job_ref(&job->job);
173
+ bdrv_node_info_dump(elem->value, 0);
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
}
174
}
360
- block_job_unref(job);
175
}
361
+ job_unref(&job->job);
176
362
aio_context_release(aio_context);
177
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
363
364
/* publish completion progress only when success */
365
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
366
index XXXXXXX..XXXXXXX 100644
178
index XXXXXXX..XXXXXXX 100644
367
--- a/tests/test-bdrv-drain.c
179
--- a/qemu-io-cmds.c
368
+++ b/tests/test-bdrv-drain.c
180
+++ b/qemu-io-cmds.c
369
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
181
@@ -XXX,XX +XXX,XX @@ static int info_f(BlockBackend *blk, int argc, char **argv)
370
BlockJobDriver test_job_driver = {
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
}
182
}
421
assert(job->job.status == JOB_STATUS_NULL);
183
if (spec_info) {
422
- block_job_unref(job);
184
bdrv_image_info_specific_dump(spec_info,
423
+ job_unref(&job->job);
185
- "Format specific information:\n");
424
destroy_blk(blk);
186
+ "Format specific information:\n",
425
}
187
+ 0);
188
qapi_free_ImageInfoSpecific(spec_info);
189
}
426
190
427
--
191
--
428
2.13.6
192
2.38.1
429
430
diff view generated by jsdifflib
1
NFS paths were only partially filtered in _filter_img_create, _img_info
1
From: Hanna Reitz <hreitz@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
Before we let qemu-img info print child node information, have
4
common.filter, common.rc, and iotests.py filter it from the test output
5
so we get as few reference output changes as possible.
6
7
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
8
Message-Id: <20220620162704.80987-10-hreitz@redhat.com>
9
Tested-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Fam Zheng <famz@redhat.com>
9
---
11
---
10
tests/qemu-iotests/126.out | 2 +-
12
tests/qemu-iotests/iotests.py | 18 +++++++++++++++---
11
tests/qemu-iotests/common.filter | 6 ++++--
13
tests/qemu-iotests/common.filter | 22 ++++++++++++++--------
12
tests/qemu-iotests/common.rc | 8 +++++++-
14
tests/qemu-iotests/common.rc | 22 ++++++++++++++--------
13
3 files changed, 12 insertions(+), 4 deletions(-)
15
3 files changed, 43 insertions(+), 19 deletions(-)
14
16
15
diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out
17
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
16
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/126.out
19
--- a/tests/qemu-iotests/iotests.py
18
+++ b/tests/qemu-iotests/126.out
20
+++ b/tests/qemu-iotests/iotests.py
19
@@ -XXX,XX +XXX,XX @@ QA output created by 126
21
@@ -XXX,XX +XXX,XX @@ def qemu_img_log(*args: str, check: bool = True
20
=== Testing plain files ===
22
21
23
def img_info_log(filename: str, filter_path: Optional[str] = None,
22
Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
24
use_image_opts: bool = False, extra_args: Sequence[str] = (),
23
-Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
25
- check: bool = True,
24
+Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
26
+ check: bool = True, drop_child_info: bool = True,
25
27
) -> None:
26
=== Testing relative backing filename resolution ===
28
args = ['info']
27
29
if use_image_opts:
30
@@ -XXX,XX +XXX,XX @@ def img_info_log(filename: str, filter_path: Optional[str] = None,
31
output = qemu_img(*args, check=check).stdout
32
if not filter_path:
33
filter_path = filename
34
- log(filter_img_info(output, filter_path))
35
+ log(filter_img_info(output, filter_path, drop_child_info))
36
37
def qemu_io_wrap_args(args: Sequence[str]) -> List[str]:
38
if '-f' in args or '--image-opts' in args:
39
@@ -XXX,XX +XXX,XX @@ def _filter(_key, value):
40
def filter_generated_node_ids(msg):
41
return re.sub("#block[0-9]+", "NODE_NAME", msg)
42
43
-def filter_img_info(output, filename):
44
+def filter_img_info(output: str, filename: str,
45
+ drop_child_info: bool = True) -> str:
46
lines = []
47
+ drop_indented = False
48
for line in output.split('\n'):
49
if 'disk size' in line or 'actual-size' in line:
50
continue
51
+
52
+ # Drop child node info
53
+ if drop_indented:
54
+ if line.startswith(' '):
55
+ continue
56
+ drop_indented = False
57
+ if drop_child_info and "Child node '/" in line:
58
+ drop_indented = True
59
+ continue
60
+
61
line = line.replace(filename, 'TEST_IMG')
62
line = filter_testfiles(line)
63
line = line.replace(imgfmt, 'IMGFMT')
28
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
64
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
29
index XXXXXXX..XXXXXXX 100644
65
index XXXXXXX..XXXXXXX 100644
30
--- a/tests/qemu-iotests/common.filter
66
--- a/tests/qemu-iotests/common.filter
31
+++ b/tests/qemu-iotests/common.filter
67
+++ b/tests/qemu-iotests/common.filter
32
@@ -XXX,XX +XXX,XX @@ _filter_actual_image_size()
33
# replace driver-specific options in the "Formatting..." line
34
_filter_img_create()
35
{
36
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
37
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
38
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
39
-e "s#$TEST_DIR#TEST_DIR#g" \
40
-e "s#$IMGFMT#IMGFMT#g" \
41
-e 's#nbd:127.0.0.1:10810#TEST_DIR/t.IMGFMT#g' \
42
@@ -XXX,XX +XXX,XX @@ _filter_img_info()
68
@@ -XXX,XX +XXX,XX @@ _filter_img_info()
43
69
44
discard=0
70
discard=0
45
regex_json_spec_start='^ *"format-specific": \{'
71
regex_json_spec_start='^ *"format-specific": \{'
46
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
72
+ regex_json_child_start='^ *"children": \['
47
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
73
gsed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
48
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
74
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
49
-e "s#$TEST_DIR#TEST_DIR#g" \
75
-e "s#$TEST_DIR#TEST_DIR#g" \
50
-e "s#$IMGFMT#IMGFMT#g" \
76
@@ -XXX,XX +XXX,XX @@ _filter_img_info()
51
-e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \
77
-e 's/\(compression type: \)\(zlib\|zstd\)/\1COMPRESSION_TYPE/' \
78
-e "s/uuid: [-a-f0-9]\\+/uuid: 00000000-0000-0000-0000-000000000000/" | \
79
while IFS='' read -r line; do
80
- if [[ $format_specific == 1 ]]; then
81
- discard=0
82
- elif [[ $line == "Format specific information:" ]]; then
83
- discard=1
84
- elif [[ $line =~ $regex_json_spec_start ]]; then
85
- discard=2
86
- regex_json_spec_end="^${line%%[^ ]*}\\},? *$"
87
+ if [[ $discard == 0 ]]; then
88
+ if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then
89
+ discard=1
90
+ elif [[ $line =~ "Child node '/" ]]; then
91
+ discard=1
92
+ elif [[ $line =~ $regex_json_spec_start ]]; then
93
+ discard=2
94
+ regex_json_end="^${line%%[^ ]*}\\},? *$"
95
+ elif [[ $line =~ $regex_json_child_start ]]; then
96
+ discard=2
97
+ regex_json_end="^${line%%[^ ]*}\\],? *$"
98
+ fi
99
fi
100
if [[ $discard == 0 ]]; then
101
echo "$line"
102
elif [[ $discard == 1 && ! $line ]]; then
103
echo
104
discard=0
105
- elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then
106
+ elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then
107
discard=0
108
fi
109
done
52
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
110
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
53
index XXXXXXX..XXXXXXX 100644
111
index XXXXXXX..XXXXXXX 100644
54
--- a/tests/qemu-iotests/common.rc
112
--- a/tests/qemu-iotests/common.rc
55
+++ b/tests/qemu-iotests/common.rc
113
+++ 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()
114
@@ -XXX,XX +XXX,XX @@ _img_info()
115
76
discard=0
116
discard=0
77
regex_json_spec_start='^ *"format-specific": \{'
117
regex_json_spec_start='^ *"format-specific": \{'
118
+ regex_json_child_start='^ *"children": \['
78
$QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
119
$QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
79
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
120
sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
80
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
121
-e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
81
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
122
@@ -XXX,XX +XXX,XX @@ _img_info()
82
-e "s#$TEST_DIR#TEST_DIR#g" \
83
-e "s#$IMGFMT#IMGFMT#g" \
84
-e "/^disk size:/ D" \
123
-e "/^disk size:/ D" \
124
-e "/actual-size/ D" | \
125
while IFS='' read -r line; do
126
- if [[ $format_specific == 1 ]]; then
127
- discard=0
128
- elif [[ $line == "Format specific information:" ]]; then
129
- discard=1
130
- elif [[ $line =~ $regex_json_spec_start ]]; then
131
- discard=2
132
- regex_json_spec_end="^${line%%[^ ]*}\\},? *$"
133
+ if [[ $discard == 0 ]]; then
134
+ if [[ $format_specific == 0 && $line == "Format specific information:" ]]; then
135
+ discard=1
136
+ elif [[ $line =~ "Child node '/" ]]; then
137
+ discard=1
138
+ elif [[ $format_specific == 0 && $line =~ $regex_json_spec_start ]]; then
139
+ discard=2
140
+ regex_json_end="^${line%%[^ ]*}\\},? *$"
141
+ elif [[ $line =~ $regex_json_child_start ]]; then
142
+ discard=2
143
+ regex_json_end="^${line%%[^ ]*}\\],? *$"
144
+ fi
145
fi
146
if [[ $discard == 0 ]]; then
147
echo "$line"
148
elif [[ $discard == 1 && ! $line ]]; then
149
echo
150
discard=0
151
- elif [[ $discard == 2 && $line =~ $regex_json_spec_end ]]; then
152
+ elif [[ $discard == 2 && $line =~ $regex_json_end ]]; then
153
discard=0
154
fi
155
done
85
--
156
--
86
2.13.6
157
2.38.1
87
88
diff view generated by jsdifflib
Deleted patch
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
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
8
tests/qemu-iotests/086 | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
10
11
diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/086
14
+++ b/tests/qemu-iotests/086
15
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
16
. ./common.filter
17
18
_supported_fmt qcow2 raw
19
-_supported_proto file nfs
20
+_supported_proto file
21
_supported_os Linux
22
23
function run_qemu_img()
24
--
25
2.13.6
26
27
diff view generated by jsdifflib
1
From: Peter Maydell <peter.maydell@linaro.org>
1
From: Hanna Reitz <hreitz@redhat.com>
2
2
3
In commit 8b9ad56e9cbfd852a, we removed the code that could result
3
These tests read size information (sometimes disk size, sometimes
4
in our getting to sd_prealloc()'s out_with_err_set label with a
4
virtual size) from qemu-img info's output. Once qemu-img starts
5
NULL blk pointer. That makes the NULL check in the error-handling
5
printing info about child nodes, we are going to see multiple instances
6
path unnecessary, and Coverity gripes about it (CID 1390636).
6
of that per image, but these tests are only interested in the first one,
7
Delete the redundant check.
7
so use "head -n 1" to get it.
8
8
9
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
9
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
10
Message-Id: <20220620162704.80987-11-hreitz@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
13
---
12
block/sheepdog.c | 4 +---
14
tests/qemu-iotests/106 | 4 ++--
13
1 file changed, 1 insertion(+), 3 deletions(-)
15
tests/qemu-iotests/214 | 6 ++++--
16
tests/qemu-iotests/308 | 4 ++--
17
3 files changed, 8 insertions(+), 6 deletions(-)
14
18
15
diff --git a/block/sheepdog.c b/block/sheepdog.c
19
diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106
16
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100755
17
--- a/block/sheepdog.c
21
--- a/tests/qemu-iotests/106
18
+++ b/block/sheepdog.c
22
+++ b/tests/qemu-iotests/106
19
@@ -XXX,XX +XXX,XX @@ out:
23
@@ -XXX,XX +XXX,XX @@ for create_mode in off falloc full; do
20
error_setg_errno(errp, -ret, "Can't pre-allocate");
24
expected_size=$((expected_size + $GROWTH_SIZE))
21
}
25
fi
22
out_with_err_set:
26
23
- if (blk) {
27
- actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size')
24
- blk_unref(blk);
28
+ actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1)
25
- }
29
actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/')
26
+ blk_unref(blk);
30
27
g_free(buf);
31
# The actual size may exceed the expected size, depending on the file
28
32
@@ -XXX,XX +XXX,XX @@ for growth_mode in falloc full; do
29
return ret;
33
_make_test_img -o "extent_size_hint=0" 2G
34
$QEMU_IMG resize -f "$IMGFMT" --preallocation=$growth_mode "$TEST_IMG" +${GROWTH_SIZE}K
35
36
- actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size')
37
+ actual_size=$($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep 'disk size' | head -n 1)
38
actual_size=$(echo "$actual_size" | sed -e 's/^[^0-9]*\([0-9]\+\).*$/\1/')
39
40
if [ $actual_size -lt $GROWTH_SIZE ]; then
41
diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214
42
index XXXXXXX..XXXXXXX 100755
43
--- a/tests/qemu-iotests/214
44
+++ b/tests/qemu-iotests/214
45
@@ -XXX,XX +XXX,XX @@ let data_size="8 * $cluster_size"
46
$QEMU_IO -c "write -P 0xaa 0 $data_size" "$TEST_IMG" \
47
2>&1 | _filter_qemu_io | _filter_testdir
48
sizeA=$($QEMU_IMG info --output=json "$TEST_IMG" |
49
- sed -n '/"actual-size":/ s/[^0-9]//gp')
50
+ sed -n '/"actual-size":/ s/[^0-9]//gp' |
51
+ head -n 1)
52
53
_make_test_img 2M -o cluster_size=$cluster_size
54
echo "Write compressed data:"
55
@@ -XXX,XX +XXX,XX @@ $QEMU_IO -c "write -P 0xcc $offset $data_size" "json:{\
56
_filter_qemu_io | _filter_testdir
57
58
sizeB=$($QEMU_IMG info --output=json "$TEST_IMG" |
59
- sed -n '/"actual-size":/ s/[^0-9]//gp')
60
+ sed -n '/"actual-size":/ s/[^0-9]//gp' |
61
+ head -n 1)
62
63
if [ $sizeA -lt $sizeB ]
64
then
65
diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308
66
index XXXXXXX..XXXXXXX 100755
67
--- a/tests/qemu-iotests/308
68
+++ b/tests/qemu-iotests/308
69
@@ -XXX,XX +XXX,XX @@ echo
70
echo '=== Remove export ==='
71
72
# Double-check that $EXT_MP appears as a non-empty file (the raw image)
73
-$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
74
+$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1
75
76
fuse_export_del 'export-mp'
77
78
# See that the file appears empty again
79
-$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size'
80
+$QEMU_IMG info -f raw "$EXT_MP" | grep 'virtual size' | head -n 1
81
82
echo
83
echo '=== Writable export ==='
30
--
84
--
31
2.13.6
85
2.38.1
32
33
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
This moves the job_type field from BlockJobDriver to JobDriver.
2
1
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: John Snow <jsnow@redhat.com>
6
---
7
include/block/blockjob_int.h | 3 ---
8
include/qemu/job.h | 11 +++++++++++
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
17
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob_int.h
20
+++ b/include/block/blockjob_int.h
21
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
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;
157
}
158
159
- qapi_event_send_block_job_cancelled(job->driver->job_type,
160
+ qapi_event_send_block_job_cancelled(job_type(&job->job),
161
job->job.id,
162
job->len,
163
job->offset,
164
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
165
return;
166
}
167
168
- qapi_event_send_block_job_completed(job->driver->job_type,
169
+ qapi_event_send_block_job_completed(job_type(&job->job),
170
job->job.id,
171
job->len,
172
job->offset,
173
@@ -XXX,XX +XXX,XX @@ static int block_job_event_pending(BlockJob *job)
174
{
175
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
176
if (!job->auto_finalize && !block_job_is_internal(job)) {
177
- qapi_event_send_block_job_pending(job->driver->job_type,
178
+ qapi_event_send_block_job_pending(job_type(&job->job),
179
job->job.id,
180
&error_abort);
181
}
182
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
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
--
222
2.13.6
223
224
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
Move the defer_to_main_loop functionality from BlockJob to Job.
2
1
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
14
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
---
18
include/block/blockjob.h | 5 ----
19
include/block/blockjob_int.h | 19 ---------------
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
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
426
index XXXXXXX..XXXXXXX 100644
427
--- a/tests/test-bdrv-drain.c
428
+++ b/tests/test-bdrv-drain.c
429
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
430
bool should_complete;
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
}
440
441
static void coroutine_fn test_job_start(void *opaque)
442
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
443
block_job_sleep_ns(&s->common, 100000);
444
}
445
446
- block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
447
+ job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
448
}
449
450
static void test_job_complete(BlockJob *job, Error **errp)
451
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
452
index XXXXXXX..XXXXXXX 100644
453
--- a/tests/test-blockjob-txn.c
454
+++ b/tests/test-blockjob-txn.c
455
@@ -XXX,XX +XXX,XX @@ typedef struct {
456
int *result;
457
} TestBlockJob;
458
459
-static void test_block_job_complete(BlockJob *job, void *opaque)
460
+static void test_block_job_complete(Job *job, void *opaque)
461
{
462
- BlockDriverState *bs = blk_bs(job->blk);
463
+ BlockJob *bjob = container_of(job, BlockJob, job);
464
+ BlockDriverState *bs = blk_bs(bjob->blk);
465
int rc = (intptr_t)opaque;
466
467
- if (job_is_cancelled(&job->job)) {
468
+ if (job_is_cancelled(job)) {
469
rc = -ECANCELED;
470
}
471
472
- block_job_completed(job, rc);
473
+ block_job_completed(bjob, rc);
474
bdrv_unref(bs);
475
}
476
477
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
478
}
479
}
480
481
- block_job_defer_to_main_loop(job, test_block_job_complete,
482
- (void *)(intptr_t)s->rc);
483
+ job_defer_to_main_loop(&job->job, test_block_job_complete,
484
+ (void *)(intptr_t)s->rc);
485
}
486
487
typedef struct {
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
489
index XXXXXXX..XXXXXXX 100644
490
--- a/tests/test-blockjob.c
491
+++ b/tests/test-blockjob.c
492
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
493
bool completed;
494
} CancelJob;
495
496
-static void cancel_job_completed(BlockJob *job, void *opaque)
497
+static void cancel_job_completed(Job *job, void *opaque)
498
{
499
+ BlockJob *bjob = container_of(job, BlockJob, job);
500
CancelJob *s = opaque;
501
s->completed = true;
502
- block_job_completed(job, 0);
503
+ block_job_completed(bjob, 0);
504
}
505
506
static void cancel_job_complete(BlockJob *job, Error **errp)
507
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
508
}
509
510
defer:
511
- block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
512
+ job_defer_to_main_loop(&s->common.job, cancel_job_completed, s);
513
}
514
515
static const BlockJobDriver test_cancel_driver = {
516
--
517
2.13.6
518
519
diff view generated by jsdifflib
1
This moves the logic that implements job transactions from BlockJob to
1
From: Hanna Reitz <hreitz@redhat.com>
2
Job.
2
3
3
For every node in the backing chain, collect its BlockGraphInfo struct
4
using bdrv_query_block_graph_info(). Print all nodes' information,
5
indenting child nodes and labelling them with a path constructed from
6
the child names leading to the node from the root (e.g. /file/file).
7
8
Note that we open each image with BDRV_O_NO_BACKING, so its backing
9
child is omitted from this graph, and thus presented in the previous
10
manner: By simply concatenating all images' information, separated with
11
blank lines.
12
13
This affects two iotests:
14
- 065: Here we try to get the format node's format specific information.
15
The pre-patch code does so by taking all lines from "Format specific
16
information:" until an empty line. This format specific information
17
is no longer followed by an empty line, though, but by child node
18
information, so limit the range by "Child node '/file':".
19
- 302: Calls qemu_img() for qemu-img info directly, which does not
20
filter the output, so the child node information ends up in the
21
output.
22
23
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
24
Message-Id: <20220620162704.80987-12-hreitz@redhat.com>
25
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
26
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
27
---
7
include/block/blockjob.h | 54 ----------
28
qapi/block-core.json | 4 +--
8
include/block/blockjob_int.h | 10 --
29
qemu-img.c | 69 ++++++++++++++++++++++++++------------
9
include/qemu/job.h | 71 +++++++++++--
30
tests/qemu-iotests/065 | 2 +-
10
blockdev.c | 6 +-
31
tests/qemu-iotests/302.out | 5 +++
11
blockjob.c | 238 +------------------------------------------
32
4 files changed, 56 insertions(+), 24 deletions(-)
12
job.c | 234 ++++++++++++++++++++++++++++++++++++++++--
33
13
tests/test-blockjob-txn.c | 12 +--
34
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
tests/test-blockjob.c | 2 +-
15
8 files changed, 303 insertions(+), 324 deletions(-)
16
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
index XXXXXXX..XXXXXXX 100644
35
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
36
--- a/qapi/block-core.json
20
+++ b/include/block/blockjob.h
37
+++ b/qapi/block-core.json
21
@@ -XXX,XX +XXX,XX @@
38
@@ -XXX,XX +XXX,XX @@
22
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
39
##
23
40
# @DummyBlockCoreForceArrays:
24
typedef struct BlockJobDriver BlockJobDriver;
41
#
25
-typedef struct JobTxn JobTxn;
42
-# Not used by QMP; hack to let us use BlockNodeInfoList internally
26
43
+# Not used by QMP; hack to let us use BlockGraphInfoList internally
27
/**
44
#
28
* BlockJob:
45
# Since: 8.0
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
46
##
30
47
{ 'struct': 'DummyBlockCoreForceArrays',
31
/** BlockDriverStates that are involved in this block job */
48
- 'data': { 'unused-block-node-info': ['BlockNodeInfo'] } }
32
GSList *nodes;
49
+ 'data': { 'unused-block-graph-info': ['BlockGraphInfo'] } }
33
-
50
diff --git a/qemu-img.c b/qemu-img.c
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
51
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/blockjob_int.h
52
--- a/qemu-img.c
106
+++ b/include/block/blockjob_int.h
53
+++ b/qemu-img.c
107
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
54
@@ -XXX,XX +XXX,XX @@ static void dump_snapshots(BlockDriverState *bs)
108
/** Generic JobDriver callbacks and settings */
55
g_free(sn_tab);
109
JobDriver job_driver;
56
}
110
57
111
- /**
58
-static void dump_json_block_node_info_list(BlockNodeInfoList *list)
112
- * If the callback is not NULL, prepare will be invoked when all the jobs
59
+static void dump_json_block_graph_info_list(BlockGraphInfoList *list)
113
- * belonging to the same transaction complete; or upon this job's completion
60
{
114
- * if it is not in a transaction.
61
GString *str;
115
- *
62
QObject *obj;
116
- * This callback will not be invoked if the job has already failed.
63
Visitor *v = qobject_output_visitor_new(&obj);
117
- * If it fails, abort and then clean will be called.
64
118
- */
65
- visit_type_BlockNodeInfoList(v, NULL, &list, &error_abort);
119
- int (*prepare)(BlockJob *job);
66
+ visit_type_BlockGraphInfoList(v, NULL, &list, &error_abort);
120
-
67
visit_complete(v, &obj);
121
/*
68
str = qobject_to_json_pretty(obj, true);
122
* If the callback is not NULL, it will be invoked before the job is
69
assert(str != NULL);
123
* resumed in a new AioContext. This is the place to move any resources
70
@@ -XXX,XX +XXX,XX @@ static void dump_json_block_node_info_list(BlockNodeInfoList *list)
124
diff --git a/include/qemu/job.h b/include/qemu/job.h
71
g_string_free(str, true);
125
index XXXXXXX..XXXXXXX 100644
72
}
126
--- a/include/qemu/job.h
73
127
+++ b/include/qemu/job.h
74
-static void dump_json_block_node_info(BlockNodeInfo *info)
128
@@ -XXX,XX +XXX,XX @@
75
+static void dump_json_block_graph_info(BlockGraphInfo *info)
129
#include "block/aio.h"
76
{
130
77
GString *str;
131
typedef struct JobDriver JobDriver;
78
QObject *obj;
132
+typedef struct JobTxn JobTxn;
79
Visitor *v = qobject_output_visitor_new(&obj);
133
+
80
134
81
- visit_type_BlockNodeInfo(v, NULL, &info, &error_abort);
135
/**
82
+ visit_type_BlockGraphInfo(v, NULL, &info, &error_abort);
136
* Long-running operation.
83
visit_complete(v, &obj);
137
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
84
str = qobject_to_json_pretty(obj, true);
138
/** Element of the list of jobs */
85
assert(str != NULL);
139
QLIST_ENTRY(Job) job_list;
86
@@ -XXX,XX +XXX,XX @@ static void dump_json_block_node_info(BlockNodeInfo *info)
140
87
g_string_free(str, true);
141
+ /** Transaction this job is part of */
88
}
142
+ JobTxn *txn;
89
143
+
90
-static void dump_human_image_info_list(BlockNodeInfoList *list)
144
/** Element of the list of jobs in a job transaction */
91
+static void dump_human_image_info(BlockGraphInfo *info, int indentation,
145
QLIST_ENTRY(Job) txn_list;
92
+ const char *path)
146
} Job;
93
{
147
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
94
- BlockNodeInfoList *elem;
148
void (*drain)(Job *job);
95
+ BlockChildInfoList *children_list;
149
96
+
150
/**
97
+ bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation);
151
+ * If the callback is not NULL, prepare will be invoked when all the jobs
98
+
152
+ * belonging to the same transaction complete; or upon this job's completion
99
+ for (children_list = info->children; children_list;
153
+ * if it is not in a transaction.
100
+ children_list = children_list->next)
154
+ *
101
+ {
155
+ * This callback will not be invoked if the job has already failed.
102
+ BlockChildInfo *child = children_list->value;
156
+ * If it fails, abort and then clean will be called.
103
+ g_autofree char *child_path;
157
+ */
104
+
158
+ int (*prepare)(Job *job);
105
+ printf("%*sChild node '%s%s':\n",
159
+
106
+ indentation * 4, "", path, child->name);
160
+ /**
107
+ child_path = g_strdup_printf("%s%s/", path, child->name);
161
* If the callback is not NULL, it will be invoked when all the jobs
108
+ dump_human_image_info(child->info, indentation + 1, child_path);
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
}
280
281
void qmp_eject(bool has_device, const char *device,
282
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
283
}
284
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
+};
620
+
621
/* Right now, this mutex is only needed to synchronize accesses to job->busy
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
+{
630
+ JobTxn *txn = g_new0(JobTxn, 1);
631
+ QLIST_INIT(&txn->jobs);
632
+ txn->refcnt = 1;
633
+ return txn;
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
+ }
109
+ }
646
+}
110
+}
647
+
111
+
648
+void job_txn_add_job(JobTxn *txn, Job *job)
112
+static void dump_human_image_info_list(BlockGraphInfoList *list)
649
+{
113
+{
650
+ if (!txn) {
114
+ BlockGraphInfoList *elem;
651
+ return;
115
bool delim = false;
652
+ }
116
653
+
117
for (elem = list; elem; elem = elem->next) {
654
+ assert(!job->txn);
118
@@ -XXX,XX +XXX,XX @@ static void dump_human_image_info_list(BlockNodeInfoList *list)
655
+ job->txn = txn;
119
}
656
+
120
delim = true;
657
+ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
121
658
+ job_txn_ref(txn);
122
- bdrv_node_info_dump(elem->value, 0);
659
+}
123
+ dump_human_image_info(elem->value, 0, "/");
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
+ }
668
+}
669
+
670
+static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
671
+{
672
+ AioContext *ctx;
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
+}
691
+
692
+
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
}
124
}
754
}
125
}
755
126
756
-int job_finalize_single(Job *job)
127
@@ -XXX,XX +XXX,XX @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
757
+static int job_finalize_single(Job *job)
128
}
758
{
129
759
assert(job_is_completed(job));
130
/**
760
131
- * Open an image file chain and return an BlockNodeInfoList
761
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
132
+ * Open an image file chain and return an BlockGraphInfoList
133
*
134
* @filename: topmost image filename
135
* @fmt: topmost image format (may be NULL to autodetect)
136
@@ -XXX,XX +XXX,XX @@ static gboolean str_equal_func(gconstpointer a, gconstpointer b)
137
* opening an image file. If there was an error a message will have been
138
* printed to stderr.
139
*/
140
-static BlockNodeInfoList *collect_image_info_list(bool image_opts,
141
- const char *filename,
142
- const char *fmt,
143
- bool chain, bool force_share)
144
+static BlockGraphInfoList *collect_image_info_list(bool image_opts,
145
+ const char *filename,
146
+ const char *fmt,
147
+ bool chain, bool force_share)
148
{
149
- BlockNodeInfoList *head = NULL;
150
- BlockNodeInfoList **tail = &head;
151
+ BlockGraphInfoList *head = NULL;
152
+ BlockGraphInfoList **tail = &head;
153
GHashTable *filenames;
154
Error *err = NULL;
155
156
@@ -XXX,XX +XXX,XX @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
157
while (filename) {
158
BlockBackend *blk;
159
BlockDriverState *bs;
160
- BlockNodeInfo *info;
161
+ BlockGraphInfo *info;
162
163
if (g_hash_table_lookup_extended(filenames, filename, NULL, NULL)) {
164
error_report("Backing file '%s' creates an infinite loop.",
165
@@ -XXX,XX +XXX,XX @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
762
}
166
}
167
bs = blk_bs(blk);
168
169
- bdrv_query_block_node_info(bs, &info, &err);
170
+ /*
171
+ * Note that the returned BlockGraphInfo object will not have
172
+ * information about this image's backing node, because we have opened
173
+ * it with BDRV_O_NO_BACKING. Printing this object will therefore not
174
+ * duplicate the backing chain information that we obtain by walking
175
+ * the chain manually here.
176
+ */
177
+ bdrv_query_block_graph_info(bs, &info, &err);
178
if (err) {
179
error_report_err(err);
180
blk_unref(blk);
181
@@ -XXX,XX +XXX,XX @@ static BlockNodeInfoList *collect_image_info_list(bool image_opts,
182
return head;
183
184
err:
185
- qapi_free_BlockNodeInfoList(head);
186
+ qapi_free_BlockGraphInfoList(head);
187
g_hash_table_destroy(filenames);
188
return NULL;
189
}
190
@@ -XXX,XX +XXX,XX @@ static int img_info(int argc, char **argv)
191
OutputFormat output_format = OFORMAT_HUMAN;
192
bool chain = false;
193
const char *filename, *fmt, *output;
194
- BlockNodeInfoList *list;
195
+ BlockGraphInfoList *list;
196
bool image_opts = false;
197
bool force_share = false;
198
199
@@ -XXX,XX +XXX,XX @@ static int img_info(int argc, char **argv)
200
break;
201
case OFORMAT_JSON:
202
if (chain) {
203
- dump_json_block_node_info_list(list);
204
+ dump_json_block_graph_info_list(list);
205
} else {
206
- dump_json_block_node_info(list->value);
207
+ dump_json_block_graph_info(list->value);
208
}
209
break;
763
}
210
}
764
211
765
- /* TODO Don't assume it's a BlockJob */
212
- qapi_free_BlockNodeInfoList(list);
766
- block_job_txn_del_job((BlockJob*) job);
213
+ qapi_free_BlockGraphInfoList(list);
767
+ job_txn_del_job(job);
768
job_conclude(job);
769
return 0;
214
return 0;
770
}
215
}
771
216
772
+void job_cancel_async(Job *job, bool force)
217
diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065
773
+{
218
index XXXXXXX..XXXXXXX 100755
774
+ if (job->user_paused) {
219
--- a/tests/qemu-iotests/065
775
+ /* Do not call job_enter here, the caller will handle it. */
220
+++ b/tests/qemu-iotests/065
776
+ job->user_paused = false;
221
@@ -XXX,XX +XXX,XX @@ class TestQemuImgInfo(TestImageInfoSpecific):
777
+ if (job->driver->user_resume) {
222
def test_human(self):
778
+ job->driver->user_resume(job);
223
data = qemu_img('info', '--output=human', test_img).stdout.split('\n')
779
+ }
224
data = data[(data.index('Format specific information:') + 1)
780
+ assert(job->pause_count > 0);
225
- :data.index('')]
781
+ job->pause_count--;
226
+ :data.index("Child node '/file':")]
782
+ }
227
for field in data:
783
+ job->cancelled = true;
228
self.assertTrue(re.match('^ {4}[^ ]', field) is not None)
784
+ /* To prevent 'force == false' overriding a previous 'force == true' */
229
data = [line.strip() for line in data]
785
+ job->force_cancel |= force;
230
diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out
786
+}
787
+
788
+void job_completed_txn_abort(Job *job)
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
{
904
/* Should not be reachable via external interface for internal jobs */
905
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
906
index XXXXXXX..XXXXXXX 100644
231
index XXXXXXX..XXXXXXX 100644
907
--- a/tests/test-blockjob-txn.c
232
--- a/tests/qemu-iotests/302.out
908
+++ b/tests/test-blockjob-txn.c
233
+++ b/tests/qemu-iotests/302.out
909
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
234
@@ -XXX,XX +XXX,XX @@ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
910
JobTxn *txn;
235
file format: raw
911
int result = -EINPROGRESS;
236
virtual size: 448 KiB (458752 bytes)
912
237
disk size: unavailable
913
- txn = block_job_txn_new();
238
+Child node '/file':
914
+ txn = job_txn_new();
239
+ image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
915
job = test_block_job_start(1, true, expected, &result, txn);
240
+ file format: nbd
916
job_start(&job->job);
241
+ virtual size: 448 KiB (458752 bytes)
917
242
+ disk size: unavailable
918
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
243
919
}
244
=== Converted image info ===
920
g_assert_cmpint(result, ==, expected);
245
image: TEST_IMG
921
922
- block_job_txn_unref(txn);
923
+ job_txn_unref(txn);
924
}
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
--
246
--
977
2.13.6
247
2.38.1
978
979
diff view generated by jsdifflib
1
Since we introduced an explicit status to block job, BlockJob.completed
1
From: Hanna Reitz <hreitz@redhat.com>
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
2
3
Currently, when querying a qcow2 image, qemu-img info reports something
4
like this:
5
6
image: test.qcow2
7
file format: qcow2
8
virtual size: 64 MiB (67108864 bytes)
9
disk size: 196 KiB
10
cluster_size: 65536
11
Format specific information:
12
compat: 1.1
13
compression type: zlib
14
lazy refcounts: false
15
refcount bits: 16
16
corrupt: false
17
extended l2: false
18
Child node '/file':
19
image: test.qcow2
20
file format: file
21
virtual size: 192 KiB (197120 bytes)
22
disk size: 196 KiB
23
Format specific information:
24
extent size hint: 1048576
25
26
Notably, the way the keys are named is specific for image files: The
27
filename is shown under "image", the BDS driver under "file format", and
28
the BDS length under "virtual size". This does not make much sense for
29
nodes that are not actually supposed to be guest images, like the /file
30
child node shown above.
31
32
Give bdrv_node_info_dump() a @protocol parameter that gives a hint that
33
the respective node is probably just used for data storage and does not
34
necessarily present the data for a VM guest disk. This renames the keys
35
so that with this patch, the output becomes:
36
37
image: test.qcow2
38
[...]
39
Child node '/file':
40
filename: test.qcow2
41
protocol type: file
42
file length: 192 KiB (197120 bytes)
43
disk size: 196 KiB
44
Format specific information:
45
extent size hint: 1048576
46
47
(Perhaps we should also rename "Format specific information", but I
48
could not come up with anything better that will not become problematic
49
if we guess wrong with the protocol "heuristic".)
50
51
This change affects iotest 302, which has protocol node information in
52
its reference output.
53
54
Signed-off-by: Hanna Reitz <hreitz@redhat.com>
55
Message-Id: <20220620162704.80987-13-hreitz@redhat.com>
56
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
57
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
---
58
---
10
include/block/blockjob.h | 3 ---
59
include/block/qapi.h | 2 +-
11
include/qemu/job.h | 3 +++
60
block/monitor/block-hmp-cmds.c | 2 +-
12
blockjob.c | 16 +++++++---------
61
block/qapi.c | 39 ++++++++++++++++++++++++++++------
13
job.c | 22 ++++++++++++++++++++++
62
qemu-img.c | 3 ++-
14
qemu-img.c | 4 ++--
63
tests/qemu-iotests/302.out | 6 +++---
15
5 files changed, 34 insertions(+), 14 deletions(-)
64
5 files changed, 39 insertions(+), 13 deletions(-)
16
65
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
66
diff --git a/include/block/qapi.h b/include/block/qapi.h
18
index XXXXXXX..XXXXXXX 100644
67
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
68
--- a/include/block/qapi.h
20
+++ b/include/block/blockjob.h
69
+++ b/include/block/qapi.h
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
70
@@ -XXX,XX +XXX,XX @@ void bdrv_snapshot_dump(QEMUSnapshotInfo *sn);
22
/** The opaque value that is passed to the completion function. */
71
void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
23
void *opaque;
72
const char *prefix,
24
73
int indentation);
25
- /** True when job has reported completion by calling block_job_completed. */
74
-void bdrv_node_info_dump(BlockNodeInfo *info, int indentation);
26
- bool completed;
75
+void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol);
27
-
76
#endif
28
/** ret code passed to block_job_completed. */
77
diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c
29
int ret;
30
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
32
index XXXXXXX..XXXXXXX 100644
78
index XXXXXXX..XXXXXXX 100644
33
--- a/include/qemu/job.h
79
--- a/block/monitor/block-hmp-cmds.c
34
+++ b/include/qemu/job.h
80
+++ b/block/monitor/block-hmp-cmds.c
35
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
81
@@ -XXX,XX +XXX,XX @@ static void print_block_info(Monitor *mon, BlockInfo *info,
36
/** Returns whether the job is scheduled for cancellation. */
82
monitor_printf(mon, "\nImages:\n");
37
bool job_is_cancelled(Job *job);
83
image_info = inserted->image;
38
84
while (1) {
39
+/** Returns whether the job is in a completed state. */
85
- bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0);
40
+bool job_is_completed(Job *job);
86
+ bdrv_node_info_dump(qapi_ImageInfo_base(image_info), 0, false);
87
if (image_info->backing_image) {
88
image_info = image_info->backing_image;
89
} else {
90
diff --git a/block/qapi.c b/block/qapi.c
91
index XXXXXXX..XXXXXXX 100644
92
--- a/block/qapi.c
93
+++ b/block/qapi.c
94
@@ -XXX,XX +XXX,XX @@ void bdrv_image_info_specific_dump(ImageInfoSpecific *info_spec,
95
visit_free(v);
96
}
97
98
-void bdrv_node_info_dump(BlockNodeInfo *info, int indentation)
99
+/**
100
+ * Print the given @info object in human-readable form. Every field is indented
101
+ * using the given @indentation (four spaces per indentation level).
102
+ *
103
+ * When using this to print a whole block graph, @protocol can be set to true to
104
+ * signify that the given information is associated with a protocol node, i.e.
105
+ * just data storage for an image, such that the data it presents is not really
106
+ * a full VM disk. If so, several fields change name: For example, "virtual
107
+ * size" is printed as "file length".
108
+ * (Consider a qcow2 image, which is represented by a qcow2 node and a file
109
+ * node. Printing a "virtual size" for the file node does not make sense,
110
+ * because without the qcow2 node, it is not really a guest disk, so it does not
111
+ * have a "virtual size". Therefore, we call it "file length" instead.)
112
+ *
113
+ * @protocol is ignored when @indentation is 0, because we take that to mean
114
+ * that the associated node is the root node in the queried block graph, and
115
+ * thus is always to be interpreted as a standalone guest disk.
116
+ */
117
+void bdrv_node_info_dump(BlockNodeInfo *info, int indentation, bool protocol)
118
{
119
char *size_buf, *dsize_buf;
120
g_autofree char *ind_s = g_strdup_printf("%*s", indentation * 4, "");
121
122
+ if (indentation == 0) {
123
+ /* Top level, consider this a normal image */
124
+ protocol = false;
125
+ }
41
+
126
+
42
/**
127
if (!info->has_actual_size) {
43
* Request @job to pause at the next pause point. Must be paired with
128
dsize_buf = g_strdup("unavailable");
44
* job_resume(). If the job is supposed to be resumed by user action, call
129
} else {
45
diff --git a/blockjob.c b/blockjob.c
130
dsize_buf = size_to_str(info->actual_size);
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
}
131
}
57
132
size_buf = size_to_str(info->virtual_size);
58
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
133
- qemu_printf("%simage: %s\n"
59
static void block_job_decommission(BlockJob *job)
134
- "%sfile format: %s\n"
60
{
135
- "%svirtual size: %s (%" PRId64 " bytes)\n"
61
assert(job);
136
+ qemu_printf("%s%s: %s\n"
62
- job->completed = true;
137
+ "%s%s: %s\n"
63
job->job.busy = false;
138
+ "%s%s: %s (%" PRId64 " bytes)\n"
64
job->job.paused = false;
139
"%sdisk size: %s\n",
65
job->job.deferred_to_main_loop = true;
140
- ind_s, info->filename,
66
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
141
- ind_s, info->format,
67
142
- ind_s, size_buf, info->virtual_size,
68
static int block_job_finalize_single(BlockJob *job)
143
+ ind_s, protocol ? "filename" : "image", info->filename,
69
{
144
+ ind_s, protocol ? "protocol type" : "file format",
70
- assert(job->completed);
145
+ info->format,
71
+ assert(job_is_completed(&job->job));
146
+ ind_s, protocol ? "file length" : "virtual size",
72
147
+ size_buf, info->virtual_size,
73
/* Ensure abort is called for late-transactional failures */
148
ind_s, dsize_buf);
74
block_job_update_rc(job);
149
g_free(size_buf);
75
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
150
g_free(dsize_buf);
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
diff --git a/qemu-img.c b/qemu-img.c
151
index XXXXXXX..XXXXXXX 100644
152
index XXXXXXX..XXXXXXX 100644
152
--- a/qemu-img.c
153
--- a/qemu-img.c
153
+++ b/qemu-img.c
154
+++ b/qemu-img.c
154
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
155
@@ -XXX,XX +XXX,XX @@ static void dump_human_image_info(BlockGraphInfo *info, int indentation,
155
aio_poll(aio_context, true);
156
{
156
qemu_progress_print(job->len ?
157
BlockChildInfoList *children_list;
157
((float)job->offset / job->len * 100.f) : 0.0f, 0);
158
158
- } while (!job->ready && !job->completed);
159
- bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation);
159
+ } while (!job->ready && !job_is_completed(&job->job));
160
+ bdrv_node_info_dump(qapi_BlockGraphInfo_base(info), indentation,
160
161
+ info->children == NULL);
161
- if (!job->completed) {
162
162
+ if (!job_is_completed(&job->job)) {
163
for (children_list = info->children; children_list;
163
ret = block_job_complete_sync(job, errp);
164
children_list = children_list->next)
164
} else {
165
diff --git a/tests/qemu-iotests/302.out b/tests/qemu-iotests/302.out
165
ret = job->ret;
166
index XXXXXXX..XXXXXXX 100644
167
--- a/tests/qemu-iotests/302.out
168
+++ b/tests/qemu-iotests/302.out
169
@@ -XXX,XX +XXX,XX @@ file format: raw
170
virtual size: 448 KiB (458752 bytes)
171
disk size: unavailable
172
Child node '/file':
173
- image: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
174
- file format: nbd
175
- virtual size: 448 KiB (458752 bytes)
176
+ filename: nbd+unix:///exp?socket=SOCK_DIR/PID-nbd-sock
177
+ protocol type: nbd
178
+ file length: 448 KiB (458752 bytes)
179
disk size: unavailable
180
181
=== Converted image info ===
166
--
182
--
167
2.13.6
183
2.38.1
168
169
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