1
The following changes since commit 4f50c1673a89b07f376ce5c42d22d79a79cd466d:
1
The following changes since commit b2f7a038bb4c4fc5ce6b8486e8513dfd97665e2a:
2
2
3
Merge remote-tracking branch 'remotes/ehabkost/tags/x86-next-pull-request' into staging (2018-05-22 09:43:58 +0100)
3
Merge remote-tracking branch 'remotes/rth/tags/pull-softfloat-20181104' into staging (2018-11-05 10:32:49 +0000)
4
4
5
are available in the git repository at:
5
are available in the Git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to bdebdc712b06ba82e103d617c335830682cde242:
9
for you to fetch changes up to 1240ac558d348f6c7a5752b1a57c1da58e4efe3e:
10
10
11
qemu-iotests: Test job-* with block jobs (2018-05-23 14:30:52 +0200)
11
include: Add a comment to explain the origin of sizes' lookup table (2018-11-05 15:29:59 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches:
15
15
16
- Generic background jobs
16
- auto-read-only option to fix commit job when used with -blockdev
17
- qemu-iotests fixes for NFS and the 'migration' group
17
- Fix help text related qemu-iotests failure (by improving the help text
18
- sheepdog: Minor code simplification
18
and updating the reference output)
19
- quorum: Add missing checks when adding/removing child nodes
20
- Don't take address of fields in packed structs
21
- vvfat: Fix crash when reporting error about too many files in directory
19
22
20
----------------------------------------------------------------
23
----------------------------------------------------------------
21
Kevin Wolf (45):
24
Alberto Garcia (7):
22
qemu-iotests: Fix paths for NFS
25
block: replace "discard" literal with BDRV_OPT_DISCARD macro
23
qemu-iotests: Filter NFS paths
26
qcow2: Get the request alignment for encrypted images from QCryptoBlock
24
qemu-iotests: 086 doesn't work with NFS
27
quorum: Remove quorum_err()
25
qemu-iotests: Add more tests to "migration" group
28
quorum: Return an error if the blkverify mode has invalid settings
26
qemu-iotests: Remove MIG_SOCKET from non-migration tests
29
iotest: Test the blkverify mode of the Quorum driver
27
blockjob: Update block-job-pause/resume documentation
30
quorum: Forbid adding children in blkverify mode
28
blockjob: Improve BlockJobInfo.offset/len documentation
31
iotest: Test x-blockdev-change on a Quorum
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
32
68
Peter Maydell (1):
33
Cleber Rosa (1):
69
sheepdog: Remove unnecessary NULL check in sd_prealloc()
34
iotests: make 083 specific to raw
70
35
71
qapi/block-core.json | 116 +---
36
Daniel P. Berrangé (1):
72
qapi/job.json | 253 +++++++++
37
crypto: initialize sector size even when opening with no IO flag
73
qapi/qapi-schema.json | 1 +
74
include/block/block_int.h | 2 +-
75
include/block/blockjob.h | 324 +----------
76
include/block/blockjob_int.h | 176 +-----
77
include/qemu/job.h | 562 ++++++++++++++++++++
78
block.c | 2 +-
79
block/backup.c | 59 +-
80
block/commit.c | 44 +-
81
block/mirror.c | 113 ++--
82
block/replication.c | 10 +-
83
block/sheepdog.c | 4 +-
84
block/stream.c | 39 +-
85
blockdev.c | 68 +--
86
blockjob.c | 1094 ++++++--------------------------------
87
job-qmp.c | 188 +++++++
88
job.c | 1000 ++++++++++++++++++++++++++++++++++
89
qemu-img.c | 22 +-
90
qemu-nbd.c | 8 +-
91
tests/test-bdrv-drain.c | 63 ++-
92
tests/test-blockjob-txn.c | 74 +--
93
tests/test-blockjob.c | 141 ++---
94
vl.c | 1 +
95
MAINTAINERS | 4 +
96
Makefile | 9 +
97
Makefile.objs | 7 +-
98
block/trace-events | 5 -
99
tests/qemu-iotests/030 | 17 +-
100
tests/qemu-iotests/040 | 2 +
101
tests/qemu-iotests/041 | 23 +-
102
tests/qemu-iotests/086 | 2 +-
103
tests/qemu-iotests/094.out | 7 +
104
tests/qemu-iotests/095 | 2 +-
105
tests/qemu-iotests/095.out | 6 +
106
tests/qemu-iotests/109 | 2 +-
107
tests/qemu-iotests/109.out | 178 ++++++-
108
tests/qemu-iotests/124 | 8 +
109
tests/qemu-iotests/126.out | 2 +-
110
tests/qemu-iotests/127.out | 7 +
111
tests/qemu-iotests/141 | 13 +-
112
tests/qemu-iotests/141.out | 29 +
113
tests/qemu-iotests/144 | 2 +-
114
tests/qemu-iotests/144.out | 7 +
115
tests/qemu-iotests/155 | 2 +-
116
tests/qemu-iotests/156 | 2 +-
117
tests/qemu-iotests/156.out | 7 +
118
tests/qemu-iotests/185 | 14 +-
119
tests/qemu-iotests/185.out | 10 +
120
tests/qemu-iotests/191 | 6 +-
121
tests/qemu-iotests/191.out | 132 +++++
122
tests/qemu-iotests/219 | 209 ++++++++
123
tests/qemu-iotests/219.out | 327 ++++++++++++
124
tests/qemu-iotests/common.filter | 6 +-
125
tests/qemu-iotests/common.rc | 12 +-
126
tests/qemu-iotests/group | 11 +-
127
tests/qemu-iotests/iotests.py | 50 +-
128
trace-events | 14 +
129
58 files changed, 3601 insertions(+), 1897 deletions(-)
130
create mode 100644 qapi/job.json
131
create mode 100644 include/qemu/job.h
132
create mode 100644 job-qmp.c
133
create mode 100644 job.c
134
create mode 100755 tests/qemu-iotests/219
135
create mode 100644 tests/qemu-iotests/219.out
136
38
39
Kevin Wolf (12):
40
vpc: Don't leak opts in vpc_open()
41
block: Update flags in bdrv_set_read_only()
42
block: Add auto-read-only option
43
rbd: Close image in qemu_rbd_open() error path
44
block: Require auto-read-only for existing fallbacks
45
nbd: Support auto-read-only option
46
file-posix: Support auto-read-only option
47
curl: Support auto-read-only option
48
gluster: Support auto-read-only option
49
iscsi: Support auto-read-only option
50
block: Make auto-read-only=on default for -drive
51
qemu-iotests: Test auto-read-only with -drive and -blockdev
52
53
Leonid Bloch (2):
54
vdi: Use a literal number of bytes for DEFAULT_CLUSTER_SIZE
55
include: Add a comment to explain the origin of sizes' lookup table
56
57
Li Qiang (1):
58
block: change some function return type to bool
59
60
Max Reitz (5):
61
option: Make option help nicer to read
62
chardev: Indent list of chardevs
63
qdev-monitor: Make device options help nicer
64
object: Make option help nicer to read
65
fw_cfg: Drop newline in @file description
66
67
Peter Maydell (5):
68
block/qcow2: Don't take address of fields in packed structs
69
block/qcow: Don't take address of fields in packed structs
70
block/qcow2-bitmap: Don't take address of fields in packed structs
71
block/vhdx: Don't take address of fields in packed structs
72
block/vdi: Don't take address of fields in packed structs
73
74
Stefan Weil (1):
75
qemu-io-cmds: Fix two format strings
76
77
Thomas Huth (1):
78
block/vvfat: Fix crash when reporting error about too many files in directory
79
80
qapi/block-core.json | 7 +
81
block/vhdx.h | 12 +-
82
include/block/block.h | 5 +-
83
include/qemu/option.h | 2 +-
84
include/qemu/units.h | 18 +
85
include/sysemu/block-backend.h | 6 +-
86
block.c | 60 ++-
87
block/block-backend.c | 8 +-
88
block/bochs.c | 17 +-
89
block/cloop.c | 16 +-
90
block/curl.c | 8 +-
91
block/dmg.c | 16 +-
92
block/file-posix.c | 19 +-
93
block/gluster.c | 12 +-
94
block/iscsi.c | 8 +-
95
block/nbd-client.c | 10 +-
96
block/qcow.c | 18 +-
97
block/qcow2-bitmap.c | 24 +-
98
block/qcow2.c | 66 +--
99
block/quorum.c | 45 +-
100
block/rbd.c | 14 +-
101
block/vdi.c | 68 +--
102
block/vhdx-endian.c | 118 ++---
103
block/vhdx-log.c | 4 +-
104
block/vhdx.c | 18 +-
105
block/vpc.c | 2 +
106
block/vvfat.c | 15 +-
107
blockdev.c | 3 +-
108
chardev/char.c | 2 +-
109
crypto/block-qcow.c | 2 +
110
qdev-monitor.c | 13 +-
111
qemu-img.c | 4 +-
112
qemu-io-cmds.c | 4 +-
113
util/qemu-option.c | 32 +-
114
vl.c | 15 +-
115
tests/qemu-iotests/081 | 116 +++++
116
tests/qemu-iotests/081.out | 70 +++
117
tests/qemu-iotests/082.out | 956 ++++++++++++++++++++---------------------
118
tests/qemu-iotests/083 | 2 +-
119
tests/qemu-iotests/232 | 147 +++++++
120
tests/qemu-iotests/232.out | 59 +++
121
tests/qemu-iotests/group | 1 +
122
42 files changed, 1266 insertions(+), 776 deletions(-)
123
create mode 100755 tests/qemu-iotests/232
124
create mode 100644 tests/qemu-iotests/232.out
125
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
When using the vvfat driver with a directory that contains too many files,
4
QEMU currently crashes. This can be triggered like this for example:
5
6
mkdir /tmp/vvfattest
7
cd /tmp/vvfattest
8
for ((x=0;x<=513;x++)); do mkdir $x; done
9
qemu-system-x86_64 -drive \
10
file.driver=vvfat,file.dir=.,read-only=on,media=cdrom
11
12
Seems like read_directory() is changing the mapping->path variable. Make
13
sure we use the right pointer instead.
14
15
Signed-off-by: Thomas Huth <thuth@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
17
---
8
tests/qemu-iotests/041 | 6 +++---
18
block/vvfat.c | 4 ++--
9
tests/qemu-iotests/155 | 2 +-
19
1 file changed, 2 insertions(+), 2 deletions(-)
10
tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++++++---------------------
11
3 files changed, 27 insertions(+), 26 deletions(-)
12
20
13
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
21
diff --git a/block/vvfat.c b/block/vvfat.c
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/041
16
+++ b/tests/qemu-iotests/041
17
@@ -XXX,XX +XXX,XX @@ class TestOrphanedSource(iotests.QMPTestCase):
18
'read-only': 'on' }
19
20
self.vm = iotests.VM()
21
- self.vm.add_blockdev(self.qmp_to_opts(blk0))
22
- self.vm.add_blockdev(self.qmp_to_opts(blk1))
23
- self.vm.add_blockdev(self.qmp_to_opts(blk2))
24
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk0))
25
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk1))
26
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blk2))
27
self.vm.launch()
28
29
def tearDown(self):
30
diff --git a/tests/qemu-iotests/155 b/tests/qemu-iotests/155
31
index XXXXXXX..XXXXXXX 100755
32
--- a/tests/qemu-iotests/155
33
+++ b/tests/qemu-iotests/155
34
@@ -XXX,XX +XXX,XX @@ class BaseClass(iotests.QMPTestCase):
35
'driver': iotests.imgfmt,
36
'file': {'driver': 'file',
37
'filename': source_img}}
38
- self.vm.add_blockdev(self.qmp_to_opts(blockdev))
39
+ self.vm.add_blockdev(self.vm.qmp_to_opts(blockdev))
40
self.vm.add_device('virtio-blk,id=qdev0,drive=source')
41
self.vm.launch()
42
43
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
44
index XXXXXXX..XXXXXXX 100644
22
index XXXXXXX..XXXXXXX 100644
45
--- a/tests/qemu-iotests/iotests.py
23
--- a/block/vvfat.c
46
+++ b/tests/qemu-iotests/iotests.py
24
+++ b/block/vvfat.c
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
25
@@ -XXX,XX +XXX,XX @@ static int init_directories(BDRVVVFATState* s,
48
return self.qmp('human-monitor-command',
26
mapping = array_get(&(s->mapping), i);
49
command_line='qemu-io %s "%s"' % (drive, cmd))
27
50
28
if (mapping->mode & MODE_DIRECTORY) {
51
+ def flatten_qmp_object(self, obj, output=None, basestr=''):
29
+ char *path = mapping->path;
52
+ if output is None:
30
mapping->begin = cluster;
53
+ output = dict()
31
if(read_directory(s, i)) {
54
+ if isinstance(obj, list):
32
- error_setg(errp, "Could not read directory %s",
55
+ for i in range(len(obj)):
33
- mapping->path);
56
+ self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
34
+ error_setg(errp, "Could not read directory %s", path);
57
+ elif isinstance(obj, dict):
35
return -1;
58
+ for key in obj:
36
}
59
+ self.flatten_qmp_object(obj[key], output, basestr + key + '.')
37
mapping = array_get(&(s->mapping), i);
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
--
38
--
114
2.13.6
39
2.19.1
115
40
116
41
diff view generated by jsdifflib
1
Go through the Job layer in order to send QMP events. For the moment,
1
From: Alberto Garcia <berto@igalia.com>
2
these functions only call a notifier in the BlockJob layer that sends
3
the existing commands.
4
2
5
This uses notifiers rather than JobDriver callbacks because internal
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
6
users of jobs won't receive QMP events, but might still be interested
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
in getting notified for the events.
5
---
6
block.c | 6 +++---
7
1 file changed, 3 insertions(+), 3 deletions(-)
8
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
diff --git a/block.c b/block.c
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
---
12
include/block/blockjob.h | 9 +++++++++
13
include/qemu/job.h | 18 ++++++++++++++++++
14
blockjob.c | 41 +++++++++++++++++++++++++++--------------
15
job.c | 19 +++++++++++++++++++
16
4 files changed, 73 insertions(+), 14 deletions(-)
17
18
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
19
index XXXXXXX..XXXXXXX 100644
10
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/blockjob.h
11
--- a/block.c
21
+++ b/include/block/blockjob.h
12
+++ b/block.c
22
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
13
@@ -XXX,XX +XXX,XX @@ QemuOptsList bdrv_runtime_opts = {
23
/** Block other operations when block job is running */
14
.help = "try to optimize zero writes (off, on, unmap)",
24
Error *blocker;
15
},
25
16
{
26
+ /** Called when a cancelled job is finalised. */
17
- .name = "discard",
27
+ Notifier finalize_cancelled_notifier;
18
+ .name = BDRV_OPT_DISCARD,
28
+
19
.type = QEMU_OPT_STRING,
29
+ /** Called when a successfully completed job is finalised. */
20
.help = "discard operation (ignore/off, unmap/on)",
30
+ Notifier finalize_completed_notifier;
21
},
31
+
22
@@ -XXX,XX +XXX,XX @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file,
32
+ /** Called when the job transitions to PENDING */
33
+ Notifier pending_notifier;
34
+
35
/** BlockDriverStates that are involved in this block job */
36
GSList *nodes;
37
38
diff --git a/include/qemu/job.h b/include/qemu/job.h
39
index XXXXXXX..XXXXXXX 100644
40
--- a/include/qemu/job.h
41
+++ b/include/qemu/job.h
42
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
43
/** True if this job should automatically dismiss itself */
44
bool auto_dismiss;
45
46
+ /** Notifiers called when a cancelled job is finalised */
47
+ NotifierList on_finalize_cancelled;
48
+
49
+ /** Notifiers called when a successfully completed job is finalised */
50
+ NotifierList on_finalize_completed;
51
+
52
+ /** Notifiers called when the job transitions to PENDING */
53
+ NotifierList on_pending;
54
+
55
/** Element of the list of jobs */
56
QLIST_ENTRY(Job) job_list;
57
} Job;
58
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
59
*/
60
void job_unref(Job *job);
61
62
+/** To be called when a cancelled job is finalised. */
63
+void job_event_cancelled(Job *job);
64
+
65
+/** To be called when a successfully completed job is finalised. */
66
+void job_event_completed(Job *job);
67
+
68
+/** To be called when the job transitions to PENDING */
69
+void job_event_pending(Job *job);
70
+
71
/**
72
* Conditionally enter the job coroutine if the job is ready to run, not
73
* already busy and fn() returns true. fn() is called while under the job_lock
74
diff --git a/blockjob.c b/blockjob.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/blockjob.c
77
+++ b/blockjob.c
78
@@ -XXX,XX +XXX,XX @@
79
#include "qemu/coroutine.h"
80
#include "qemu/timer.h"
81
82
-static void block_job_event_cancelled(BlockJob *job);
83
-static void block_job_event_completed(BlockJob *job, const char *msg);
84
-static void block_job_event_pending(BlockJob *job);
85
-
86
/* Transactional group of block jobs */
87
struct BlockJobTxn {
88
89
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
90
/* Emit events only if we actually started */
91
if (job_started(&job->job)) {
92
if (job_is_cancelled(&job->job)) {
93
- block_job_event_cancelled(job);
94
+ job_event_cancelled(&job->job);
95
} else {
96
- const char *msg = NULL;
97
- if (job->ret < 0) {
98
- msg = strerror(-job->ret);
99
- }
100
- block_job_event_completed(job, msg);
101
+ job_event_completed(&job->job);
102
}
23
}
103
}
24
}
104
25
105
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
26
- discard = qemu_opt_get(opts, "discard");
106
{
27
+ discard = qemu_opt_get(opts, BDRV_OPT_DISCARD);
107
job_state_transition(&job->job, JOB_STATUS_PENDING);
28
if (discard != NULL) {
108
if (!job->job.auto_finalize) {
29
if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) {
109
- block_job_event_pending(job);
30
error_setg(errp, "Invalid discard option");
110
+ job_event_pending(&job->job);
31
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
111
}
32
112
return 0;
33
update_flags_from_options(&reopen_state->flags, opts);
113
}
34
114
@@ -XXX,XX +XXX,XX @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
35
- discard = qemu_opt_get_del(opts, "discard");
115
}
36
+ discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD);
116
}
37
if (discard != NULL) {
117
38
if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) {
118
-static void block_job_event_cancelled(BlockJob *job)
39
error_setg(errp, "Invalid discard option");
119
+static void block_job_event_cancelled(Notifier *n, void *opaque)
120
{
121
+ BlockJob *job = opaque;
122
+
123
if (block_job_is_internal(job)) {
124
return;
125
}
126
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
127
&error_abort);
128
}
129
130
-static void block_job_event_completed(BlockJob *job, const char *msg)
131
+static void block_job_event_completed(Notifier *n, void *opaque)
132
{
133
+ BlockJob *job = opaque;
134
+ const char *msg = NULL;
135
+
136
if (block_job_is_internal(job)) {
137
return;
138
}
139
140
+ if (job->ret < 0) {
141
+ msg = strerror(-job->ret);
142
+ }
143
+
144
qapi_event_send_block_job_completed(job_type(&job->job),
145
job->job.id,
146
job->len,
147
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
148
&error_abort);
149
}
150
151
-static void block_job_event_pending(BlockJob *job)
152
+static void block_job_event_pending(Notifier *n, void *opaque)
153
{
154
+ BlockJob *job = opaque;
155
+
156
if (block_job_is_internal(job)) {
157
return;
158
}
159
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
160
job->cb = cb;
161
job->opaque = opaque;
162
163
+ job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
164
+ job->finalize_completed_notifier.notify = block_job_event_completed;
165
+ job->pending_notifier.notify = block_job_event_pending;
166
+
167
+ notifier_list_add(&job->job.on_finalize_cancelled,
168
+ &job->finalize_cancelled_notifier);
169
+ notifier_list_add(&job->job.on_finalize_completed,
170
+ &job->finalize_completed_notifier);
171
+ notifier_list_add(&job->job.on_pending, &job->pending_notifier);
172
+
173
error_setg(&job->blocker, "block device is in use by block job: %s",
174
job_type_str(&job->job));
175
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
176
diff --git a/job.c b/job.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/job.c
179
+++ b/job.c
180
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
181
job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
182
job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
183
184
+ notifier_list_init(&job->on_finalize_cancelled);
185
+ notifier_list_init(&job->on_finalize_completed);
186
+ notifier_list_init(&job->on_pending);
187
+
188
job_state_transition(job, JOB_STATUS_CREATED);
189
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
190
QEMU_CLOCK_REALTIME, SCALE_NS,
191
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
192
}
193
}
194
195
+void job_event_cancelled(Job *job)
196
+{
197
+ notifier_list_notify(&job->on_finalize_cancelled, job);
198
+}
199
+
200
+void job_event_completed(Job *job)
201
+{
202
+ notifier_list_notify(&job->on_finalize_completed, job);
203
+}
204
+
205
+void job_event_pending(Job *job)
206
+{
207
+ notifier_list_notify(&job->on_pending, job);
208
+}
209
+
210
void job_enter_cond(Job *job, bool(*fn)(Job *job))
211
{
212
if (!job_started(job)) {
213
--
40
--
214
2.13.6
41
2.19.1
215
42
216
43
diff view generated by jsdifflib
1
BlockJob has fields .offset and .len, which are actually misnomers today
1
From: Stefan Weil <sw@weilnetz.de>
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
Use %zu instead of %zd for unsigned numbers.
6
and .progress_total to describe their function better.
7
4
5
This fixes two error messages from the LSTM static code analyzer:
6
7
This argument should be of type 'ssize_t' but is of type 'unsigned long'
8
9
Signed-off-by: Stefan Weil <sw@weilnetz.de>
10
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
12
---
11
include/block/blockjob.h | 25 -------------------------
13
qemu-io-cmds.c | 4 ++--
12
include/qemu/job.h | 28 ++++++++++++++++++++++++++++
14
1 file changed, 2 insertions(+), 2 deletions(-)
13
block/backup.c | 8 ++++----
14
block/commit.c | 4 ++--
15
block/mirror.c | 4 ++--
16
block/stream.c | 4 ++--
17
blockjob.c | 26 ++++++++------------------
18
job.c | 10 ++++++++++
19
qemu-img.c | 8 ++++++--
20
9 files changed, 62 insertions(+), 55 deletions(-)
21
15
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
16
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
23
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
18
--- a/qemu-io-cmds.c
25
+++ b/include/block/blockjob.h
19
+++ b/qemu-io-cmds.c
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
20
@@ -XXX,XX +XXX,XX @@ static int readv_f(BlockBackend *blk, int argc, char **argv)
27
/** Status that is published by the query-block-jobs QMP API */
21
memset(cmp_buf, pattern, qiov.size);
28
BlockDeviceIoStatus iostatus;
22
if (memcmp(buf, cmp_buf, qiov.size)) {
29
23
printf("Pattern verification failed at offset %"
30
- /** Offset that is published by the query-block-jobs QMP API */
24
- PRId64 ", %zd bytes\n", offset, qiov.size);
31
- int64_t offset;
25
+ PRId64 ", %zu bytes\n", offset, qiov.size);
32
-
26
ret = -EINVAL;
33
- /** Length that is published by the query-block-jobs QMP API */
27
}
34
- int64_t len;
28
g_free(cmp_buf);
35
-
29
@@ -XXX,XX +XXX,XX @@ static void aio_read_done(void *opaque, int ret)
36
/** Speed that was set with @block_job_set_speed. */
30
memset(cmp_buf, ctx->pattern, ctx->qiov.size);
37
int64_t speed;
31
if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) {
38
32
printf("Pattern verification failed at offset %"
39
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
33
- PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size);
40
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
34
+ PRId64 ", %zu bytes\n", ctx->offset, ctx->qiov.size);
41
35
}
42
/**
36
g_free(cmp_buf);
43
- * block_job_progress_update:
44
- * @job: The job that has made progress
45
- * @done: How much progress the job made
46
- *
47
- * Updates the progress counter of the job.
48
- */
49
-void block_job_progress_update(BlockJob *job, uint64_t done);
50
-
51
-/**
52
- * block_job_progress_set_remaining:
53
- * @job: The job whose expected progress end value is set
54
- * @remaining: Expected end value of the progress counter of the job
55
- *
56
- * Sets the expected end value of the progress counter of a job so that a
57
- * completion percentage can be calculated when the progress is updated.
58
- */
59
-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
60
-
61
-/**
62
* block_job_query:
63
* @job: The job to get information about.
64
*
65
diff --git a/include/qemu/job.h b/include/qemu/job.h
66
index XXXXXXX..XXXXXXX 100644
67
--- a/include/qemu/job.h
68
+++ b/include/qemu/job.h
69
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
70
/** True if this job should automatically dismiss itself */
71
bool auto_dismiss;
72
73
+ /**
74
+ * Current progress. The unit is arbitrary as long as the ratio between
75
+ * progress_current and progress_total represents the estimated percentage
76
+ * of work already done.
77
+ */
78
+ int64_t progress_current;
79
+
80
+ /** Estimated progress_current value at the completion of the job */
81
+ int64_t progress_total;
82
+
83
/** ret code passed to job_completed. */
84
int ret;
85
86
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
87
*/
88
void job_unref(Job *job);
89
90
+/**
91
+ * @job: The job that has made progress
92
+ * @done: How much progress the job made since the last call
93
+ *
94
+ * Updates the progress counter of the job.
95
+ */
96
+void job_progress_update(Job *job, uint64_t done);
97
+
98
+/**
99
+ * @job: The job whose expected progress end value is set
100
+ * @remaining: Missing progress (on top of the current progress counter value)
101
+ * until the new expected end value is reached
102
+ *
103
+ * Sets the expected end value of the progress counter of a job so that a
104
+ * completion percentage can be calculated when the progress is updated.
105
+ */
106
+void job_progress_set_remaining(Job *job, uint64_t remaining);
107
+
108
/** To be called when a cancelled job is finalised. */
109
void job_event_cancelled(Job *job);
110
111
diff --git a/block/backup.c b/block/backup.c
112
index XXXXXXX..XXXXXXX 100644
113
--- a/block/backup.c
114
+++ b/block/backup.c
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
116
* offset field is an opaque progress value, it is not a disk offset.
117
*/
118
job->bytes_read += n;
119
- block_job_progress_update(&job->common, n);
120
+ job_progress_update(&job->common.job, n);
121
}
37
}
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
--
38
--
319
2.13.6
39
2.19.1
320
40
321
41
diff view generated by jsdifflib
1
Move the defer_to_main_loop functionality from BlockJob to Job.
1
From: Peter Maydell <peter.maydell@linaro.org>
2
2
3
The code can be simplified because we can use job->aio_context in
3
Taking the address of a field in a packed struct is a bad idea, because
4
job_defer_to_main_loop_bh() now, instead of having to access the
4
it might not be actually aligned enough for that pointer type (and
5
BlockDriverState.
5
thus cause a crash on dereference on some host architectures). Newer
6
versions of clang warn about this. Avoid the bug by not using the
7
"modify in place" byte swapping functions.
6
8
7
Probably taking the data->aio_context lock in addition was already
9
There are a few places where the in-place swap function is
8
unnecessary in the old code because we didn't actually make use of
10
used on something other than a packed struct field; we convert
9
anything protected by the old AioContext except getting the new
11
those anyway, for consistency.
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
12
13
This patch was produced with the following spatch script
14
(and hand-editing to fold a few resulting overlength lines):
15
16
@@
17
expression E;
18
@@
19
-be16_to_cpus(&E);
20
+E = be16_to_cpu(E);
21
@@
22
expression E;
23
@@
24
-be32_to_cpus(&E);
25
+E = be32_to_cpu(E);
26
@@
27
expression E;
28
@@
29
-be64_to_cpus(&E);
30
+E = be64_to_cpu(E);
31
@@
32
expression E;
33
@@
34
-cpu_to_be16s(&E);
35
+E = cpu_to_be16(E);
36
@@
37
expression E;
38
@@
39
-cpu_to_be32s(&E);
40
+E = cpu_to_be32(E);
41
@@
42
expression E;
43
@@
44
-cpu_to_be64s(&E);
45
+E = cpu_to_be64(E);
46
47
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
48
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
49
Tested-by: John Snow <jsnow@redhat.com>
50
Reviewed-by: John Snow <jsnow@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
51
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
---
52
---
18
include/block/blockjob.h | 5 ----
53
block/qcow2.c | 64 +++++++++++++++++++++++++++------------------------
19
include/block/blockjob_int.h | 19 ---------------
54
1 file changed, 34 insertions(+), 30 deletions(-)
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
55
32
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
56
diff --git a/block/qcow2.c b/block/qcow2.c
33
index XXXXXXX..XXXXXXX 100644
57
index XXXXXXX..XXXXXXX 100644
34
--- a/include/block/blockjob.h
58
--- a/block/qcow2.c
35
+++ b/include/block/blockjob.h
59
+++ b/block/qcow2.c
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
60
@@ -XXX,XX +XXX,XX @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
37
*/
61
"pread fail from offset %" PRIu64, offset);
38
bool ready;
62
return 1;
39
63
}
40
- /**
64
- be32_to_cpus(&ext.magic);
41
- * Set to true when the job has deferred work to the main loop.
65
- be32_to_cpus(&ext.len);
42
- */
66
+ ext.magic = be32_to_cpu(ext.magic);
43
- bool deferred_to_main_loop;
67
+ ext.len = be32_to_cpu(ext.len);
44
-
68
offset += sizeof(ext);
45
/** Status that is published by the query-block-jobs QMP API */
69
#ifdef DEBUG_EXT
46
BlockDeviceIoStatus iostatus;
70
printf("ext.magic = 0x%x\n", ext.magic);
47
71
@@ -XXX,XX +XXX,XX @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
48
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
72
"Unable to read CRYPTO header extension");
49
index XXXXXXX..XXXXXXX 100644
73
return ret;
50
--- a/include/block/blockjob_int.h
74
}
51
+++ b/include/block/blockjob_int.h
75
- be64_to_cpus(&s->crypto_header.offset);
52
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job);
76
- be64_to_cpus(&s->crypto_header.length);
53
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
77
+ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset);
54
int is_read, int error);
78
+ s->crypto_header.length = be64_to_cpu(s->crypto_header.length);
55
79
56
-typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
80
if ((s->crypto_header.offset % s->cluster_size) != 0) {
57
-
81
error_setg(errp, "Encryption header offset '%" PRIu64 "' is "
58
-/**
82
@@ -XXX,XX +XXX,XX @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset,
59
- * block_job_defer_to_main_loop:
83
return -EINVAL;
60
- * @job: The job
84
}
61
- * @fn: The function to run in the main loop
85
62
- * @opaque: The opaque value that is passed to @fn
86
- be32_to_cpus(&bitmaps_ext.nb_bitmaps);
63
- *
87
- be64_to_cpus(&bitmaps_ext.bitmap_directory_size);
64
- * This function must be called by the main job coroutine just before it
88
- be64_to_cpus(&bitmaps_ext.bitmap_directory_offset);
65
- * returns. @fn is executed in the main loop with the BlockDriverState
89
+ bitmaps_ext.nb_bitmaps = be32_to_cpu(bitmaps_ext.nb_bitmaps);
66
- * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
90
+ bitmaps_ext.bitmap_directory_size =
67
- * anything that uses bdrv_drain_all() in the main loop.
91
+ be64_to_cpu(bitmaps_ext.bitmap_directory_size);
68
- *
92
+ bitmaps_ext.bitmap_directory_offset =
69
- * The @job AioContext is held while @fn executes.
93
+ be64_to_cpu(bitmaps_ext.bitmap_directory_offset);
70
- */
94
71
-void block_job_defer_to_main_loop(BlockJob *job,
95
if (bitmaps_ext.nb_bitmaps > QCOW2_MAX_BITMAPS) {
72
- BlockJobDeferToMainLoopFn *fn,
96
error_setg(errp,
73
- void *opaque);
97
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
74
-
98
error_setg_errno(errp, -ret, "Could not read qcow2 header");
75
#endif
99
goto fail;
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
}
100
}
229
- block_job_defer_to_main_loop(&s->common, mirror_exit, data);
101
- be32_to_cpus(&header.magic);
230
+ job_defer_to_main_loop(&s->common.job, mirror_exit, data);
102
- be32_to_cpus(&header.version);
231
}
103
- be64_to_cpus(&header.backing_file_offset);
232
104
- be32_to_cpus(&header.backing_file_size);
233
static void mirror_complete(BlockJob *job, Error **errp)
105
- be64_to_cpus(&header.size);
234
diff --git a/block/stream.c b/block/stream.c
106
- be32_to_cpus(&header.cluster_bits);
235
index XXXXXXX..XXXXXXX 100644
107
- be32_to_cpus(&header.crypt_method);
236
--- a/block/stream.c
108
- be64_to_cpus(&header.l1_table_offset);
237
+++ b/block/stream.c
109
- be32_to_cpus(&header.l1_size);
238
@@ -XXX,XX +XXX,XX @@ typedef struct {
110
- be64_to_cpus(&header.refcount_table_offset);
239
int ret;
111
- be32_to_cpus(&header.refcount_table_clusters);
240
} StreamCompleteData;
112
- be64_to_cpus(&header.snapshots_offset);
241
113
- be32_to_cpus(&header.nb_snapshots);
242
-static void stream_complete(BlockJob *job, void *opaque)
114
+ header.magic = be32_to_cpu(header.magic);
243
+static void stream_complete(Job *job, void *opaque)
115
+ header.version = be32_to_cpu(header.version);
244
{
116
+ header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
245
- StreamBlockJob *s = container_of(job, StreamBlockJob, common);
117
+ header.backing_file_size = be32_to_cpu(header.backing_file_size);
246
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
118
+ header.size = be64_to_cpu(header.size);
247
+ BlockJob *bjob = &s->common;
119
+ header.cluster_bits = be32_to_cpu(header.cluster_bits);
248
StreamCompleteData *data = opaque;
120
+ header.crypt_method = be32_to_cpu(header.crypt_method);
249
- BlockDriverState *bs = blk_bs(job->blk);
121
+ header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
250
+ BlockDriverState *bs = blk_bs(bjob->blk);
122
+ header.l1_size = be32_to_cpu(header.l1_size);
251
BlockDriverState *base = s->base;
123
+ header.refcount_table_offset = be64_to_cpu(header.refcount_table_offset);
252
Error *local_err = NULL;
124
+ header.refcount_table_clusters =
253
125
+ be32_to_cpu(header.refcount_table_clusters);
254
- if (!job_is_cancelled(&s->common.job) && bs->backing &&
126
+ header.snapshots_offset = be64_to_cpu(header.snapshots_offset);
255
- data->ret == 0) {
127
+ header.nb_snapshots = be32_to_cpu(header.nb_snapshots);
256
+ if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
128
257
const char *base_id = NULL, *base_fmt = NULL;
129
if (header.magic != QCOW_MAGIC) {
258
if (base) {
130
error_setg(errp, "Image is not in qcow2 format");
259
base_id = s->backing_file_str;
131
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
260
@@ -XXX,XX +XXX,XX @@ out:
132
header.refcount_order = 4;
261
/* Reopen the image back in read-only mode if necessary */
133
header.header_length = 72;
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 {
134
} else {
308
block_job_enter(job);
135
- be64_to_cpus(&header.incompatible_features);
309
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
136
- be64_to_cpus(&header.compatible_features);
310
if (!block_job_started(job)) {
137
- be64_to_cpus(&header.autoclear_features);
311
return;
138
- be32_to_cpus(&header.refcount_order);
312
}
139
- be32_to_cpus(&header.header_length);
313
- if (job->deferred_to_main_loop) {
140
+ header.incompatible_features =
314
+ if (job->job.deferred_to_main_loop) {
141
+ be64_to_cpu(header.incompatible_features);
315
return;
142
+ header.compatible_features = be64_to_cpu(header.compatible_features);
316
}
143
+ header.autoclear_features = be64_to_cpu(header.autoclear_features);
317
144
+ header.refcount_order = be32_to_cpu(header.refcount_order);
318
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
145
+ header.header_length = be32_to_cpu(header.header_length);
319
return;
146
320
}
147
if (header.header_length < 104) {
321
148
error_setg(errp, "qcow2 header too short");
322
- assert(!job->deferred_to_main_loop);
149
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options,
323
+ assert(!job->job.deferred_to_main_loop);
150
goto fail;
324
timer_del(&job->sleep_timer);
151
}
325
job->busy = true;
152
for(i = 0;i < s->l1_size; i++) {
326
block_job_unlock();
153
- be64_to_cpus(&s->l1_table[i]);
327
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
154
+ s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
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
}
155
}
479
}
156
}
480
157
481
- block_job_defer_to_main_loop(job, test_block_job_complete,
158
@@ -XXX,XX +XXX,XX @@ int qcow2_update_header(BlockDriverState *bs)
482
- (void *)(intptr_t)s->rc);
159
483
+ job_defer_to_main_loop(&job->job, test_block_job_complete,
160
/* Full disk encryption header pointer extension */
484
+ (void *)(intptr_t)s->rc);
161
if (s->crypto_header.offset != 0) {
485
}
162
- cpu_to_be64s(&s->crypto_header.offset);
486
163
- cpu_to_be64s(&s->crypto_header.length);
487
typedef struct {
164
+ s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset);
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
165
+ s->crypto_header.length = cpu_to_be64(s->crypto_header.length);
489
index XXXXXXX..XXXXXXX 100644
166
ret = header_ext_add(buf, QCOW2_EXT_MAGIC_CRYPTO_HEADER,
490
--- a/tests/test-blockjob.c
167
&s->crypto_header, sizeof(s->crypto_header),
491
+++ b/tests/test-blockjob.c
168
buflen);
492
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
169
- be64_to_cpus(&s->crypto_header.offset);
493
bool completed;
170
- be64_to_cpus(&s->crypto_header.length);
494
} CancelJob;
171
+ s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset);
495
172
+ s->crypto_header.length = be64_to_cpu(s->crypto_header.length);
496
-static void cancel_job_completed(BlockJob *job, void *opaque)
173
if (ret < 0) {
497
+static void cancel_job_completed(Job *job, void *opaque)
174
goto fail;
498
{
175
}
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
--
176
--
517
2.13.6
177
2.19.1
518
178
519
179
diff view generated by jsdifflib
1
When block jobs need an AioContext, they just take it from their main
1
From: Peter Maydell <peter.maydell@linaro.org>
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
Taking the address of a field in a packed struct is a bad idea, because
4
it might not be actually aligned enough for that pointer type (and
5
thus cause a crash on dereference on some host architectures). Newer
6
versions of clang warn about this. Avoid the bug by not using the
7
"modify in place" byte swapping functions.
8
9
There are a few places where the in-place swap function is
10
used on something other than a packed struct field; we convert
11
those anyway, for consistency.
12
13
This patch was produced with the following spatch script:
14
15
@@
16
expression E;
17
@@
18
-be16_to_cpus(&E);
19
+E = be16_to_cpu(E);
20
@@
21
expression E;
22
@@
23
-be32_to_cpus(&E);
24
+E = be32_to_cpu(E);
25
@@
26
expression E;
27
@@
28
-be64_to_cpus(&E);
29
+E = be64_to_cpu(E);
30
@@
31
expression E;
32
@@
33
-cpu_to_be16s(&E);
34
+E = cpu_to_be16(E);
35
@@
36
expression E;
37
@@
38
-cpu_to_be32s(&E);
39
+E = cpu_to_be32(E);
40
@@
41
expression E;
42
@@
43
-cpu_to_be64s(&E);
44
+E = cpu_to_be64(E);
45
46
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
47
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
48
Tested-by: John Snow <jsnow@redhat.com>
49
Reviewed-by: John Snow <jsnow@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
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
---
51
---
9
include/qemu/job.h | 7 ++++++-
52
block/qcow.c | 18 +++++++++---------
10
blockjob.c | 5 ++++-
53
1 file changed, 9 insertions(+), 9 deletions(-)
11
job.c | 4 +++-
12
3 files changed, 13 insertions(+), 3 deletions(-)
13
54
14
diff --git a/include/qemu/job.h b/include/qemu/job.h
55
diff --git a/block/qcow.c b/block/qcow.c
15
index XXXXXXX..XXXXXXX 100644
56
index XXXXXXX..XXXXXXX 100644
16
--- a/include/qemu/job.h
57
--- a/block/qcow.c
17
+++ b/include/qemu/job.h
58
+++ b/block/qcow.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
59
@@ -XXX,XX +XXX,XX @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
19
/** Current state; See @JobStatus for details. */
60
if (ret < 0) {
20
JobStatus status;
61
goto fail;
21
22
+ /** AioContext to run the job coroutine in */
23
+ AioContext *aio_context;
24
+
25
/**
26
* Set to true if the job should cancel itself. The flag must
27
* always be tested just before toggling the busy flag from false
28
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
29
*
30
* @job_id: The id of the newly-created job, or %NULL for internal jobs
31
* @driver: The class object for the newly-created job.
32
+ * @ctx: The AioContext to run the job coroutine in.
33
* @errp: Error object.
34
*/
35
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
36
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
37
+ Error **errp);
38
39
/**
40
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
41
diff --git a/blockjob.c b/blockjob.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/blockjob.c
44
+++ b/blockjob.c
45
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
46
{
47
BlockJob *job = opaque;
48
49
+ job->job.aio_context = new_context;
50
if (job->driver->attached_aio_context) {
51
job->driver->attached_aio_context(job, new_context);
52
}
62
}
53
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
63
- be32_to_cpus(&header.magic);
54
block_job_drain(job);
64
- be32_to_cpus(&header.version);
65
- be64_to_cpus(&header.backing_file_offset);
66
- be32_to_cpus(&header.backing_file_size);
67
- be32_to_cpus(&header.mtime);
68
- be64_to_cpus(&header.size);
69
- be32_to_cpus(&header.crypt_method);
70
- be64_to_cpus(&header.l1_table_offset);
71
+ header.magic = be32_to_cpu(header.magic);
72
+ header.version = be32_to_cpu(header.version);
73
+ header.backing_file_offset = be64_to_cpu(header.backing_file_offset);
74
+ header.backing_file_size = be32_to_cpu(header.backing_file_size);
75
+ header.mtime = be32_to_cpu(header.mtime);
76
+ header.size = be64_to_cpu(header.size);
77
+ header.crypt_method = be32_to_cpu(header.crypt_method);
78
+ header.l1_table_offset = be64_to_cpu(header.l1_table_offset);
79
80
if (header.magic != QCOW_MAGIC) {
81
error_setg(errp, "Image not in qcow format");
82
@@ -XXX,XX +XXX,XX @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,
55
}
83
}
56
84
57
+ job->job.aio_context = NULL;
85
for(i = 0;i < s->l1_size; i++) {
58
job_unref(&job->job);
86
- be64_to_cpus(&s->l1_table[i]);
59
}
87
+ s->l1_table[i] = be64_to_cpu(s->l1_table[i]);
60
61
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
62
return NULL;
63
}
88
}
64
89
65
- job = job_create(job_id, &driver->job_driver, errp);
90
/* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */
66
+ job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
67
+ errp);
68
if (job == NULL) {
69
blk_unref(blk);
70
return NULL;
71
diff --git a/job.c b/job.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/job.c
74
+++ b/job.c
75
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id)
76
return NULL;
77
}
78
79
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
80
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
81
+ Error **errp)
82
{
83
Job *job;
84
85
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
86
job->driver = driver;
87
job->id = g_strdup(job_id);
88
job->refcnt = 1;
89
+ job->aio_context = ctx;
90
91
job_state_transition(job, JOB_STATUS_CREATED);
92
93
--
91
--
94
2.13.6
92
2.19.1
95
93
96
94
diff view generated by jsdifflib
1
This doesn't actually move any transaction code to Job yet, but it
1
From: Peter Maydell <peter.maydell@linaro.org>
2
renames the type for transactions from BlockJobTxn to JobTxn and makes
3
them contain Jobs rather than BlockJobs
4
2
3
Taking the address of a field in a packed struct is a bad idea, because
4
it might not be actually aligned enough for that pointer type (and
5
thus cause a crash on dereference on some host architectures). Newer
6
versions of clang warn about this. Avoid the bug by not using the
7
"modify in place" byte swapping functions.
8
9
There are a few places where the in-place swap function is
10
used on something other than a packed struct field; we convert
11
those anyway, for consistency.
12
13
This patch was produced with the following spatch script:
14
15
@@
16
expression E;
17
@@
18
-be16_to_cpus(&E);
19
+E = be16_to_cpu(E);
20
@@
21
expression E;
22
@@
23
-be32_to_cpus(&E);
24
+E = be32_to_cpu(E);
25
@@
26
expression E;
27
@@
28
-be64_to_cpus(&E);
29
+E = be64_to_cpu(E);
30
@@
31
expression E;
32
@@
33
-cpu_to_be16s(&E);
34
+E = cpu_to_be16(E);
35
@@
36
expression E;
37
@@
38
-cpu_to_be32s(&E);
39
+E = cpu_to_be32(E);
40
@@
41
expression E;
42
@@
43
-cpu_to_be64s(&E);
44
+E = cpu_to_be64(E);
45
46
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
47
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
48
Tested-by: John Snow <jsnow@redhat.com>
49
Reviewed-by: John Snow <jsnow@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
50
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
51
---
8
include/block/block_int.h | 2 +-
52
block/qcow2-bitmap.c | 24 ++++++++++++------------
9
include/block/blockjob.h | 11 ++++----
53
1 file changed, 12 insertions(+), 12 deletions(-)
10
include/block/blockjob_int.h | 2 +-
11
include/qemu/job.h | 3 +++
12
block/backup.c | 2 +-
13
blockdev.c | 14 +++++------
14
blockjob.c | 60 +++++++++++++++++++++++---------------------
15
tests/test-blockjob-txn.c | 8 +++---
16
8 files changed, 54 insertions(+), 48 deletions(-)
17
54
18
diff --git a/include/block/block_int.h b/include/block/block_int.h
55
diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c
19
index XXXXXXX..XXXXXXX 100644
56
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/block_int.h
57
--- a/block/qcow2-bitmap.c
21
+++ b/include/block/block_int.h
58
+++ b/block/qcow2-bitmap.c
22
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
59
@@ -XXX,XX +XXX,XX @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size)
23
BlockdevOnError on_target_error,
60
size_t i;
24
int creation_flags,
61
25
BlockCompletionFunc *cb, void *opaque,
62
for (i = 0; i < size; ++i) {
26
- BlockJobTxn *txn, Error **errp);
63
- cpu_to_be64s(&bitmap_table[i]);
27
+ JobTxn *txn, Error **errp);
64
+ bitmap_table[i] = cpu_to_be64(bitmap_table[i]);
28
29
void hmp_drive_add_node(Monitor *mon, const char *optstr);
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 @@
36
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
37
38
typedef struct BlockJobDriver BlockJobDriver;
39
-typedef struct BlockJobTxn BlockJobTxn;
40
+typedef struct JobTxn JobTxn;
41
42
/**
43
* BlockJob:
44
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
45
/** BlockDriverStates that are involved in this block job */
46
GSList *nodes;
47
48
- BlockJobTxn *txn;
49
- QLIST_ENTRY(BlockJob) txn_list;
50
+ JobTxn *txn;
51
} BlockJob;
52
53
/**
54
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
55
* group. Jobs wait for each other before completing. Cancelling one job
56
* cancels all jobs in the transaction.
57
*/
58
-BlockJobTxn *block_job_txn_new(void);
59
+JobTxn *block_job_txn_new(void);
60
61
/**
62
* block_job_txn_unref:
63
@@ -XXX,XX +XXX,XX @@ BlockJobTxn *block_job_txn_new(void);
64
* or block_job_txn_new. If it's the last reference to the object, it will be
65
* freed.
66
*/
67
-void block_job_txn_unref(BlockJobTxn *txn);
68
+void block_job_txn_unref(JobTxn *txn);
69
70
/**
71
* block_job_txn_add_job:
72
@@ -XXX,XX +XXX,XX @@ void block_job_txn_unref(BlockJobTxn *txn);
73
* The caller must call either block_job_txn_unref() or block_job_completed()
74
* to release the reference that is automatically grabbed here.
75
*/
76
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job);
77
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
78
79
/**
80
* block_job_is_internal:
81
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob_int.h
84
+++ b/include/block/blockjob_int.h
85
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
86
* called from a wrapper that is specific to the job type.
87
*/
88
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
89
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
90
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
91
uint64_t shared_perm, int64_t speed, int flags,
92
BlockCompletionFunc *cb, void *opaque, Error **errp);
93
94
diff --git a/include/qemu/job.h b/include/qemu/job.h
95
index XXXXXXX..XXXXXXX 100644
96
--- a/include/qemu/job.h
97
+++ b/include/qemu/job.h
98
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
99
100
/** Element of the list of jobs */
101
QLIST_ENTRY(Job) job_list;
102
+
103
+ /** Element of the list of jobs in a job transaction */
104
+ QLIST_ENTRY(Job) txn_list;
105
} Job;
106
107
/**
108
diff --git a/block/backup.c b/block/backup.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/backup.c
111
+++ b/block/backup.c
112
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
113
BlockdevOnError on_target_error,
114
int creation_flags,
115
BlockCompletionFunc *cb, void *opaque,
116
- BlockJobTxn *txn, Error **errp)
117
+ JobTxn *txn, Error **errp)
118
{
119
int64_t len;
120
BlockDriverInfo bdi;
121
diff --git a/blockdev.c b/blockdev.c
122
index XXXXXXX..XXXXXXX 100644
123
--- a/blockdev.c
124
+++ b/blockdev.c
125
@@ -XXX,XX +XXX,XX @@ typedef struct BlkActionOps {
126
struct BlkActionState {
127
TransactionAction *action;
128
const BlkActionOps *ops;
129
- BlockJobTxn *block_job_txn;
130
+ JobTxn *block_job_txn;
131
TransactionProperties *txn_props;
132
QSIMPLEQ_ENTRY(BlkActionState) entry;
133
};
134
@@ -XXX,XX +XXX,XX @@ typedef struct DriveBackupState {
135
BlockJob *job;
136
} DriveBackupState;
137
138
-static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
139
+static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn,
140
Error **errp);
141
142
static void drive_backup_prepare(BlkActionState *common, Error **errp)
143
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevBackupState {
144
BlockJob *job;
145
} BlockdevBackupState;
146
147
-static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
148
+static BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
149
Error **errp);
150
151
static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
152
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
153
Error **errp)
154
{
155
TransactionActionList *dev_entry = dev_list;
156
- BlockJobTxn *block_job_txn = NULL;
157
+ JobTxn *block_job_txn = NULL;
158
BlkActionState *state, *next;
159
Error *local_err = NULL;
160
161
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
162
QSIMPLEQ_INIT(&snap_bdrv_states);
163
164
/* Does this transaction get canceled as a group on failure?
165
- * If not, we don't really need to make a BlockJobTxn.
166
+ * If not, we don't really need to make a JobTxn.
167
*/
168
props = get_transaction_properties(props);
169
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
170
@@ -XXX,XX +XXX,XX @@ out:
171
aio_context_release(aio_context);
172
}
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
{
178
BlockDriverState *bs;
179
@@ -XXX,XX +XXX,XX @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp)
180
return bdrv_named_nodes_list(errp);
181
}
182
183
-BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
184
+BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn,
185
Error **errp)
186
{
187
BlockDriverState *bs;
188
diff --git a/blockjob.c b/blockjob.c
189
index XXXXXXX..XXXXXXX 100644
190
--- a/blockjob.c
191
+++ b/blockjob.c
192
@@ -XXX,XX +XXX,XX @@
193
#include "qemu/timer.h"
194
195
/* Transactional group of block jobs */
196
-struct BlockJobTxn {
197
+struct JobTxn {
198
199
/* Is this txn being cancelled? */
200
bool aborting;
201
202
/* List of jobs */
203
- QLIST_HEAD(, BlockJob) jobs;
204
+ QLIST_HEAD(, Job) jobs;
205
206
/* Reference count */
207
int refcnt;
208
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
209
}
65
}
210
}
66
}
211
67
212
-BlockJobTxn *block_job_txn_new(void)
68
@@ -XXX,XX +XXX,XX @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb,
213
+JobTxn *block_job_txn_new(void)
69
}
70
71
for (i = 0; i < tb->size; ++i) {
72
- be64_to_cpus(&table[i]);
73
+ table[i] = be64_to_cpu(table[i]);
74
ret = check_table_entry(table[i], s->cluster_size);
75
if (ret < 0) {
76
goto fail;
77
@@ -XXX,XX +XXX,XX @@ fail:
78
79
static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry)
214
{
80
{
215
- BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
81
- be64_to_cpus(&entry->bitmap_table_offset);
216
+ JobTxn *txn = g_new0(JobTxn, 1);
82
- be32_to_cpus(&entry->bitmap_table_size);
217
QLIST_INIT(&txn->jobs);
83
- be32_to_cpus(&entry->flags);
218
txn->refcnt = 1;
84
- be16_to_cpus(&entry->name_size);
219
return txn;
85
- be32_to_cpus(&entry->extra_data_size);
86
+ entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset);
87
+ entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size);
88
+ entry->flags = be32_to_cpu(entry->flags);
89
+ entry->name_size = be16_to_cpu(entry->name_size);
90
+ entry->extra_data_size = be32_to_cpu(entry->extra_data_size);
220
}
91
}
221
92
222
-static void block_job_txn_ref(BlockJobTxn *txn)
93
static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry)
223
+static void block_job_txn_ref(JobTxn *txn)
224
{
94
{
225
txn->refcnt++;
95
- cpu_to_be64s(&entry->bitmap_table_offset);
96
- cpu_to_be32s(&entry->bitmap_table_size);
97
- cpu_to_be32s(&entry->flags);
98
- cpu_to_be16s(&entry->name_size);
99
- cpu_to_be32s(&entry->extra_data_size);
100
+ entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset);
101
+ entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size);
102
+ entry->flags = cpu_to_be32(entry->flags);
103
+ entry->name_size = cpu_to_be16(entry->name_size);
104
+ entry->extra_data_size = cpu_to_be32(entry->extra_data_size);
226
}
105
}
227
106
228
-void block_job_txn_unref(BlockJobTxn *txn)
107
static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size)
229
+void block_job_txn_unref(JobTxn *txn)
230
{
231
if (txn && --txn->refcnt == 0) {
232
g_free(txn);
233
}
234
}
235
236
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
237
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job)
238
{
239
if (!txn) {
240
return;
241
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
242
assert(!job->txn);
243
job->txn = txn;
244
245
- QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
246
+ QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list);
247
block_job_txn_ref(txn);
248
}
249
250
void block_job_txn_del_job(BlockJob *job)
251
{
252
if (job->txn) {
253
- QLIST_REMOVE(job, txn_list);
254
+ QLIST_REMOVE(&job->job, txn_list);
255
block_job_txn_unref(job->txn);
256
job->txn = NULL;
257
}
258
@@ -XXX,XX +XXX,XX @@ static void job_cancel_async(Job *job, bool force)
259
job->force_cancel |= force;
260
}
261
262
-static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
263
+static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock)
264
{
265
AioContext *ctx;
266
- BlockJob *job, *next;
267
+ Job *job, *next;
268
+ BlockJob *bjob;
269
int rc = 0;
270
271
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
272
+ assert(is_block_job(job));
273
+ bjob = container_of(job, BlockJob, job);
274
+
275
if (lock) {
276
- ctx = blk_get_aio_context(job->blk);
277
+ ctx = job->aio_context;
278
aio_context_acquire(ctx);
279
}
280
- rc = fn(job);
281
+ rc = fn(bjob);
282
if (lock) {
283
aio_context_release(ctx);
284
}
285
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
286
static void block_job_completed_txn_abort(BlockJob *job)
287
{
288
AioContext *ctx;
289
- BlockJobTxn *txn = job->txn;
290
- BlockJob *other_job;
291
+ JobTxn *txn = job->txn;
292
+ Job *other_job;
293
294
if (txn->aborting) {
295
/*
296
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
297
298
/* We are the first failed job. Cancel other jobs. */
299
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
300
- ctx = blk_get_aio_context(other_job->blk);
301
+ ctx = other_job->aio_context;
302
aio_context_acquire(ctx);
303
}
304
305
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
306
* them; this job, however, may or may not be cancelled, depending
307
* on the caller, so leave it. */
308
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
309
- if (other_job != job) {
310
- job_cancel_async(&other_job->job, false);
311
+ if (other_job != &job->job) {
312
+ job_cancel_async(other_job, false);
313
}
314
}
315
while (!QLIST_EMPTY(&txn->jobs)) {
316
other_job = QLIST_FIRST(&txn->jobs);
317
- ctx = blk_get_aio_context(other_job->blk);
318
- if (!job_is_completed(&other_job->job)) {
319
- assert(job_is_cancelled(&other_job->job));
320
- job_finish_sync(&other_job->job, NULL, NULL);
321
+ ctx = other_job->aio_context;
322
+ if (!job_is_completed(other_job)) {
323
+ assert(job_is_cancelled(other_job));
324
+ job_finish_sync(other_job, NULL, NULL);
325
}
326
- job_finalize_single(&other_job->job);
327
+ job_finalize_single(other_job);
328
aio_context_release(ctx);
329
}
330
331
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
332
333
static void block_job_completed_txn_success(BlockJob *job)
334
{
335
- BlockJobTxn *txn = job->txn;
336
- BlockJob *other_job;
337
+ JobTxn *txn = job->txn;
338
+ Job *other_job;
339
340
job_state_transition(&job->job, JOB_STATUS_WAITING);
341
342
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
343
* txn.
344
*/
345
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
346
- if (!job_is_completed(&other_job->job)) {
347
+ if (!job_is_completed(other_job)) {
348
return;
349
}
350
- assert(other_job->job.ret == 0);
351
+ assert(other_job->ret == 0);
352
}
353
354
block_job_txn_apply(txn, block_job_transition_to_pending, false);
355
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
356
*/
357
358
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
359
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
360
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
361
uint64_t shared_perm, int64_t speed, int flags,
362
BlockCompletionFunc *cb, void *opaque, Error **errp)
363
{
364
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
365
index XXXXXXX..XXXXXXX 100644
366
--- a/tests/test-blockjob-txn.c
367
+++ b/tests/test-blockjob-txn.c
368
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
369
*/
370
static BlockJob *test_block_job_start(unsigned int iterations,
371
bool use_timer,
372
- int rc, int *result, BlockJobTxn *txn)
373
+ int rc, int *result, JobTxn *txn)
374
{
375
BlockDriverState *bs;
376
TestBlockJob *s;
377
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
378
static void test_single_job(int expected)
379
{
380
BlockJob *job;
381
- BlockJobTxn *txn;
382
+ JobTxn *txn;
383
int result = -EINPROGRESS;
384
385
txn = block_job_txn_new();
386
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
387
{
388
BlockJob *job1;
389
BlockJob *job2;
390
- BlockJobTxn *txn;
391
+ JobTxn *txn;
392
int result1 = -EINPROGRESS;
393
int result2 = -EINPROGRESS;
394
395
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
396
{
397
BlockJob *job1;
398
BlockJob *job2;
399
- BlockJobTxn *txn;
400
+ JobTxn *txn;
401
int result1 = -EINPROGRESS;
402
int result2 = -EINPROGRESS;
403
404
--
108
--
405
2.13.6
109
2.19.1
406
110
407
111
diff view generated by jsdifflib
1
This adds a minimal query-jobs implementation that shouldn't pose many
1
From: Daniel P. Berrangé <berrange@redhat.com>
2
design questions. It can later be extended to expose more information,
3
and especially job-specific information.
4
2
3
The qcow2 block driver expects to see a valid sector size even when it
4
has opened the crypto layer with QCRYPTO_BLOCK_OPEN_NO_IO.
5
6
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
7
Reviewed-by: Alberto Garcia <berto@igalia.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
9
---
7
qapi/job.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++
10
crypto/block-qcow.c | 2 ++
8
include/qemu/job.h | 3 +++
11
1 file changed, 2 insertions(+)
9
job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
job.c | 2 +-
11
4 files changed, 104 insertions(+), 1 deletion(-)
12
12
13
diff --git a/qapi/job.json b/qapi/job.json
13
diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/qapi/job.json
15
--- a/crypto/block-qcow.c
16
+++ b/qapi/job.json
16
+++ b/crypto/block-qcow.c
17
@@ -XXX,XX +XXX,XX @@
17
@@ -XXX,XX +XXX,XX @@ qcrypto_block_qcow_open(QCryptoBlock *block,
18
# Since: 2.13
18
Error **errp)
19
##
20
{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
21
+
22
+##
23
+# @JobInfo:
24
+#
25
+# Information about a job.
26
+#
27
+# @id: The job identifier
28
+#
29
+# @type: The kind of job that is being performed
30
+#
31
+# @status: Current job state/status
32
+#
33
+# @current-progress: Progress made until now. The unit is arbitrary and the
34
+# value can only meaningfully be used for the ratio of
35
+# @current-progress to @total-progress. The value is
36
+# monotonically increasing.
37
+#
38
+# @total-progress: Estimated @current-progress value at the completion of
39
+# the job. This value can arbitrarily change while the
40
+# job is running, in both directions.
41
+#
42
+# @error: If this field is present, the job failed; if it is
43
+# still missing in the CONCLUDED state, this indicates
44
+# successful completion.
45
+#
46
+# The value is a human-readable error message to describe
47
+# the reason for the job failure. It should not be parsed
48
+# by applications.
49
+#
50
+# Since: 2.13
51
+##
52
+{ 'struct': 'JobInfo',
53
+ 'data': { 'id': 'str', 'type': 'JobType', 'status': 'JobStatus',
54
+ 'current-progress': 'int', 'total-progress': 'int',
55
+ '*error': 'str' } }
56
+
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
{
19
{
154
return (job->id == NULL);
20
if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
155
}
21
+ block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE;
22
+ block->payload_offset = 0;
23
return 0;
24
} else {
25
if (!options->u.qcow.key_secret) {
156
--
26
--
157
2.13.6
27
2.19.1
158
28
159
29
diff view generated by jsdifflib
1
This moves block_job_dismiss() to the Job layer.
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
This doesn't have any practical effect at the moment because the
4
values of BDRV_SECTOR_SIZE, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE and
5
QCRYPTO_BLOCK_QCOW_SECTOR_SIZE are all the same (512 bytes), but
6
future encryption methods could have different requirements.
7
8
Signed-off-by: Alberto Garcia <berto@igalia.com>
9
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
11
---
6
include/block/blockjob.h | 9 ---------
12
block/qcow2.c | 2 +-
7
include/qemu/job.h | 7 ++++++-
13
1 file changed, 1 insertion(+), 1 deletion(-)
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
14
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
15
diff --git a/block/qcow2.c b/block/qcow2.c
15
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
17
--- a/block/qcow2.c
17
+++ b/include/block/blockjob.h
18
+++ b/block/qcow2.c
18
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
19
@@ -XXX,XX +XXX,XX @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp)
19
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
20
20
21
if (bs->encrypted) {
21
/**
22
/* Encryption works on a sector granularity */
22
- * block_job_dismiss:
23
- bs->bl.request_alignment = BDRV_SECTOR_SIZE;
23
- * @job: The job to be dismissed.
24
+ bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto);
24
- * @errp: Error object.
25
- *
26
- * Remove a concluded job from the query list.
27
- */
28
-void block_job_dismiss(BlockJob **job, Error **errp);
29
-
30
-/**
31
* block_job_progress_update:
32
* @job: The job that has made progress
33
* @done: How much progress the job made
34
diff --git a/include/qemu/job.h b/include/qemu/job.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/qemu/job.h
37
+++ b/include/qemu/job.h
38
@@ -XXX,XX +XXX,XX @@ int job_complete_sync(Job *job, Error **errp);
39
*/
40
void job_finalize(Job *job, Error **errp);
41
42
+/**
43
+ * Remove the concluded @job from the query list and resets the passed pointer
44
+ * to %NULL. Returns an error if the job is not actually concluded.
45
+ */
46
+void job_dismiss(Job **job, Error **errp);
47
+
48
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
49
50
/**
51
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
52
53
/* TODO To be removed from the public interface */
54
void job_state_transition(Job *job, JobStatus s1);
55
-void job_do_dismiss(Job *job);
56
57
#endif
58
diff --git a/blockdev.c b/blockdev.c
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
}
25
}
74
26
bs->bl.pwrite_zeroes_alignment = s->cluster_size;
75
- trace_qmp_block_job_dismiss(job);
27
bs->bl.pdiscard_alignment = s->cluster_size;
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
-{
93
- BlockJob *job = *jobptr;
94
- /* similarly to _complete, this is QMP-interface only. */
95
- assert(job->job.id);
96
- if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
97
- return;
98
- }
99
-
100
- job_do_dismiss(&job->job);
101
- *jobptr = NULL;
102
-}
103
-
104
void block_job_progress_update(BlockJob *job, uint64_t done)
105
{
106
job->offset += done;
107
diff --git a/job.c b/job.c
108
index XXXXXXX..XXXXXXX 100644
109
--- a/job.c
110
+++ b/job.c
111
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
112
job_resume(job);
113
}
114
115
-void job_do_dismiss(Job *job)
116
+static void job_do_dismiss(Job *job)
117
{
118
assert(job);
119
job->busy = false;
120
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
121
job_unref(job);
122
}
123
124
+void job_dismiss(Job **jobptr, Error **errp)
125
+{
126
+ Job *job = *jobptr;
127
+ /* similarly to _complete, this is QMP-interface only. */
128
+ assert(job->id);
129
+ if (job_apply_verb(job, JOB_VERB_DISMISS, errp)) {
130
+ return;
131
+ }
132
+
133
+ job_do_dismiss(job);
134
+ *jobptr = NULL;
135
+}
136
+
137
void job_early_fail(Job *job)
138
{
139
assert(job->status == JOB_STATUS_CREATED);
140
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/tests/test-blockjob.c
143
+++ b/tests/test-blockjob.c
144
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
145
146
job_cancel_sync(&job->job);
147
if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
148
- BlockJob *dummy = job;
149
- block_job_dismiss(&dummy, &error_abort);
150
+ Job *dummy = &job->job;
151
+ job_dismiss(&dummy, &error_abort);
152
}
153
assert(job->job.status == JOB_STATUS_NULL);
154
job_unref(&job->job);
155
--
28
--
156
2.13.6
29
2.19.1
157
30
158
31
diff view generated by jsdifflib
1
This moves the finalisation of a single job from BlockJob to Job.
1
From: Li Qiang <liq3ea@163.com>
2
2
3
Some part of this code depends on job transactions, and job transactions
3
Signed-off-by: Li Qiang <liq3ea@163.com>
4
call this code, we introduce some temporary calls from Job functions to
4
Reviewed-by: Alberto Garcia <berto@igalia.com>
5
BlockJob ones. This will be fixed once transactions move to Job, too.
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
7
include/sysemu/block-backend.h | 6 +++---
8
block/block-backend.c | 8 ++++----
9
2 files changed, 7 insertions(+), 7 deletions(-)
6
10
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
10
include/block/blockjob.h | 9 ---
11
include/block/blockjob_int.h | 36 -----------
12
include/qemu/job.h | 53 +++++++++++++++-
13
block/backup.c | 22 +++----
14
block/commit.c | 2 +-
15
block/mirror.c | 2 +-
16
blockjob.c | 142 ++++++++-----------------------------------
17
job.c | 100 +++++++++++++++++++++++++++++-
18
qemu-img.c | 2 +-
19
tests/test-blockjob.c | 10 +--
20
10 files changed, 194 insertions(+), 184 deletions(-)
21
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
23
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
13
--- a/include/sysemu/block-backend.h
25
+++ b/include/block/blockjob.h
14
+++ b/include/sysemu/block-backend.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
15
@@ -XXX,XX +XXX,XX @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read,
27
/** Rate limiting data structure for implementing @speed. */
16
int error);
28
RateLimit limit;
17
void blk_error_action(BlockBackend *blk, BlockErrorAction action,
29
18
bool is_read, int error);
30
- /** The completion function that will be called when the job completes. */
19
-int blk_is_read_only(BlockBackend *blk);
31
- BlockCompletionFunc *cb;
20
-int blk_is_sg(BlockBackend *blk);
32
-
21
-int blk_enable_write_cache(BlockBackend *blk);
33
/** Block other operations when block job is running */
22
+bool blk_is_read_only(BlockBackend *blk);
34
Error *blocker;
23
+bool blk_is_sg(BlockBackend *blk);
35
24
+bool blk_enable_write_cache(BlockBackend *blk);
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
25
void blk_set_enable_write_cache(BlockBackend *blk, bool wce);
37
/** BlockDriverStates that are involved in this block job */
26
void blk_invalidate_cache(BlockBackend *blk, Error **errp);
38
GSList *nodes;
27
bool blk_is_inserted(BlockBackend *blk);
39
28
diff --git a/block/block-backend.c b/block/block-backend.c
40
- /** The opaque value that is passed to the completion function. */
41
- void *opaque;
42
-
43
- /** ret code passed to block_job_completed. */
44
- int ret;
45
-
46
BlockJobTxn *txn;
47
QLIST_ENTRY(BlockJob) txn_list;
48
} BlockJob;
49
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
50
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
51
--- a/include/block/blockjob_int.h
30
--- a/block/block-backend.c
52
+++ b/include/block/blockjob_int.h
31
+++ b/block/block-backend.c
53
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
32
@@ -XXX,XX +XXX,XX @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action,
54
*/
55
int (*prepare)(BlockJob *job);
56
57
- /**
58
- * If the callback is not NULL, it will be invoked when all the jobs
59
- * belonging to the same transaction complete; or upon this job's
60
- * completion if it is not in a transaction. Skipped if NULL.
61
- *
62
- * All jobs will complete with a call to either .commit() or .abort() but
63
- * never both.
64
- */
65
- void (*commit)(BlockJob *job);
66
-
67
- /**
68
- * If the callback is not NULL, it will be invoked when any job in the
69
- * same transaction fails; or upon this job's failure (due to error or
70
- * cancellation) if it is not in a transaction. Skipped if NULL.
71
- *
72
- * All jobs will complete with a call to either .commit() or .abort() but
73
- * never both.
74
- */
75
- void (*abort)(BlockJob *job);
76
-
77
- /**
78
- * If the callback is not NULL, it will be invoked after a call to either
79
- * .commit() or .abort(). Regardless of which callback is invoked after
80
- * completion, .clean() will always be called, even if the job does not
81
- * belong to a transaction group.
82
- */
83
- void (*clean)(BlockJob *job);
84
-
85
/*
86
* If the callback is not NULL, it will be invoked before the job is
87
* resumed in a new AioContext. This is the place to move any resources
88
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job);
89
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
90
91
/**
92
- * block_job_early_fail:
93
- * @bs: The block device.
94
- *
95
- * The block job could not be started, free it.
96
- */
97
-void block_job_early_fail(BlockJob *job);
98
-
99
-/**
100
* block_job_completed:
101
* @job: The job being completed.
102
* @ret: The status code.
103
diff --git a/include/qemu/job.h b/include/qemu/job.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/qemu/job.h
106
+++ b/include/qemu/job.h
107
@@ -XXX,XX +XXX,XX @@
108
#include "qapi/qapi-types-block-core.h"
109
#include "qemu/queue.h"
110
#include "qemu/coroutine.h"
111
+#include "block/aio.h"
112
113
typedef struct JobDriver JobDriver;
114
115
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
116
/** True if this job should automatically dismiss itself */
117
bool auto_dismiss;
118
119
+ /** ret code passed to block_job_completed. */
120
+ int ret;
121
+
122
+ /** The completion function that will be called when the job completes. */
123
+ BlockCompletionFunc *cb;
124
+
125
+ /** The opaque value that is passed to the completion function. */
126
+ void *opaque;
127
+
128
/** Notifiers called when a cancelled job is finalised */
129
NotifierList on_finalize_cancelled;
130
131
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
132
*/
133
void (*user_resume)(Job *job);
134
135
+ /**
136
+ * If the callback is not NULL, it will be invoked when all the jobs
137
+ * belonging to the same transaction complete; or upon this job's
138
+ * completion if it is not in a transaction. Skipped if NULL.
139
+ *
140
+ * All jobs will complete with a call to either .commit() or .abort() but
141
+ * never both.
142
+ */
143
+ void (*commit)(Job *job);
144
+
145
+ /**
146
+ * If the callback is not NULL, it will be invoked when any job in the
147
+ * same transaction fails; or upon this job's failure (due to error or
148
+ * cancellation) if it is not in a transaction. Skipped if NULL.
149
+ *
150
+ * All jobs will complete with a call to either .commit() or .abort() but
151
+ * never both.
152
+ */
153
+ void (*abort)(Job *job);
154
+
155
+ /**
156
+ * If the callback is not NULL, it will be invoked after a call to either
157
+ * .commit() or .abort(). Regardless of which callback is invoked after
158
+ * completion, .clean() will always be called, even if the job does not
159
+ * belong to a transaction group.
160
+ */
161
+ void (*clean)(Job *job);
162
+
163
+
164
/** Called when the job is freed */
165
void (*free)(Job *job);
166
};
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
*/
175
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
176
- int flags, Error **errp);
177
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
178
179
/**
180
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
181
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id);
182
*/
183
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
184
185
+/** The @job could not be started, free it. */
186
+void job_early_fail(Job *job);
187
+
188
+
189
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
190
191
/**
192
@@ -XXX,XX +XXX,XX @@ void job_state_transition(Job *job, JobStatus s1);
193
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
194
bool job_should_pause(Job *job);
195
bool job_started(Job *job);
196
+void job_do_dismiss(Job *job);
197
+int job_finalize_single(Job *job);
198
+void job_update_rc(Job *job);
199
+
200
+typedef struct BlockJob BlockJob;
201
+void block_job_txn_del_job(BlockJob *job);
202
203
#endif
204
diff --git a/block/backup.c b/block/backup.c
205
index XXXXXXX..XXXXXXX 100644
206
--- a/block/backup.c
207
+++ b/block/backup.c
208
@@ -XXX,XX +XXX,XX @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
209
}
33
}
210
}
34
}
211
35
212
-static void backup_commit(BlockJob *job)
36
-int blk_is_read_only(BlockBackend *blk)
213
+static void backup_commit(Job *job)
37
+bool blk_is_read_only(BlockBackend *blk)
214
{
38
{
215
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
39
BlockDriverState *bs = blk_bs(blk);
216
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
40
217
if (s->sync_bitmap) {
41
@@ -XXX,XX +XXX,XX @@ int blk_is_read_only(BlockBackend *blk)
218
backup_cleanup_sync_bitmap(s, 0);
219
}
42
}
220
}
43
}
221
44
222
-static void backup_abort(BlockJob *job)
45
-int blk_is_sg(BlockBackend *blk)
223
+static void backup_abort(Job *job)
46
+bool blk_is_sg(BlockBackend *blk)
224
{
47
{
225
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
48
BlockDriverState *bs = blk_bs(blk);
226
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
49
227
if (s->sync_bitmap) {
50
if (!bs) {
228
backup_cleanup_sync_bitmap(s, -1);
51
- return 0;
52
+ return false;
229
}
53
}
54
55
return bdrv_is_sg(bs);
230
}
56
}
231
57
232
-static void backup_clean(BlockJob *job)
58
-int blk_enable_write_cache(BlockBackend *blk)
233
+static void backup_clean(Job *job)
59
+bool blk_enable_write_cache(BlockBackend *blk)
234
{
60
{
235
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
61
return blk->enable_write_cache;
236
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
237
assert(s->target);
238
blk_unref(s->target);
239
s->target = NULL;
240
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
241
.free = block_job_free,
242
.user_resume = block_job_user_resume,
243
.start = backup_run,
244
+ .commit = backup_commit,
245
+ .abort = backup_abort,
246
+ .clean = backup_clean,
247
},
248
- .commit = backup_commit,
249
- .abort = backup_abort,
250
- .clean = backup_clean,
251
.attached_aio_context = backup_attached_aio_context,
252
.drain = backup_drain,
253
};
254
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
255
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
256
}
257
if (job) {
258
- backup_clean(&job->common);
259
- block_job_early_fail(&job->common);
260
+ backup_clean(&job->common.job);
261
+ job_early_fail(&job->common.job);
262
}
263
264
return NULL;
265
diff --git a/block/commit.c b/block/commit.c
266
index XXXXXXX..XXXXXXX 100644
267
--- a/block/commit.c
268
+++ b/block/commit.c
269
@@ -XXX,XX +XXX,XX @@ fail:
270
if (commit_top_bs) {
271
bdrv_replace_node(commit_top_bs, top, &error_abort);
272
}
273
- block_job_early_fail(&s->common);
274
+ job_early_fail(&s->common.job);
275
}
62
}
276
277
278
diff --git a/block/mirror.c b/block/mirror.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/block/mirror.c
281
+++ b/block/mirror.c
282
@@ -XXX,XX +XXX,XX @@ fail:
283
284
g_free(s->replaces);
285
blk_unref(s->target);
286
- block_job_early_fail(&s->common);
287
+ job_early_fail(&s->common.job);
288
}
289
290
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
291
diff --git a/blockjob.c b/blockjob.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockjob.c
294
+++ b/blockjob.c
295
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
296
block_job_txn_ref(txn);
297
}
298
299
-static void block_job_txn_del_job(BlockJob *job)
300
+void block_job_txn_del_job(BlockJob *job)
301
{
302
if (job->txn) {
303
QLIST_REMOVE(job, txn_list);
304
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
305
return job->driver;
306
}
307
308
-static void block_job_decommission(BlockJob *job)
309
-{
310
- assert(job);
311
- job->job.busy = false;
312
- job->job.paused = false;
313
- job->job.deferred_to_main_loop = true;
314
- block_job_txn_del_job(job);
315
- job_state_transition(&job->job, JOB_STATUS_NULL);
316
- job_unref(&job->job);
317
-}
318
-
319
-static void block_job_do_dismiss(BlockJob *job)
320
-{
321
- block_job_decommission(job);
322
-}
323
-
324
-static void block_job_conclude(BlockJob *job)
325
-{
326
- job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
327
- if (job->job.auto_dismiss || !job_started(&job->job)) {
328
- block_job_do_dismiss(job);
329
- }
330
-}
331
-
332
-static void block_job_update_rc(BlockJob *job)
333
-{
334
- if (!job->ret && job_is_cancelled(&job->job)) {
335
- job->ret = -ECANCELED;
336
- }
337
- if (job->ret) {
338
- job_state_transition(&job->job, JOB_STATUS_ABORTING);
339
- }
340
-}
341
-
342
static int block_job_prepare(BlockJob *job)
343
{
344
- if (job->ret == 0 && job->driver->prepare) {
345
- job->ret = job->driver->prepare(job);
346
- }
347
- return job->ret;
348
-}
349
-
350
-static void block_job_commit(BlockJob *job)
351
-{
352
- assert(!job->ret);
353
- if (job->driver->commit) {
354
- job->driver->commit(job);
355
- }
356
-}
357
-
358
-static void block_job_abort(BlockJob *job)
359
-{
360
- assert(job->ret);
361
- if (job->driver->abort) {
362
- job->driver->abort(job);
363
- }
364
-}
365
-
366
-static void block_job_clean(BlockJob *job)
367
-{
368
- if (job->driver->clean) {
369
- job->driver->clean(job);
370
+ if (job->job.ret == 0 && job->driver->prepare) {
371
+ job->job.ret = job->driver->prepare(job);
372
}
373
-}
374
-
375
-static int block_job_finalize_single(BlockJob *job)
376
-{
377
- assert(job_is_completed(&job->job));
378
-
379
- /* Ensure abort is called for late-transactional failures */
380
- block_job_update_rc(job);
381
-
382
- if (!job->ret) {
383
- block_job_commit(job);
384
- } else {
385
- block_job_abort(job);
386
- }
387
- block_job_clean(job);
388
-
389
- if (job->cb) {
390
- job->cb(job->opaque, job->ret);
391
- }
392
-
393
- /* Emit events only if we actually started */
394
- if (job_started(&job->job)) {
395
- if (job_is_cancelled(&job->job)) {
396
- job_event_cancelled(&job->job);
397
- } else {
398
- job_event_completed(&job->job);
399
- }
400
- }
401
-
402
- block_job_txn_del_job(job);
403
- block_job_conclude(job);
404
- return 0;
405
+ return job->job.ret;
406
}
407
408
static void block_job_cancel_async(BlockJob *job, bool force)
409
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
410
while (!job_is_completed(&job->job)) {
411
aio_poll(qemu_get_aio_context(), true);
412
}
413
- ret = (job_is_cancelled(&job->job) && job->ret == 0)
414
- ? -ECANCELED : job->ret;
415
+ ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
416
+ ? -ECANCELED : job->job.ret;
417
job_unref(&job->job);
418
return ret;
419
}
420
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
421
assert(job_is_cancelled(&other_job->job));
422
block_job_finish_sync(other_job, NULL, NULL);
423
}
424
- block_job_finalize_single(other_job);
425
+ job_finalize_single(&other_job->job);
426
aio_context_release(ctx);
427
}
428
429
@@ -XXX,XX +XXX,XX @@ static int block_job_needs_finalize(BlockJob *job)
430
return !job->job.auto_finalize;
431
}
432
433
+static int block_job_finalize_single(BlockJob *job)
434
+{
435
+ return job_finalize_single(&job->job);
436
+}
437
+
438
static void block_job_do_finalize(BlockJob *job)
439
{
440
int rc;
441
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
442
if (!job_is_completed(&other_job->job)) {
443
return;
444
}
445
- assert(other_job->ret == 0);
446
+ assert(other_job->job.ret == 0);
447
}
448
449
block_job_txn_apply(txn, block_job_transition_to_pending, false);
450
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
451
return;
452
}
453
454
- block_job_do_dismiss(job);
455
+ job_do_dismiss(&job->job);
456
*jobptr = NULL;
457
}
458
459
void block_job_cancel(BlockJob *job, bool force)
460
{
461
if (job->job.status == JOB_STATUS_CONCLUDED) {
462
- block_job_do_dismiss(job);
463
+ job_do_dismiss(&job->job);
464
return;
465
}
466
block_job_cancel_async(job, force);
467
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
468
info->status = job->job.status;
469
info->auto_finalize = job->job.auto_finalize;
470
info->auto_dismiss = job->job.auto_dismiss;
471
- info->has_error = job->ret != 0;
472
- info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
473
+ info->has_error = job->job.ret != 0;
474
+ info->error = job->job.ret ? g_strdup(strerror(-job->job.ret)) : NULL;
475
return info;
476
}
477
478
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
479
return;
480
}
481
482
- if (job->ret < 0) {
483
- msg = strerror(-job->ret);
484
+ if (job->job.ret < 0) {
485
+ msg = strerror(-job->job.ret);
486
}
487
488
qapi_event_send_block_job_completed(job_type(&job->job),
489
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
490
}
491
492
job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
493
- flags, errp);
494
+ flags, cb, opaque, errp);
495
if (job == NULL) {
496
blk_unref(blk);
497
return NULL;
498
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
499
500
job->driver = driver;
501
job->blk = blk;
502
- job->cb = cb;
503
- job->opaque = opaque;
504
505
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
506
job->finalize_completed_notifier.notify = block_job_event_completed;
507
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
508
509
block_job_set_speed(job, speed, &local_err);
510
if (local_err) {
511
- block_job_early_fail(job);
512
+ job_early_fail(&job->job);
513
error_propagate(errp, local_err);
514
return NULL;
515
}
516
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
517
return job;
518
}
519
520
-void block_job_early_fail(BlockJob *job)
521
-{
522
- assert(job->job.status == JOB_STATUS_CREATED);
523
- block_job_decommission(job);
524
-}
525
-
526
void block_job_completed(BlockJob *job, int ret)
527
{
528
assert(job && job->txn && !job_is_completed(&job->job));
529
assert(blk_bs(job->blk)->job == job);
530
- job->ret = ret;
531
- block_job_update_rc(job);
532
- trace_block_job_completed(job, ret, job->ret);
533
- if (job->ret) {
534
+ job->job.ret = ret;
535
+ job_update_rc(&job->job);
536
+ trace_block_job_completed(job, ret, job->job.ret);
537
+ if (job->job.ret) {
538
block_job_completed_txn_abort(job);
539
} else {
540
block_job_completed_txn_success(job);
541
diff --git a/job.c b/job.c
542
index XXXXXXX..XXXXXXX 100644
543
--- a/job.c
544
+++ b/job.c
545
@@ -XXX,XX +XXX,XX @@ void job_state_transition(Job *job, JobStatus s1)
546
{
547
JobStatus s0 = job->status;
548
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
549
- trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
550
+ trace_job_state_transition(job, job->ret,
551
JobSTT[s0][s1] ? "allowed" : "disallowed",
552
JobStatus_str(s0), JobStatus_str(s1));
553
assert(JobSTT[s0][s1]);
554
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
555
}
556
557
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
558
- int flags, Error **errp)
559
+ int flags, BlockCompletionFunc *cb, void *opaque, Error **errp)
560
{
561
Job *job;
562
563
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
564
job->pause_count = 1;
565
job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
566
job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
567
+ job->cb = cb;
568
+ job->opaque = opaque;
569
570
notifier_list_init(&job->on_finalize_cancelled);
571
notifier_list_init(&job->on_finalize_completed);
572
@@ -XXX,XX +XXX,XX @@ void job_user_resume(Job *job, Error **errp)
573
job_resume(job);
574
}
575
576
+void job_do_dismiss(Job *job)
577
+{
578
+ assert(job);
579
+ job->busy = false;
580
+ job->paused = false;
581
+ job->deferred_to_main_loop = true;
582
+
583
+ /* TODO Don't assume it's a BlockJob */
584
+ block_job_txn_del_job((BlockJob*) job);
585
+
586
+ job_state_transition(job, JOB_STATUS_NULL);
587
+ job_unref(job);
588
+}
589
+
590
+void job_early_fail(Job *job)
591
+{
592
+ assert(job->status == JOB_STATUS_CREATED);
593
+ job_do_dismiss(job);
594
+}
595
+
596
+static void job_conclude(Job *job)
597
+{
598
+ job_state_transition(job, JOB_STATUS_CONCLUDED);
599
+ if (job->auto_dismiss || !job_started(job)) {
600
+ job_do_dismiss(job);
601
+ }
602
+}
603
+
604
+void job_update_rc(Job *job)
605
+{
606
+ if (!job->ret && job_is_cancelled(job)) {
607
+ job->ret = -ECANCELED;
608
+ }
609
+ if (job->ret) {
610
+ job_state_transition(job, JOB_STATUS_ABORTING);
611
+ }
612
+}
613
+
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
--
63
--
718
2.13.6
64
2.19.1
719
65
720
66
diff view generated by jsdifflib
1
The reference output file only works for file. 'qemu-img convert -p'
1
From: Cleber Rosa <crosa@redhat.com>
2
makes a lot more progress updates for NFS than for file, so disable the
3
test for NFS.
4
2
3
While testing the Python 3 changes which touch the 083 test, I noticed
4
that it would fail with qcow2. Expanding the testing, I noticed it
5
had nothing to do with the Python 3 changes, and in fact, it would not
6
pass on anything but raw:
7
8
raw: pass
9
bochs: not generic
10
cloop: not generic
11
parallels: fail
12
qcow: fail
13
qcow2: fail
14
qed: fail
15
vdi: fail
16
vhdx: fail
17
vmdk: fail
18
vpc: fail
19
luks: fail
20
21
The errors are a mixture I/O and "image not in xxx format", such as:
22
23
=== Check disconnect before data ===
24
25
Unexpected end-of-file before all bytes were read
26
-read failed: Input/output error
27
+can't open device nbd+tcp://127.0.0.1:PORT/foo: Could not open 'nbd://127.0.0.1:PORT/foo': Input/output error
28
29
=== Check disconnect after data ===
30
31
-read 512/512 bytes at offset 0
32
-512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
33
+can't open device nbd+tcp://127.0.0.1:PORT/foo: Image not in qcow format
34
35
I'm not aware if there's a quick fix, so, for the time being, it looks
36
like the honest approach is to make the test known to work on raw
37
only.
38
39
Signed-off-by: Cleber Rosa <crosa@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
40
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
41
---
8
tests/qemu-iotests/086 | 2 +-
42
tests/qemu-iotests/083 | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
43
1 file changed, 1 insertion(+), 1 deletion(-)
10
44
11
diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086
45
diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083
12
index XXXXXXX..XXXXXXX 100755
46
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/086
47
--- a/tests/qemu-iotests/083
14
+++ b/tests/qemu-iotests/086
48
+++ b/tests/qemu-iotests/083
15
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
49
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
50
. ./common.rc
16
. ./common.filter
51
. ./common.filter
17
52
18
_supported_fmt qcow2 raw
53
-_supported_fmt generic
19
-_supported_proto file nfs
54
+_supported_fmt raw
20
+_supported_proto file
55
_supported_proto nbd
21
_supported_os Linux
56
_supported_os Linux
22
57
23
function run_qemu_img()
24
--
58
--
25
2.13.6
59
2.19.1
26
60
27
61
diff view generated by jsdifflib
1
Since we introduced an explicit status to block job, BlockJob.completed
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
is redundant because it can be derived from the status. Remove the field
2
Reviewed-by: Alberto Garcia <berto@igalia.com>
3
from BlockJob and add a function to derive it from the status at the Job
3
---
4
level.
4
block/vpc.c | 2 ++
5
1 file changed, 2 insertions(+)
5
6
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
diff --git a/block/vpc.c b/block/vpc.c
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
---
10
include/block/blockjob.h | 3 ---
11
include/qemu/job.h | 3 +++
12
blockjob.c | 16 +++++++---------
13
job.c | 22 ++++++++++++++++++++++
14
qemu-img.c | 4 ++--
15
5 files changed, 34 insertions(+), 14 deletions(-)
16
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
index XXXXXXX..XXXXXXX 100644
8
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
9
--- a/block/vpc.c
20
+++ b/include/block/blockjob.h
10
+++ b/block/vpc.c
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
11
@@ -XXX,XX +XXX,XX @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags,
22
/** The opaque value that is passed to the completion function. */
23
void *opaque;
24
25
- /** True when job has reported completion by calling block_job_completed. */
26
- bool completed;
27
-
28
/** ret code passed to block_job_completed. */
29
int ret;
30
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/qemu/job.h
34
+++ b/include/qemu/job.h
35
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
36
/** Returns whether the job is scheduled for cancellation. */
37
bool job_is_cancelled(Job *job);
38
39
+/** Returns whether the job is in a completed state. */
40
+bool job_is_completed(Job *job);
41
+
42
/**
43
* Request @job to pause at the next pause point. Must be paired with
44
* job_resume(). If the job is supposed to be resumed by user action, call
45
diff --git a/blockjob.c b/blockjob.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/blockjob.c
48
+++ b/blockjob.c
49
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
50
51
job_pause(&job->job);
52
53
- while (!job->job.paused && !job->completed) {
54
+ while (!job->job.paused && !job_is_completed(&job->job)) {
55
block_job_drain(job);
56
}
12
}
57
13
58
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
14
qemu_co_mutex_init(&s->lock);
59
static void block_job_decommission(BlockJob *job)
15
+ qemu_opts_del(opts);
60
{
16
61
assert(job);
17
return 0;
62
- job->completed = true;
18
63
job->job.busy = false;
19
fail:
64
job->job.paused = false;
20
+ qemu_opts_del(opts);
65
job->job.deferred_to_main_loop = true;
21
qemu_vfree(s->pagetable);
66
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
22
#ifdef CACHE
67
23
g_free(s->pageentry_u8);
68
static int block_job_finalize_single(BlockJob *job)
69
{
70
- assert(job->completed);
71
+ assert(job_is_completed(&job->job));
72
73
/* Ensure abort is called for late-transactional failures */
74
block_job_update_rc(job);
75
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
76
/* block_job_drain calls block_job_enter, and it should be enough to
77
* induce progress until the job completes or moves to the main thread.
78
*/
79
- while (!job->job.deferred_to_main_loop && !job->completed) {
80
+ while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
81
block_job_drain(job);
82
}
83
- while (!job->completed) {
84
+ while (!job_is_completed(&job->job)) {
85
aio_poll(qemu_get_aio_context(), true);
86
}
87
ret = (job_is_cancelled(&job->job) && job->ret == 0)
88
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
89
while (!QLIST_EMPTY(&txn->jobs)) {
90
other_job = QLIST_FIRST(&txn->jobs);
91
ctx = blk_get_aio_context(other_job->blk);
92
- if (!other_job->completed) {
93
+ if (!job_is_completed(&other_job->job)) {
94
assert(job_is_cancelled(&other_job->job));
95
block_job_finish_sync(other_job, NULL, NULL);
96
}
97
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
98
* txn.
99
*/
100
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
101
- if (!other_job->completed) {
102
+ if (!job_is_completed(&other_job->job)) {
103
return;
104
}
105
assert(other_job->ret == 0);
106
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job)
107
108
void block_job_completed(BlockJob *job, int ret)
109
{
110
- assert(job && job->txn && !job->completed);
111
+ assert(job && job->txn && !job_is_completed(&job->job));
112
assert(blk_bs(job->blk)->job == job);
113
- job->completed = true;
114
job->ret = ret;
115
block_job_update_rc(job);
116
trace_block_job_completed(job, ret, job->ret);
117
diff --git a/job.c b/job.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/job.c
120
+++ b/job.c
121
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
122
return job->cancelled;
123
}
124
125
+bool job_is_completed(Job *job)
126
+{
127
+ switch (job->status) {
128
+ case JOB_STATUS_UNDEFINED:
129
+ case JOB_STATUS_CREATED:
130
+ case JOB_STATUS_RUNNING:
131
+ case JOB_STATUS_PAUSED:
132
+ case JOB_STATUS_READY:
133
+ case JOB_STATUS_STANDBY:
134
+ return false;
135
+ case JOB_STATUS_WAITING:
136
+ case JOB_STATUS_PENDING:
137
+ case JOB_STATUS_ABORTING:
138
+ case JOB_STATUS_CONCLUDED:
139
+ case JOB_STATUS_NULL:
140
+ return true;
141
+ default:
142
+ g_assert_not_reached();
143
+ }
144
+ return false;
145
+}
146
+
147
bool job_started(Job *job)
148
{
149
return job->co;
150
diff --git a/qemu-img.c b/qemu-img.c
151
index XXXXXXX..XXXXXXX 100644
152
--- a/qemu-img.c
153
+++ b/qemu-img.c
154
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
155
aio_poll(aio_context, true);
156
qemu_progress_print(job->len ?
157
((float)job->offset / job->len * 100.f) : 0.0f, 0);
158
- } while (!job->ready && !job->completed);
159
+ } while (!job->ready && !job_is_completed(&job->job));
160
161
- if (!job->completed) {
162
+ if (!job_is_completed(&job->job)) {
163
ret = block_job_complete_sync(job, errp);
164
} else {
165
ret = job->ret;
166
--
24
--
167
2.13.6
25
2.19.1
168
26
169
27
diff view generated by jsdifflib
1
While we already moved the state related to job pausing to Job, the
1
From: Peter Maydell <peter.maydell@linaro.org>
2
functions to do were still BlockJob only. This commit moves them over to
2
3
Job.
3
Taking the address of a field in a packed struct is a bad idea, because
4
4
it might not be actually aligned enough for that pointer type (and
5
thus cause a crash on dereference on some host architectures). Newer
6
versions of clang warn about this. Avoid the bug by not using the
7
"modify in place" byte swapping functions.
8
9
There are a few places where the in-place swap function is
10
used on something other than a packed struct field; we convert
11
those anyway, for consistency.
12
13
Patch produced with scripts/coccinelle/inplace-byteswaps.cocci.
14
15
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
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
---
18
---
9
include/block/blockjob.h | 32 -----------------
19
block/vhdx.h | 12 ++---
10
include/block/blockjob_int.h | 7 ++++
20
block/vhdx-endian.c | 118 ++++++++++++++++++++++----------------------
11
include/qemu/job.h | 37 ++++++++++++++++++++
21
block/vhdx-log.c | 4 +-
12
block/backup.c | 1 +
22
block/vhdx.c | 18 +++----
13
block/commit.c | 1 +
23
4 files changed, 76 insertions(+), 76 deletions(-)
14
block/mirror.c | 2 ++
24
15
block/stream.c | 1 +
25
diff --git a/block/vhdx.h b/block/vhdx.h
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
24
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
25
index XXXXXXX..XXXXXXX 100644
26
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/blockjob.h
27
--- a/block/vhdx.h
27
+++ b/include/block/blockjob.h
28
+++ b/block/vhdx.h
28
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
29
@@ -XXX,XX +XXX,XX @@ int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s,
29
bool force;
30
30
31
static inline void leguid_to_cpus(MSGUID *guid)
31
/**
32
{
32
- * Set to true if the job is paused by user. Can be unpaused with the
33
- le32_to_cpus(&guid->data1);
33
- * block-job-resume QMP command.
34
- le16_to_cpus(&guid->data2);
34
- */
35
- le16_to_cpus(&guid->data3);
35
- bool user_paused;
36
+ guid->data1 = le32_to_cpu(guid->data1);
36
-
37
+ guid->data2 = le16_to_cpu(guid->data2);
37
- /**
38
+ guid->data3 = le16_to_cpu(guid->data3);
38
* Set to true when the job is ready to be completed.
39
}
39
*/
40
40
bool ready;
41
static inline void cpu_to_leguids(MSGUID *guid)
41
@@ -XXX,XX +XXX,XX @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
42
{
42
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
43
- cpu_to_le32s(&guid->data1);
43
44
- cpu_to_le16s(&guid->data2);
44
/**
45
- cpu_to_le16s(&guid->data3);
45
- * block_job_user_pause:
46
+ guid->data1 = cpu_to_le32(guid->data1);
46
- * @job: The job to be paused.
47
+ guid->data2 = cpu_to_le16(guid->data2);
47
- *
48
+ guid->data3 = cpu_to_le16(guid->data3);
48
- * Asynchronously pause the specified job.
49
}
49
- * Do not allow a resume until a matching call to block_job_user_resume.
50
50
- */
51
void vhdx_header_le_import(VHDXHeader *h);
51
-void block_job_user_pause(BlockJob *job, Error **errp);
52
diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c
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
53
index XXXXXXX..XXXXXXX 100644
76
--- a/include/block/blockjob_int.h
54
--- a/block/vhdx-endian.c
77
+++ b/include/block/blockjob_int.h
55
+++ b/block/vhdx-endian.c
78
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
56
@@ -XXX,XX +XXX,XX @@ void vhdx_header_le_import(VHDXHeader *h)
79
void block_job_free(Job *job);
57
{
80
58
assert(h != NULL);
81
/**
59
82
+ * block_job_user_resume:
60
- le32_to_cpus(&h->signature);
83
+ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the
61
- le32_to_cpus(&h->checksum);
84
+ * iostatus when the user resumes @job.
62
- le64_to_cpus(&h->sequence_number);
85
+ */
63
+ h->signature = le32_to_cpu(h->signature);
86
+void block_job_user_resume(Job *job);
64
+ h->checksum = le32_to_cpu(h->checksum);
87
+
65
+ h->sequence_number = le64_to_cpu(h->sequence_number);
88
+/**
66
89
* block_job_yield:
67
leguid_to_cpus(&h->file_write_guid);
90
* @job: The job that calls the function.
68
leguid_to_cpus(&h->data_write_guid);
91
*
69
leguid_to_cpus(&h->log_guid);
92
diff --git a/include/qemu/job.h b/include/qemu/job.h
70
71
- le16_to_cpus(&h->log_version);
72
- le16_to_cpus(&h->version);
73
- le32_to_cpus(&h->log_length);
74
- le64_to_cpus(&h->log_offset);
75
+ h->log_version = le16_to_cpu(h->log_version);
76
+ h->version = le16_to_cpu(h->version);
77
+ h->log_length = le32_to_cpu(h->log_length);
78
+ h->log_offset = le64_to_cpu(h->log_offset);
79
}
80
81
void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h)
82
@@ -XXX,XX +XXX,XX @@ void vhdx_log_desc_le_import(VHDXLogDescriptor *d)
83
{
84
assert(d != NULL);
85
86
- le32_to_cpus(&d->signature);
87
- le64_to_cpus(&d->file_offset);
88
- le64_to_cpus(&d->sequence_number);
89
+ d->signature = le32_to_cpu(d->signature);
90
+ d->file_offset = le64_to_cpu(d->file_offset);
91
+ d->sequence_number = le64_to_cpu(d->sequence_number);
92
}
93
94
void vhdx_log_desc_le_export(VHDXLogDescriptor *d)
95
{
96
assert(d != NULL);
97
98
- cpu_to_le32s(&d->signature);
99
- cpu_to_le32s(&d->trailing_bytes);
100
- cpu_to_le64s(&d->leading_bytes);
101
- cpu_to_le64s(&d->file_offset);
102
- cpu_to_le64s(&d->sequence_number);
103
+ d->signature = cpu_to_le32(d->signature);
104
+ d->trailing_bytes = cpu_to_le32(d->trailing_bytes);
105
+ d->leading_bytes = cpu_to_le64(d->leading_bytes);
106
+ d->file_offset = cpu_to_le64(d->file_offset);
107
+ d->sequence_number = cpu_to_le64(d->sequence_number);
108
}
109
110
void vhdx_log_data_le_import(VHDXLogDataSector *d)
111
{
112
assert(d != NULL);
113
114
- le32_to_cpus(&d->data_signature);
115
- le32_to_cpus(&d->sequence_high);
116
- le32_to_cpus(&d->sequence_low);
117
+ d->data_signature = le32_to_cpu(d->data_signature);
118
+ d->sequence_high = le32_to_cpu(d->sequence_high);
119
+ d->sequence_low = le32_to_cpu(d->sequence_low);
120
}
121
122
void vhdx_log_data_le_export(VHDXLogDataSector *d)
123
{
124
assert(d != NULL);
125
126
- cpu_to_le32s(&d->data_signature);
127
- cpu_to_le32s(&d->sequence_high);
128
- cpu_to_le32s(&d->sequence_low);
129
+ d->data_signature = cpu_to_le32(d->data_signature);
130
+ d->sequence_high = cpu_to_le32(d->sequence_high);
131
+ d->sequence_low = cpu_to_le32(d->sequence_low);
132
}
133
134
void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr)
135
{
136
assert(hdr != NULL);
137
138
- le32_to_cpus(&hdr->signature);
139
- le32_to_cpus(&hdr->checksum);
140
- le32_to_cpus(&hdr->entry_length);
141
- le32_to_cpus(&hdr->tail);
142
- le64_to_cpus(&hdr->sequence_number);
143
- le32_to_cpus(&hdr->descriptor_count);
144
+ hdr->signature = le32_to_cpu(hdr->signature);
145
+ hdr->checksum = le32_to_cpu(hdr->checksum);
146
+ hdr->entry_length = le32_to_cpu(hdr->entry_length);
147
+ hdr->tail = le32_to_cpu(hdr->tail);
148
+ hdr->sequence_number = le64_to_cpu(hdr->sequence_number);
149
+ hdr->descriptor_count = le32_to_cpu(hdr->descriptor_count);
150
leguid_to_cpus(&hdr->log_guid);
151
- le64_to_cpus(&hdr->flushed_file_offset);
152
- le64_to_cpus(&hdr->last_file_offset);
153
+ hdr->flushed_file_offset = le64_to_cpu(hdr->flushed_file_offset);
154
+ hdr->last_file_offset = le64_to_cpu(hdr->last_file_offset);
155
}
156
157
void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr)
158
{
159
assert(hdr != NULL);
160
161
- cpu_to_le32s(&hdr->signature);
162
- cpu_to_le32s(&hdr->checksum);
163
- cpu_to_le32s(&hdr->entry_length);
164
- cpu_to_le32s(&hdr->tail);
165
- cpu_to_le64s(&hdr->sequence_number);
166
- cpu_to_le32s(&hdr->descriptor_count);
167
+ hdr->signature = cpu_to_le32(hdr->signature);
168
+ hdr->checksum = cpu_to_le32(hdr->checksum);
169
+ hdr->entry_length = cpu_to_le32(hdr->entry_length);
170
+ hdr->tail = cpu_to_le32(hdr->tail);
171
+ hdr->sequence_number = cpu_to_le64(hdr->sequence_number);
172
+ hdr->descriptor_count = cpu_to_le32(hdr->descriptor_count);
173
cpu_to_leguids(&hdr->log_guid);
174
- cpu_to_le64s(&hdr->flushed_file_offset);
175
- cpu_to_le64s(&hdr->last_file_offset);
176
+ hdr->flushed_file_offset = cpu_to_le64(hdr->flushed_file_offset);
177
+ hdr->last_file_offset = cpu_to_le64(hdr->last_file_offset);
178
}
179
180
181
@@ -XXX,XX +XXX,XX @@ void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr)
182
{
183
assert(hdr != NULL);
184
185
- le32_to_cpus(&hdr->signature);
186
- le32_to_cpus(&hdr->checksum);
187
- le32_to_cpus(&hdr->entry_count);
188
+ hdr->signature = le32_to_cpu(hdr->signature);
189
+ hdr->checksum = le32_to_cpu(hdr->checksum);
190
+ hdr->entry_count = le32_to_cpu(hdr->entry_count);
191
}
192
193
void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr)
194
{
195
assert(hdr != NULL);
196
197
- cpu_to_le32s(&hdr->signature);
198
- cpu_to_le32s(&hdr->checksum);
199
- cpu_to_le32s(&hdr->entry_count);
200
+ hdr->signature = cpu_to_le32(hdr->signature);
201
+ hdr->checksum = cpu_to_le32(hdr->checksum);
202
+ hdr->entry_count = cpu_to_le32(hdr->entry_count);
203
}
204
205
void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
206
@@ -XXX,XX +XXX,XX @@ void vhdx_region_entry_le_import(VHDXRegionTableEntry *e)
207
assert(e != NULL);
208
209
leguid_to_cpus(&e->guid);
210
- le64_to_cpus(&e->file_offset);
211
- le32_to_cpus(&e->length);
212
- le32_to_cpus(&e->data_bits);
213
+ e->file_offset = le64_to_cpu(e->file_offset);
214
+ e->length = le32_to_cpu(e->length);
215
+ e->data_bits = le32_to_cpu(e->data_bits);
216
}
217
218
void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
219
@@ -XXX,XX +XXX,XX @@ void vhdx_region_entry_le_export(VHDXRegionTableEntry *e)
220
assert(e != NULL);
221
222
cpu_to_leguids(&e->guid);
223
- cpu_to_le64s(&e->file_offset);
224
- cpu_to_le32s(&e->length);
225
- cpu_to_le32s(&e->data_bits);
226
+ e->file_offset = cpu_to_le64(e->file_offset);
227
+ e->length = cpu_to_le32(e->length);
228
+ e->data_bits = cpu_to_le32(e->data_bits);
229
}
230
231
232
@@ -XXX,XX +XXX,XX @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr)
233
{
234
assert(hdr != NULL);
235
236
- le64_to_cpus(&hdr->signature);
237
- le16_to_cpus(&hdr->entry_count);
238
+ hdr->signature = le64_to_cpu(hdr->signature);
239
+ hdr->entry_count = le16_to_cpu(hdr->entry_count);
240
}
241
242
void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr)
243
{
244
assert(hdr != NULL);
245
246
- cpu_to_le64s(&hdr->signature);
247
- cpu_to_le16s(&hdr->entry_count);
248
+ hdr->signature = cpu_to_le64(hdr->signature);
249
+ hdr->entry_count = cpu_to_le16(hdr->entry_count);
250
}
251
252
void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
253
@@ -XXX,XX +XXX,XX @@ void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e)
254
assert(e != NULL);
255
256
leguid_to_cpus(&e->item_id);
257
- le32_to_cpus(&e->offset);
258
- le32_to_cpus(&e->length);
259
- le32_to_cpus(&e->data_bits);
260
+ e->offset = le32_to_cpu(e->offset);
261
+ e->length = le32_to_cpu(e->length);
262
+ e->data_bits = le32_to_cpu(e->data_bits);
263
}
264
void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e)
265
{
266
assert(e != NULL);
267
268
cpu_to_leguids(&e->item_id);
269
- cpu_to_le32s(&e->offset);
270
- cpu_to_le32s(&e->length);
271
- cpu_to_le32s(&e->data_bits);
272
+ e->offset = cpu_to_le32(e->offset);
273
+ e->length = cpu_to_le32(e->length);
274
+ e->data_bits = cpu_to_le32(e->data_bits);
275
}
276
diff --git a/block/vhdx-log.c b/block/vhdx-log.c
93
index XXXXXXX..XXXXXXX 100644
277
index XXXXXXX..XXXXXXX 100644
94
--- a/include/qemu/job.h
278
--- a/block/vhdx-log.c
95
+++ b/include/qemu/job.h
279
+++ b/block/vhdx-log.c
96
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
280
@@ -XXX,XX +XXX,XX @@ static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc,
97
bool paused;
281
/* 8 + 4084 + 4 = 4096, 1 log sector */
98
282
memcpy(&desc->leading_bytes, data, 8);
99
/**
283
data += 8;
100
+ * Set to true if the job is paused by user. Can be unpaused with the
284
- cpu_to_le64s(&desc->leading_bytes);
101
+ * block-job-resume QMP command.
285
+ desc->leading_bytes = cpu_to_le64(desc->leading_bytes);
102
+ */
286
memcpy(sector->data, data, 4084);
103
+ bool user_paused;
287
data += 4084;
104
+
288
memcpy(&desc->trailing_bytes, data, 4);
105
+ /**
289
- cpu_to_le32s(&desc->trailing_bytes);
106
* Set to true if the job should cancel itself. The flag must
290
+ desc->trailing_bytes = cpu_to_le32(desc->trailing_bytes);
107
* always be tested just before toggling the busy flag from false
291
data += 4;
108
* to true. After a job has been cancelled, it should only yield
292
109
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
293
sector->sequence_high = (uint32_t) (seq >> 32);
110
*/
294
diff --git a/block/vhdx.c b/block/vhdx.c
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
295
index XXXXXXX..XXXXXXX 100644
156
--- a/block/backup.c
296
--- a/block/vhdx.c
157
+++ b/block/backup.c
297
+++ b/block/vhdx.c
158
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
298
@@ -XXX,XX +XXX,XX @@ uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset)
159
.instance_size = sizeof(BackupBlockJob),
299
160
.job_type = JOB_TYPE_BACKUP,
300
memset(buf + crc_offset, 0, sizeof(crc));
161
.free = block_job_free,
301
crc = crc32c(0xffffffff, buf, size);
162
+ .user_resume = block_job_user_resume,
302
- cpu_to_le32s(&crc);
163
.start = backup_run,
303
+ crc = cpu_to_le32(crc);
164
},
304
memcpy(buf + crc_offset, &crc, sizeof(crc));
165
.commit = backup_commit,
305
166
diff --git a/block/commit.c b/block/commit.c
306
return crc;
167
index XXXXXXX..XXXXXXX 100644
307
@@ -XXX,XX +XXX,XX @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
168
--- a/block/commit.c
308
goto exit;
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
}
309
}
217
310
218
- if (block_job_user_paused(job) && !force) {
311
- le32_to_cpus(&s->params.block_size);
219
+ if (job_user_paused(&job->job) && !force) {
312
- le32_to_cpus(&s->params.data_bits);
220
error_setg(errp, "The block job for device '%s' is currently paused",
313
+ s->params.block_size = le32_to_cpu(s->params.block_size);
221
device);
314
+ s->params.data_bits = le32_to_cpu(s->params.data_bits);
222
goto out;
315
223
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_pause(const char *device, Error **errp)
316
317
/* We now have the file parameters, so we can tell if this is a
318
@@ -XXX,XX +XXX,XX @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s)
319
goto exit;
224
}
320
}
225
321
226
trace_qmp_block_job_pause(job);
322
- le64_to_cpus(&s->virtual_disk_size);
227
- block_job_user_pause(job, errp);
323
- le32_to_cpus(&s->logical_sector_size);
228
+ job_user_pause(&job->job, errp);
324
- le32_to_cpus(&s->physical_sector_size);
229
aio_context_release(aio_context);
325
+ s->virtual_disk_size = le64_to_cpu(s->virtual_disk_size);
230
}
326
+ s->logical_sector_size = le32_to_cpu(s->logical_sector_size);
231
327
+ s->physical_sector_size = le32_to_cpu(s->physical_sector_size);
232
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_resume(const char *device, Error **errp)
328
329
if (s->params.block_size < VHDX_BLOCK_SIZE_MIN ||
330
s->params.block_size > VHDX_BLOCK_SIZE_MAX) {
331
@@ -XXX,XX +XXX,XX @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags,
332
/* endian convert, and verify populated BAT field file offsets against
333
* region table and log entries */
334
for (i = 0; i < s->bat_entries; i++) {
335
- le64_to_cpus(&s->bat[i]);
336
+ s->bat[i] = le64_to_cpu(s->bat[i]);
337
if (payblocks--) {
338
/* payload bat entries */
339
if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) ==
340
@@ -XXX,XX +XXX,XX @@ static int vhdx_create_new_metadata(BlockBackend *blk,
341
mt_file_params->block_size = cpu_to_le32(block_size);
342
if (type == VHDX_TYPE_FIXED) {
343
mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED;
344
- cpu_to_le32s(&mt_file_params->data_bits);
345
+ mt_file_params->data_bits = cpu_to_le32(mt_file_params->data_bits);
233
}
346
}
234
347
235
trace_qmp_block_job_resume(job);
348
vhdx_guid_generate(&mt_page83->page_83_data);
236
- block_job_user_resume(job, errp);
349
@@ -XXX,XX +XXX,XX @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
237
+ job_user_resume(&job->job, errp);
350
sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB);
238
aio_context_release(aio_context);
351
vhdx_update_bat_table_entry(blk_bs(blk), s, &sinfo, &unused, &unused,
239
}
352
block_state);
240
353
- cpu_to_le64s(&s->bat[sinfo.bat_idx]);
241
diff --git a/blockjob.c b/blockjob.c
354
+ s->bat[sinfo.bat_idx] = cpu_to_le64(s->bat[sinfo.bat_idx]);
242
index XXXXXXX..XXXXXXX 100644
355
sector_num += s->sectors_per_block;
243
--- a/blockjob.c
356
}
244
+++ b/blockjob.c
357
ret = blk_pwrite(blk, file_offset, s->bat, length, 0);
245
@@ -XXX,XX +XXX,XX @@ static void block_job_txn_del_job(BlockJob *job)
246
}
247
}
248
249
-/* Assumes the job_mutex is held */
250
-static bool job_timer_not_pending(Job *job)
251
-{
252
- return !timer_pending(&job->sleep_timer);
253
-}
254
-
255
-static void block_job_pause(BlockJob *job)
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
{
296
BlockJob *job = c->opaque;
297
- block_job_pause(job);
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
401
--- a/job.c
402
+++ b/job.c
403
@@ -XXX,XX +XXX,XX @@ void job_start(Job *job)
404
aio_co_enter(job->aio_context, job->co);
405
}
406
407
+/* Assumes the block_job_mutex is held */
408
+static bool job_timer_not_pending(Job *job)
409
+{
410
+ return !timer_pending(&job->sleep_timer);
411
+}
412
+
413
+void job_pause(Job *job)
414
+{
415
+ job->pause_count++;
416
+}
417
+
418
+void job_resume(Job *job)
419
+{
420
+ assert(job->pause_count > 0);
421
+ job->pause_count--;
422
+ if (job->pause_count) {
423
+ return;
424
+ }
425
+
426
+ /* kick only if no timer is pending */
427
+ job_enter_cond(job, job_timer_not_pending);
428
+}
429
+
430
+void job_user_pause(Job *job, Error **errp)
431
+{
432
+ if (job_apply_verb(job, JOB_VERB_PAUSE, errp)) {
433
+ return;
434
+ }
435
+ if (job->user_paused) {
436
+ error_setg(errp, "Job is already paused");
437
+ return;
438
+ }
439
+ job->user_paused = true;
440
+ job_pause(job);
441
+}
442
+
443
+bool job_user_paused(Job *job)
444
+{
445
+ return job->user_paused;
446
+}
447
+
448
+void job_user_resume(Job *job, Error **errp)
449
+{
450
+ assert(job);
451
+ if (!job->user_paused || job->pause_count <= 0) {
452
+ error_setg(errp, "Can't resume a job that was not paused");
453
+ return;
454
+ }
455
+ if (job_apply_verb(job, JOB_VERB_RESUME, errp)) {
456
+ return;
457
+ }
458
+ if (job->driver->user_resume) {
459
+ job->driver->user_resume(job);
460
+ }
461
+ job->user_paused = false;
462
+ job_resume(job);
463
+}
464
+
465
+
466
typedef struct {
467
Job *job;
468
JobDeferToMainLoopFn *fn;
469
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
470
index XXXXXXX..XXXXXXX 100644
471
--- a/tests/test-bdrv-drain.c
472
+++ b/tests/test-bdrv-drain.c
473
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
474
.job_driver = {
475
.instance_size = sizeof(TestBlockJob),
476
.free = block_job_free,
477
+ .user_resume = block_job_user_resume,
478
.start = test_job_start,
479
},
480
.complete = test_job_complete,
481
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
482
index XXXXXXX..XXXXXXX 100644
483
--- a/tests/test-blockjob-txn.c
484
+++ b/tests/test-blockjob-txn.c
485
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
486
.job_driver = {
487
.instance_size = sizeof(TestBlockJob),
488
.free = block_job_free,
489
+ .user_resume = block_job_user_resume,
490
.start = test_block_job_run,
491
},
492
};
493
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
494
index XXXXXXX..XXXXXXX 100644
495
--- a/tests/test-blockjob.c
496
+++ b/tests/test-blockjob.c
497
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
498
.job_driver = {
499
.instance_size = sizeof(BlockJob),
500
.free = block_job_free,
501
+ .user_resume = block_job_user_resume,
502
},
503
};
504
505
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
506
.job_driver = {
507
.instance_size = sizeof(CancelJob),
508
.free = block_job_free,
509
+ .user_resume = block_job_user_resume,
510
.start = cancel_job_start,
511
},
512
.complete = cancel_job_complete,
513
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
514
job_start(&job->job);
515
assert(job->job.status == JOB_STATUS_RUNNING);
516
517
- block_job_user_pause(job, &error_abort);
518
+ job_user_pause(&job->job, &error_abort);
519
block_job_enter(job);
520
assert(job->job.status == JOB_STATUS_PAUSED);
521
522
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
523
block_job_enter(job);
524
assert(job->job.status == JOB_STATUS_READY);
525
526
- block_job_user_pause(job, &error_abort);
527
+ job_user_pause(&job->job, &error_abort);
528
block_job_enter(job);
529
assert(job->job.status == JOB_STATUS_STANDBY);
530
531
--
358
--
532
2.13.6
359
2.19.1
533
360
534
361
diff view generated by jsdifflib
1
From: Peter Maydell <peter.maydell@linaro.org>
1
From: Peter Maydell <peter.maydell@linaro.org>
2
2
3
In commit 8b9ad56e9cbfd852a, we removed the code that could result
3
Taking the address of a field in a packed struct is a bad idea, because
4
in our getting to sd_prealloc()'s out_with_err_set label with a
4
it might not be actually aligned enough for that pointer type (and
5
NULL blk pointer. That makes the NULL check in the error-handling
5
thus cause a crash on dereference on some host architectures). Newer
6
path unnecessary, and Coverity gripes about it (CID 1390636).
6
versions of clang warn about this. Avoid the bug by not using the
7
Delete the redundant check.
7
"modify in place" byte swapping functions.
8
9
There are a few places where the in-place swap function is
10
used on something other than a packed struct field; we convert
11
those anyway, for consistency.
12
13
Patch produced with scripts/coccinelle/inplace-byteswaps.cocci.
14
15
There are other places where we take the address of a packed member
16
in this file for other purposes than passing it to a byteswap
17
function (all the calls to qemu_uuid_*()); we leave those for now.
8
18
9
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
19
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
20
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
22
---
12
block/sheepdog.c | 4 +---
23
block/vdi.c | 64 ++++++++++++++++++++++++++---------------------------
13
1 file changed, 1 insertion(+), 3 deletions(-)
24
1 file changed, 32 insertions(+), 32 deletions(-)
14
25
15
diff --git a/block/sheepdog.c b/block/sheepdog.c
26
diff --git a/block/vdi.c b/block/vdi.c
16
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
17
--- a/block/sheepdog.c
28
--- a/block/vdi.c
18
+++ b/block/sheepdog.c
29
+++ b/block/vdi.c
19
@@ -XXX,XX +XXX,XX @@ out:
30
@@ -XXX,XX +XXX,XX @@ typedef struct {
20
error_setg_errno(errp, -ret, "Can't pre-allocate");
31
21
}
32
static void vdi_header_to_cpu(VdiHeader *header)
22
out_with_err_set:
33
{
23
- if (blk) {
34
- le32_to_cpus(&header->signature);
24
- blk_unref(blk);
35
- le32_to_cpus(&header->version);
25
- }
36
- le32_to_cpus(&header->header_size);
26
+ blk_unref(blk);
37
- le32_to_cpus(&header->image_type);
27
g_free(buf);
38
- le32_to_cpus(&header->image_flags);
28
39
- le32_to_cpus(&header->offset_bmap);
29
return ret;
40
- le32_to_cpus(&header->offset_data);
41
- le32_to_cpus(&header->cylinders);
42
- le32_to_cpus(&header->heads);
43
- le32_to_cpus(&header->sectors);
44
- le32_to_cpus(&header->sector_size);
45
- le64_to_cpus(&header->disk_size);
46
- le32_to_cpus(&header->block_size);
47
- le32_to_cpus(&header->block_extra);
48
- le32_to_cpus(&header->blocks_in_image);
49
- le32_to_cpus(&header->blocks_allocated);
50
+ header->signature = le32_to_cpu(header->signature);
51
+ header->version = le32_to_cpu(header->version);
52
+ header->header_size = le32_to_cpu(header->header_size);
53
+ header->image_type = le32_to_cpu(header->image_type);
54
+ header->image_flags = le32_to_cpu(header->image_flags);
55
+ header->offset_bmap = le32_to_cpu(header->offset_bmap);
56
+ header->offset_data = le32_to_cpu(header->offset_data);
57
+ header->cylinders = le32_to_cpu(header->cylinders);
58
+ header->heads = le32_to_cpu(header->heads);
59
+ header->sectors = le32_to_cpu(header->sectors);
60
+ header->sector_size = le32_to_cpu(header->sector_size);
61
+ header->disk_size = le64_to_cpu(header->disk_size);
62
+ header->block_size = le32_to_cpu(header->block_size);
63
+ header->block_extra = le32_to_cpu(header->block_extra);
64
+ header->blocks_in_image = le32_to_cpu(header->blocks_in_image);
65
+ header->blocks_allocated = le32_to_cpu(header->blocks_allocated);
66
qemu_uuid_bswap(&header->uuid_image);
67
qemu_uuid_bswap(&header->uuid_last_snap);
68
qemu_uuid_bswap(&header->uuid_link);
69
@@ -XXX,XX +XXX,XX @@ static void vdi_header_to_cpu(VdiHeader *header)
70
71
static void vdi_header_to_le(VdiHeader *header)
72
{
73
- cpu_to_le32s(&header->signature);
74
- cpu_to_le32s(&header->version);
75
- cpu_to_le32s(&header->header_size);
76
- cpu_to_le32s(&header->image_type);
77
- cpu_to_le32s(&header->image_flags);
78
- cpu_to_le32s(&header->offset_bmap);
79
- cpu_to_le32s(&header->offset_data);
80
- cpu_to_le32s(&header->cylinders);
81
- cpu_to_le32s(&header->heads);
82
- cpu_to_le32s(&header->sectors);
83
- cpu_to_le32s(&header->sector_size);
84
- cpu_to_le64s(&header->disk_size);
85
- cpu_to_le32s(&header->block_size);
86
- cpu_to_le32s(&header->block_extra);
87
- cpu_to_le32s(&header->blocks_in_image);
88
- cpu_to_le32s(&header->blocks_allocated);
89
+ header->signature = cpu_to_le32(header->signature);
90
+ header->version = cpu_to_le32(header->version);
91
+ header->header_size = cpu_to_le32(header->header_size);
92
+ header->image_type = cpu_to_le32(header->image_type);
93
+ header->image_flags = cpu_to_le32(header->image_flags);
94
+ header->offset_bmap = cpu_to_le32(header->offset_bmap);
95
+ header->offset_data = cpu_to_le32(header->offset_data);
96
+ header->cylinders = cpu_to_le32(header->cylinders);
97
+ header->heads = cpu_to_le32(header->heads);
98
+ header->sectors = cpu_to_le32(header->sectors);
99
+ header->sector_size = cpu_to_le32(header->sector_size);
100
+ header->disk_size = cpu_to_le64(header->disk_size);
101
+ header->block_size = cpu_to_le32(header->block_size);
102
+ header->block_extra = cpu_to_le32(header->block_extra);
103
+ header->blocks_in_image = cpu_to_le32(header->blocks_in_image);
104
+ header->blocks_allocated = cpu_to_le32(header->blocks_allocated);
105
qemu_uuid_bswap(&header->uuid_image);
106
qemu_uuid_bswap(&header->uuid_last_snap);
107
qemu_uuid_bswap(&header->uuid_link);
30
--
108
--
31
2.13.6
109
2.19.1
32
110
33
111
diff view generated by jsdifflib
1
block_job_finish_sync() doesn't contain anything block job specific any
1
From: Alberto Garcia <berto@igalia.com>
2
more, so it can be moved to Job.
3
2
3
This is a static function with only one caller, so there's no need to
4
keep it. Inlining the code in quorum_compare() makes it much simpler.
5
6
Signed-off-by: Alberto Garcia <berto@igalia.com>
7
Reported-by: Markus Armbruster <armbru@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
9
---
7
include/qemu/job.h | 9 +++++++++
10
block/quorum.c | 24 +++++-------------------
8
block/commit.c | 6 +++---
11
1 file changed, 5 insertions(+), 19 deletions(-)
9
blockjob.c | 55 +++++++++---------------------------------------------
10
job.c | 28 +++++++++++++++++++++++++++
11
4 files changed, 49 insertions(+), 49 deletions(-)
12
12
13
diff --git a/include/qemu/job.h b/include/qemu/job.h
13
diff --git a/block/quorum.c b/block/quorum.c
14
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/qemu/job.h
15
--- a/block/quorum.c
16
+++ b/include/qemu/job.h
16
+++ b/block/quorum.c
17
@@ -XXX,XX +XXX,XX @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
17
@@ -XXX,XX +XXX,XX @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b)
18
*/
18
return true;
19
void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
20
21
+/**
22
+ * Synchronously finishes the given @job. If @finish is given, it is called to
23
+ * trigger completion or cancellation of the job.
24
+ *
25
+ * Returns 0 if the job is successfully completed, -ECANCELED if the job was
26
+ * cancelled before completing, and -errno in other error cases.
27
+ */
28
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
29
+
30
/* TODO To be removed from the public interface */
31
void job_state_transition(Job *job, JobStatus s1);
32
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
33
diff --git a/block/commit.c b/block/commit.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block/commit.c
36
+++ b/block/commit.c
37
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
38
blk_unref(s->top);
39
40
/* If there is more than one reference to the job (e.g. if called from
41
- * block_job_finish_sync()), block_job_completed() won't free it and
42
- * therefore the blockers on the intermediate nodes remain. This would
43
- * cause bdrv_set_backing_hd() to fail. */
44
+ * job_finish_sync()), block_job_completed() won't free it and therefore
45
+ * the blockers on the intermediate nodes remain. This would cause
46
+ * bdrv_set_backing_hd() to fail. */
47
block_job_remove_all_bdrv(bjob);
48
49
block_job_completed(&s->common, ret);
50
diff --git a/blockjob.c b/blockjob.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/blockjob.c
53
+++ b/blockjob.c
54
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
55
return rc;
56
}
19
}
57
20
58
-static int block_job_finish_sync(BlockJob *job,
21
-static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb,
59
- void (*finish)(BlockJob *, Error **errp),
22
- const char *fmt, ...)
60
- Error **errp)
61
-{
23
-{
62
- Error *local_err = NULL;
24
- va_list ap;
63
- int ret;
64
-
25
-
65
- assert(blk_bs(job->blk)->job == job);
26
- va_start(ap, fmt);
66
-
27
- fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ",
67
- job_ref(&job->job);
28
- acb->offset, acb->bytes);
68
-
29
- vfprintf(stderr, fmt, ap);
69
- if (finish) {
30
- fprintf(stderr, "\n");
70
- finish(job, &local_err);
31
- va_end(ap);
71
- }
32
- exit(1);
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
-}
33
-}
91
-
34
-
92
static void block_job_completed_txn_abort(BlockJob *job)
35
-static bool quorum_compare(QuorumAIOCB *acb,
36
- QEMUIOVector *a,
37
- QEMUIOVector *b)
38
+static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b)
93
{
39
{
94
AioContext *ctx;
40
BDRVQuorumState *s = acb->bs->opaque;
95
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
41
ssize_t offset;
96
ctx = blk_get_aio_context(other_job->blk);
42
@@ -XXX,XX +XXX,XX @@ static bool quorum_compare(QuorumAIOCB *acb,
97
if (!job_is_completed(&other_job->job)) {
43
if (s->is_blkverify) {
98
assert(job_is_cancelled(&other_job->job));
44
offset = qemu_iovec_compare(a, b);
99
- block_job_finish_sync(other_job, NULL, NULL);
45
if (offset != -1) {
100
+ job_finish_sync(&other_job->job, NULL, NULL);
46
- quorum_err(acb, "contents mismatch at offset %" PRIu64,
47
- acb->offset + offset);
48
+ fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64
49
+ " contents mismatch at offset %" PRIu64 "\n",
50
+ acb->offset, acb->bytes, acb->offset + offset);
51
+ exit(1);
101
}
52
}
102
job_finalize_single(&other_job->job);
53
return true;
103
aio_context_release(ctx);
104
@@ -XXX,XX +XXX,XX @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
105
}
106
107
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
108
- * used with block_job_finish_sync() without the need for (rather nasty)
109
- * function pointer casts there. */
110
-static void block_job_cancel_err(BlockJob *job, Error **errp)
111
+ * used with job_finish_sync() without the need for (rather nasty) function
112
+ * pointer casts there. */
113
+static void block_job_cancel_err(Job *job, Error **errp)
114
{
115
- block_job_cancel(job, false);
116
+ BlockJob *bjob = container_of(job, BlockJob, job);
117
+ assert(is_block_job(job));
118
+ block_job_cancel(bjob, false);
119
}
120
121
int block_job_cancel_sync(BlockJob *job)
122
{
123
- return block_job_finish_sync(job, &block_job_cancel_err, NULL);
124
+ return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
125
}
126
127
void block_job_cancel_sync_all(void)
128
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
129
}
54
}
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
--
55
--
181
2.13.6
56
2.19.1
182
57
183
58
diff view generated by jsdifflib
1
Instead of having a 'bool ready' in BlockJob, add a function that
1
From: Alberto Garcia <berto@igalia.com>
2
derives its value from the job status.
3
2
4
At the same time, this fixes the behaviour to match what the QAPI
3
The blkverify mode of Quorum can only be enabled if the number of
5
documentation promises for query-block-job: 'true if the job may be
4
children is exactly two and the value of vote-threshold is also two.
6
completed'. When the ready flag was introduced in commit ef6dbf1e46e,
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
5
10
Job transactions and manual job finalisation were introduced only later.
6
If the user tries to enable it but the other settings are incorrect
11
With these changes, jobs may stay around even after having completed
7
then QEMU simply prints an error message to stderr and carries on
12
(and they are not ready to be completed a second time), however their
8
disabling the blkverify setting.
13
patches forgot to reset the ready flag.
14
9
10
This patch makes quorum_open() fail and return an error in this case.
11
12
Signed-off-by: Alberto Garcia <berto@igalia.com>
13
Reported-by: Markus Armbruster <armbru@redhat.com>
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
17
---
15
---
18
include/block/blockjob.h | 5 -----
16
block/quorum.c | 13 ++++++-------
19
include/qemu/job.h | 3 +++
17
1 file changed, 6 insertions(+), 7 deletions(-)
20
blockjob.c | 3 +--
21
job.c | 22 ++++++++++++++++++++++
22
qemu-img.c | 2 +-
23
tests/test-blockjob.c | 2 +-
24
6 files changed, 28 insertions(+), 9 deletions(-)
25
18
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
19
diff --git a/block/quorum.c b/block/quorum.c
27
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
21
--- a/block/quorum.c
29
+++ b/include/block/blockjob.h
22
+++ b/block/quorum.c
30
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
23
@@ -XXX,XX +XXX,XX @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags,
31
/** The block device on which the job is operating. */
24
s->read_pattern = ret;
32
BlockBackend *blk;
25
33
26
if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) {
34
- /**
27
- /* is the driver in blkverify mode */
35
- * Set to true when the job is ready to be completed.
28
- if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) &&
36
- */
29
- s->num_children == 2 && s->threshold == 2) {
37
- bool ready;
30
- s->is_blkverify = true;
38
-
31
- } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) {
39
/** Status that is published by the query-block-jobs QMP API */
32
- fprintf(stderr, "blkverify mode is set by setting blkverify=on "
40
BlockDeviceIoStatus iostatus;
33
- "and using two files with vote_threshold=2\n");
41
34
+ s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false);
42
diff --git a/include/qemu/job.h b/include/qemu/job.h
35
+ if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) {
43
index XXXXXXX..XXXXXXX 100644
36
+ error_setg(&local_err, "blkverify=on can only be set if there are "
44
--- a/include/qemu/job.h
37
+ "exactly two files and vote-threshold is 2");
45
+++ b/include/qemu/job.h
38
+ ret = -EINVAL;
46
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job);
39
+ goto exit;
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
111
index XXXXXXX..XXXXXXX 100644
112
--- a/qemu-img.c
113
+++ b/qemu-img.c
114
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
115
aio_poll(aio_context, true);
116
qemu_progress_print(job->len ?
117
((float)job->offset / job->len * 100.f) : 0.0f, 0);
118
- } while (!job->ready && !job_is_completed(&job->job));
119
+ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
120
121
if (!job_is_completed(&job->job)) {
122
ret = job_complete_sync(&job->job, errp);
123
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
124
index XXXXXXX..XXXXXXX 100644
125
--- a/tests/test-blockjob.c
126
+++ b/tests/test-blockjob.c
127
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
128
goto defer;
129
}
40
}
130
41
131
- if (!s->common.ready && s->should_converge) {
42
s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE,
132
+ if (!job_is_ready(&s->common.job) && s->should_converge) {
133
block_job_event_ready(&s->common);
134
}
135
136
--
43
--
137
2.13.6
44
2.19.1
138
45
139
46
diff view generated by jsdifflib
1
This moves block_job_yield() to the Job layer.
1
From: Alberto Garcia <berto@igalia.com>
2
2
3
Signed-off-by: Alberto Garcia <berto@igalia.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
5
---
6
include/block/blockjob_int.h | 8 --------
6
tests/qemu-iotests/081 | 30 ++++++++++++++++++++++++++++++
7
include/qemu/job.h | 9 +++++++--
7
tests/qemu-iotests/081.out | 16 ++++++++++++++++
8
block/backup.c | 2 +-
8
2 files changed, 46 insertions(+)
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
9
15
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
10
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
11
index XXXXXXX..XXXXXXX 100755
12
--- a/tests/qemu-iotests/081
13
+++ b/tests/qemu-iotests/081
14
@@ -XXX,XX +XXX,XX @@ echo "== checking that quorum is broken =="
15
16
$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
17
18
+echo
19
+echo "== checking the blkverify mode with broken content =="
20
+
21
+quorum="driver=raw,file.driver=quorum,file.vote-threshold=2,file.blkverify=on"
22
+quorum="$quorum,file.children.0.file.filename=$TEST_DIR/1.raw"
23
+quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw"
24
+quorum="$quorum,file.children.0.driver=raw"
25
+quorum="$quorum,file.children.1.driver=raw"
26
+
27
+$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
28
+
29
+echo
30
+echo "== writing the same data to both files =="
31
+
32
+$QEMU_IO -c "write -P 0x32 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io
33
+$QEMU_IO -c "write -P 0x32 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io
34
+
35
+echo
36
+echo "== checking the blkverify mode with valid content =="
37
+
38
+$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io
39
+
40
+echo
41
+echo "== checking the blkverify mode with invalid settings =="
42
+
43
+quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw"
44
+quorum="$quorum,file.children.2.driver=raw"
45
+
46
+$QEMU_IO -c "open -o $quorum" | _filter_qemu_io
47
+
48
# success, all done
49
echo "*** done"
50
rm -f $seq.full
51
diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out
16
index XXXXXXX..XXXXXXX 100644
52
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/blockjob_int.h
53
--- a/tests/qemu-iotests/081.out
18
+++ b/include/block/blockjob_int.h
54
+++ b/tests/qemu-iotests/081.out
19
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job);
55
@@ -XXX,XX +XXX,XX @@ wrote 10485760/10485760 bytes at offset 0
20
void block_job_drain(Job *job);
56
21
57
== checking that quorum is broken ==
22
/**
58
read failed: Input/output error
23
- * block_job_yield:
24
- * @job: The job that calls the function.
25
- *
26
- * Yield the block job coroutine.
27
- */
28
-void block_job_yield(BlockJob *job);
29
-
30
-/**
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
+
59
+
47
+/**
60
+== checking the blkverify mode with broken content ==
48
+ * @job: The job that calls the function.
61
+quorum: offset=0 bytes=10485760 contents mismatch at offset 0
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
+
62
+
144
+ /* Check cancellation *before* setting busy = false, too! */
63
+== writing the same data to both files ==
145
+ if (job_is_cancelled(job)) {
64
+wrote 10485760/10485760 bytes at offset 0
146
+ return;
65
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
147
+ }
66
+wrote 10485760/10485760 bytes at offset 0
67
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
148
+
68
+
149
+ if (!job_should_pause(job)) {
69
+== checking the blkverify mode with valid content ==
150
+ job_do_yield(job, -1);
70
+read 10485760/10485760 bytes at offset 0
151
+ }
71
+10 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
152
+
72
+
153
+ job_pause_point(job);
73
+== checking the blkverify mode with invalid settings ==
154
+}
74
+can't open: blkverify=on can only be set if there are exactly two files and vote-threshold is 2
155
+
75
*** done
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
--
76
--
173
2.13.6
77
2.19.1
174
78
175
79
diff view generated by jsdifflib
1
This commit moves some core functions for dealing with the job coroutine
1
From: Alberto Garcia <berto@igalia.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
The blkverify mode of Quorum only works when the number of children is
4
exactly two, so any attempt to add a new one must return an error.
5
6
quorum_del_child() on the other hand doesn't need any additional check
7
because decreasing the number of children would make it go under the
8
vote threshold.
9
10
Signed-off-by: Alberto Garcia <berto@igalia.com>
11
Reported-by: Kevin Wolf <kwolf@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
13
---
9
include/block/blockjob.h | 40 --------
14
block/quorum.c | 8 ++++++++
10
include/block/blockjob_int.h | 26 -----
15
1 file changed, 8 insertions(+)
11
include/qemu/job.h | 76 +++++++++++++++
12
block/backup.c | 2 +-
13
block/commit.c | 4 +-
14
block/mirror.c | 22 ++---
15
block/replication.c | 2 +-
16
block/stream.c | 4 +-
17
blockdev.c | 8 +-
18
blockjob.c | 219 ++++++++-----------------------------------
19
job.c | 137 +++++++++++++++++++++++++++
20
tests/test-bdrv-drain.c | 38 ++++----
21
tests/test-blockjob-txn.c | 12 +--
22
tests/test-blockjob.c | 14 +--
23
14 files changed, 305 insertions(+), 299 deletions(-)
24
16
25
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
17
diff --git a/block/quorum.c b/block/quorum.c
26
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/blockjob.h
19
--- a/block/quorum.c
28
+++ b/include/block/blockjob.h
20
+++ b/block/quorum.c
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
21
@@ -XXX,XX +XXX,XX @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs,
30
BlockBackend *blk;
22
char indexstr[32];
31
32
/**
33
- * The coroutine that executes the job. If not NULL, it is
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;
23
int ret;
76
24
77
- /**
25
+ if (s->is_blkverify) {
78
- * Timer that is used by @block_job_sleep_ns. Accessed under
26
+ error_setg(errp, "Cannot add a child to a quorum in blkverify mode");
79
- * block_job_mutex (in blockjob.c).
80
- */
81
- QEMUTimer sleep_timer;
82
-
83
/** True if this job should automatically finalize itself */
84
bool auto_finalize;
85
86
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
87
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
88
89
/**
90
- * block_job_start:
91
- * @job: A job that has not yet been started.
92
- *
93
- * Begins execution of a block job.
94
- * Takes ownership of one reference to the job object.
95
- */
96
-void block_job_start(BlockJob *job);
97
-
98
-/**
99
* block_job_cancel:
100
* @job: The job to be canceled.
101
* @force: Quit a job without waiting for data to be in sync.
102
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
103
index XXXXXXX..XXXXXXX 100644
104
--- a/include/block/blockjob_int.h
105
+++ b/include/block/blockjob_int.h
106
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
107
/** Generic JobDriver callbacks and settings */
108
JobDriver job_driver;
109
110
- /** Mandatory: Entrypoint for the Coroutine. */
111
- CoroutineEntry *start;
112
-
113
/**
114
* Optional callback for job types whose completion must be triggered
115
* manually.
116
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
117
*/
118
void (*clean)(BlockJob *job);
119
120
- /**
121
- * If the callback is not NULL, it will be invoked when the job transitions
122
- * into the paused state. Paused jobs must not perform any asynchronous
123
- * I/O or event loop activity. This callback is used to quiesce jobs.
124
- */
125
- void coroutine_fn (*pause)(BlockJob *job);
126
-
127
- /**
128
- * If the callback is not NULL, it will be invoked when the job transitions
129
- * out of the paused state. Any asynchronous I/O or event loop activity
130
- * should be restarted from this callback.
131
- */
132
- void coroutine_fn (*resume)(BlockJob *job);
133
-
134
/*
135
* If the callback is not NULL, it will be invoked before the job is
136
* resumed in a new AioContext. This is the place to move any resources
137
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job);
138
void block_job_completed(BlockJob *job, int ret);
139
140
/**
141
- * block_job_pause_point:
142
- * @job: The job that is ready to pause.
143
- *
144
- * Pause now if block_job_pause() has been called. Block jobs that perform
145
- * lots of I/O must call this between requests so that the job can be paused.
146
- */
147
-void coroutine_fn block_job_pause_point(BlockJob *job);
148
-
149
-/**
150
* block_job_enter:
151
* @job: The job to enter.
152
*
153
diff --git a/include/qemu/job.h b/include/qemu/job.h
154
index XXXXXXX..XXXXXXX 100644
155
--- a/include/qemu/job.h
156
+++ b/include/qemu/job.h
157
@@ -XXX,XX +XXX,XX @@
158
159
#include "qapi/qapi-types-block-core.h"
160
#include "qemu/queue.h"
161
+#include "qemu/coroutine.h"
162
163
typedef struct JobDriver JobDriver;
164
165
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
166
AioContext *aio_context;
167
168
/**
169
+ * The coroutine that executes the job. If not NULL, it is reentered when
170
+ * busy is false and the job is cancelled.
171
+ */
172
+ Coroutine *co;
173
+
174
+ /**
175
+ * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in
176
+ * job.c).
177
+ */
178
+ QEMUTimer sleep_timer;
179
+
180
+ /**
181
+ * Counter for pause request. If non-zero, the block job is either paused,
182
+ * or if busy == true will pause itself as soon as possible.
183
+ */
184
+ int pause_count;
185
+
186
+ /**
187
+ * Set to false by the job while the coroutine has yielded and may be
188
+ * re-entered by block_job_enter(). There may still be I/O or event loop
189
+ * activity pending. Accessed under block_job_mutex (in blockjob.c).
190
+ */
191
+ bool busy;
192
+
193
+ /**
194
+ * Set to true by the job while it is in a quiescent state, where
195
+ * no I/O or event loop activity is pending.
196
+ */
197
+ bool paused;
198
+
199
+ /**
200
* Set to true if the job should cancel itself. The flag must
201
* always be tested just before toggling the busy flag from false
202
* to true. After a job has been cancelled, it should only yield
203
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
204
/** Enum describing the operation */
205
JobType job_type;
206
207
+ /** Mandatory: Entrypoint for the Coroutine. */
208
+ CoroutineEntry *start;
209
+
210
+ /**
211
+ * If the callback is not NULL, it will be invoked when the job transitions
212
+ * into the paused state. Paused jobs must not perform any asynchronous
213
+ * I/O or event loop activity. This callback is used to quiesce jobs.
214
+ */
215
+ void coroutine_fn (*pause)(Job *job);
216
+
217
+ /**
218
+ * If the callback is not NULL, it will be invoked when the job transitions
219
+ * out of the paused state. Any asynchronous I/O or event loop activity
220
+ * should be restarted from this callback.
221
+ */
222
+ void coroutine_fn (*resume)(Job *job);
223
+
224
/** Called when the job is freed */
225
void (*free)(Job *job);
226
};
227
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
228
*/
229
void job_unref(Job *job);
230
231
+/**
232
+ * Conditionally enter the job coroutine if the job is ready to run, not
233
+ * already busy and fn() returns true. fn() is called while under the job_lock
234
+ * critical section.
235
+ */
236
+void job_enter_cond(Job *job, bool(*fn)(Job *job));
237
+
238
+/**
239
+ * @job: A job that has not yet been started.
240
+ *
241
+ * Begins execution of a job.
242
+ * Takes ownership of one reference to the job object.
243
+ */
244
+void job_start(Job *job);
245
+
246
+/**
247
+ * @job: The job that is ready to pause.
248
+ *
249
+ * Pause now if job_pause() has been called. Jobs that perform lots of I/O
250
+ * must call this between requests so that the job can be paused.
251
+ */
252
+void coroutine_fn job_pause_point(Job *job);
253
+
254
+
255
/** Returns the JobType of a given Job. */
256
JobType job_type(const Job *job);
257
258
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
259
260
/* TODO To be removed from the public interface */
261
void job_state_transition(Job *job, JobStatus s1);
262
+void coroutine_fn job_do_yield(Job *job, uint64_t ns);
263
+bool job_should_pause(Job *job);
264
+bool job_started(Job *job);
265
266
#endif
267
diff --git a/block/backup.c b/block/backup.c
268
index XXXXXXX..XXXXXXX 100644
269
--- a/block/backup.c
270
+++ b/block/backup.c
271
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
272
.instance_size = sizeof(BackupBlockJob),
273
.job_type = JOB_TYPE_BACKUP,
274
.free = block_job_free,
275
+ .start = backup_run,
276
},
277
- .start = backup_run,
278
.commit = backup_commit,
279
.abort = backup_abort,
280
.clean = backup_clean,
281
diff --git a/block/commit.c b/block/commit.c
282
index XXXXXXX..XXXXXXX 100644
283
--- a/block/commit.c
284
+++ b/block/commit.c
285
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
286
.instance_size = sizeof(CommitBlockJob),
287
.job_type = JOB_TYPE_COMMIT,
288
.free = block_job_free,
289
+ .start = commit_run,
290
},
291
- .start = commit_run,
292
};
293
294
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
295
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
296
s->on_error = on_error;
297
298
trace_commit_start(bs, base, top, s);
299
- block_job_start(&s->common);
300
+ job_start(&s->common.job);
301
return;
302
303
fail:
304
diff --git a/block/mirror.c b/block/mirror.c
305
index XXXXXXX..XXXXXXX 100644
306
--- a/block/mirror.c
307
+++ b/block/mirror.c
308
@@ -XXX,XX +XXX,XX @@ static void mirror_iteration_done(MirrorOp *op, int ret)
309
g_free(op);
310
311
if (s->waiting_for_io) {
312
- qemu_coroutine_enter(s->common.co);
313
+ qemu_coroutine_enter(s->common.job.co);
314
}
315
}
316
317
@@ -XXX,XX +XXX,XX @@ static uint64_t coroutine_fn mirror_iteration(MirrorBlockJob *s)
318
mirror_wait_for_io(s);
319
}
320
321
- block_job_pause_point(&s->common);
322
+ job_pause_point(&s->common.job);
323
324
/* Find the number of consective dirty chunks following the first dirty
325
* one, and wait for in flight requests in them. */
326
@@ -XXX,XX +XXX,XX @@ static void mirror_throttle(MirrorBlockJob *s)
327
s->last_pause_ns = now;
328
block_job_sleep_ns(&s->common, 0);
329
} else {
330
- block_job_pause_point(&s->common);
331
+ job_pause_point(&s->common.job);
332
}
333
}
334
335
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
336
goto immediate_exit;
337
}
338
339
- block_job_pause_point(&s->common);
340
+ job_pause_point(&s->common.job);
341
342
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
343
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
344
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
345
block_job_enter(&s->common);
346
}
347
348
-static void mirror_pause(BlockJob *job)
349
+static void mirror_pause(Job *job)
350
{
351
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
352
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
353
354
mirror_wait_for_all_io(s);
355
}
356
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
357
.instance_size = sizeof(MirrorBlockJob),
358
.job_type = JOB_TYPE_MIRROR,
359
.free = block_job_free,
360
+ .start = mirror_run,
361
+ .pause = mirror_pause,
362
},
363
- .start = mirror_run,
364
.complete = mirror_complete,
365
- .pause = mirror_pause,
366
.attached_aio_context = mirror_attached_aio_context,
367
.drain = mirror_drain,
368
};
369
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
370
.instance_size = sizeof(MirrorBlockJob),
371
.job_type = JOB_TYPE_COMMIT,
372
.free = block_job_free,
373
+ .start = mirror_run,
374
+ .pause = mirror_pause,
375
},
376
- .start = mirror_run,
377
.complete = mirror_complete,
378
- .pause = mirror_pause,
379
.attached_aio_context = mirror_attached_aio_context,
380
.drain = mirror_drain,
381
};
382
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
383
}
384
385
trace_mirror_start(bs, s, opaque);
386
- block_job_start(&s->common);
387
+ job_start(&s->common.job);
388
return;
389
390
fail:
391
diff --git a/block/replication.c b/block/replication.c
392
index XXXXXXX..XXXXXXX 100644
393
--- a/block/replication.c
394
+++ b/block/replication.c
395
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
396
aio_context_release(aio_context);
397
return;
398
}
399
- block_job_start(job);
400
+ job_start(&job->job);
401
break;
402
default:
403
aio_context_release(aio_context);
404
diff --git a/block/stream.c b/block/stream.c
405
index XXXXXXX..XXXXXXX 100644
406
--- a/block/stream.c
407
+++ b/block/stream.c
408
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
409
.instance_size = sizeof(StreamBlockJob),
410
.job_type = JOB_TYPE_STREAM,
411
.free = block_job_free,
412
+ .start = stream_run,
413
},
414
- .start = stream_run,
415
};
416
417
void stream_start(const char *job_id, BlockDriverState *bs,
418
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
419
420
s->on_error = on_error;
421
trace_stream_start(bs, base, s);
422
- block_job_start(&s->common);
423
+ job_start(&s->common.job);
424
return;
425
426
fail:
427
diff --git a/blockdev.c b/blockdev.c
428
index XXXXXXX..XXXXXXX 100644
429
--- a/blockdev.c
430
+++ b/blockdev.c
431
@@ -XXX,XX +XXX,XX @@ static void drive_backup_commit(BlkActionState *common)
432
aio_context_acquire(aio_context);
433
434
assert(state->job);
435
- block_job_start(state->job);
436
+ job_start(&state->job->job);
437
438
aio_context_release(aio_context);
439
}
440
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_commit(BlkActionState *common)
441
aio_context_acquire(aio_context);
442
443
assert(state->job);
444
- block_job_start(state->job);
445
+ job_start(&state->job->job);
446
447
aio_context_release(aio_context);
448
}
449
@@ -XXX,XX +XXX,XX @@ void qmp_drive_backup(DriveBackup *arg, Error **errp)
450
BlockJob *job;
451
job = do_drive_backup(arg, NULL, errp);
452
if (job) {
453
- block_job_start(job);
454
+ job_start(&job->job);
455
}
456
}
457
458
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_backup(BlockdevBackup *arg, Error **errp)
459
BlockJob *job;
460
job = do_blockdev_backup(arg, NULL, errp);
461
if (job) {
462
- block_job_start(job);
463
+ job_start(&job->job);
464
}
465
}
466
467
diff --git a/blockjob.c b/blockjob.c
468
index XXXXXXX..XXXXXXX 100644
469
--- a/blockjob.c
470
+++ b/blockjob.c
471
@@ -XXX,XX +XXX,XX @@
472
#include "qemu/coroutine.h"
473
#include "qemu/timer.h"
474
475
-/* Right now, this mutex is only needed to synchronize accesses to job->busy
476
- * and job->sleep_timer, such as concurrent calls to block_job_do_yield and
477
- * block_job_enter. */
478
-static QemuMutex block_job_mutex;
479
-
480
-static void block_job_lock(void)
481
-{
482
- qemu_mutex_lock(&block_job_mutex);
483
-}
484
-
485
-static void block_job_unlock(void)
486
-{
487
- qemu_mutex_unlock(&block_job_mutex);
488
-}
489
-
490
-static void __attribute__((__constructor__)) block_job_init(void)
491
-{
492
- qemu_mutex_init(&block_job_mutex);
493
-}
494
-
495
static void block_job_event_cancelled(BlockJob *job);
496
static void block_job_event_completed(BlockJob *job, const char *msg);
497
static int block_job_event_pending(BlockJob *job);
498
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
499
500
/* Transactional group of block jobs */
501
struct BlockJobTxn {
502
@@ -XXX,XX +XXX,XX @@ static void block_job_txn_del_job(BlockJob *job)
503
}
504
}
505
506
-/* Assumes the block_job_mutex is held */
507
-static bool block_job_timer_pending(BlockJob *job)
508
-{
509
- return timer_pending(&job->sleep_timer);
510
-}
511
-
512
-/* Assumes the block_job_mutex is held */
513
-static bool block_job_timer_not_pending(BlockJob *job)
514
+/* Assumes the job_mutex is held */
515
+static bool job_timer_not_pending(Job *job)
516
{
517
- return !block_job_timer_pending(job);
518
+ return !timer_pending(&job->sleep_timer);
519
}
520
521
static void block_job_pause(BlockJob *job)
522
{
523
- job->pause_count++;
524
+ job->job.pause_count++;
525
}
526
527
static void block_job_resume(BlockJob *job)
528
{
529
- assert(job->pause_count > 0);
530
- job->pause_count--;
531
- if (job->pause_count) {
532
+ assert(job->job.pause_count > 0);
533
+ job->job.pause_count--;
534
+ if (job->job.pause_count) {
535
return;
536
}
537
538
/* kick only if no timer is pending */
539
- block_job_enter_cond(job, block_job_timer_not_pending);
540
+ job_enter_cond(&job->job, job_timer_not_pending);
541
}
542
543
static void block_job_attached_aio_context(AioContext *new_context,
544
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
545
block_job_detach_aio_context, bjob);
546
blk_unref(bjob->blk);
547
error_free(bjob->blocker);
548
- assert(!timer_pending(&bjob->sleep_timer));
549
+ assert(!timer_pending(&bjob->job.sleep_timer));
550
}
551
552
static void block_job_attached_aio_context(AioContext *new_context,
553
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
554
555
static void block_job_drain(BlockJob *job)
556
{
557
- /* If job is !job->busy this kicks it into the next pause point. */
558
+ /* If job is !job->job.busy this kicks it into the next pause point. */
559
block_job_enter(job);
560
561
blk_drain(job->blk);
562
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
563
564
block_job_pause(job);
565
566
- while (!job->paused && !job->completed) {
567
+ while (!job->job.paused && !job->completed) {
568
block_job_drain(job);
569
}
570
571
@@ -XXX,XX +XXX,XX @@ bool block_job_is_internal(BlockJob *job)
572
return (job->job.id == NULL);
573
}
574
575
-static bool block_job_started(BlockJob *job)
576
-{
577
- return job->co;
578
-}
579
-
580
const BlockJobDriver *block_job_driver(BlockJob *job)
581
{
582
return job->driver;
583
}
584
585
-/**
586
- * All jobs must allow a pause point before entering their job proper. This
587
- * ensures that jobs can be paused prior to being started, then resumed later.
588
- */
589
-static void coroutine_fn block_job_co_entry(void *opaque)
590
-{
591
- BlockJob *job = opaque;
592
-
593
- assert(job && job->driver && job->driver->start);
594
- block_job_pause_point(job);
595
- job->driver->start(job);
596
-}
597
-
598
static void block_job_sleep_timer_cb(void *opaque)
599
{
600
BlockJob *job = opaque;
601
@@ -XXX,XX +XXX,XX @@ static void block_job_sleep_timer_cb(void *opaque)
602
block_job_enter(job);
603
}
604
605
-void block_job_start(BlockJob *job)
606
-{
607
- assert(job && !block_job_started(job) && job->paused &&
608
- job->driver && job->driver->start);
609
- job->co = qemu_coroutine_create(block_job_co_entry, job);
610
- job->pause_count--;
611
- job->busy = true;
612
- job->paused = false;
613
- job_state_transition(&job->job, JOB_STATUS_RUNNING);
614
- bdrv_coroutine_enter(blk_bs(job->blk), job->co);
615
-}
616
-
617
static void block_job_decommission(BlockJob *job)
618
{
619
assert(job);
620
job->completed = true;
621
- job->busy = false;
622
- job->paused = false;
623
+ job->job.busy = false;
624
+ job->job.paused = false;
625
job->job.deferred_to_main_loop = true;
626
block_job_txn_del_job(job);
627
job_state_transition(&job->job, JOB_STATUS_NULL);
628
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
629
static void block_job_conclude(BlockJob *job)
630
{
631
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
632
- if (job->auto_dismiss || !block_job_started(job)) {
633
+ if (job->auto_dismiss || !job_started(&job->job)) {
634
block_job_do_dismiss(job);
635
}
636
}
637
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
638
}
639
640
/* Emit events only if we actually started */
641
- if (block_job_started(job)) {
642
+ if (job_started(&job->job)) {
643
if (job_is_cancelled(&job->job)) {
644
block_job_event_cancelled(job);
645
} else {
646
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
647
if (job->user_paused) {
648
/* Do not call block_job_enter here, the caller will handle it. */
649
job->user_paused = false;
650
- job->pause_count--;
651
+ job->job.pause_count--;
652
}
653
job->job.cancelled = true;
654
/* To prevent 'force == false' overriding a previous 'force == true' */
655
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
656
}
657
}
658
659
+/* Assumes the job_mutex is held */
660
+static bool job_timer_pending(Job *job)
661
+{
662
+ return timer_pending(&job->sleep_timer);
663
+}
664
+
665
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
666
{
667
int64_t old_speed = job->speed;
668
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
669
}
670
671
/* kick only if a timer is pending */
672
- block_job_enter_cond(job, block_job_timer_pending);
673
+ job_enter_cond(&job->job, job_timer_pending);
674
}
675
676
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
677
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
678
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
679
return;
680
}
681
- if (job->pause_count || job_is_cancelled(&job->job) ||
682
+ if (job->job.pause_count || job_is_cancelled(&job->job) ||
683
!job->driver->complete)
684
{
685
error_setg(errp, "The active block job '%s' cannot be completed",
686
@@ -XXX,XX +XXX,XX @@ bool block_job_user_paused(BlockJob *job)
687
void block_job_user_resume(BlockJob *job, Error **errp)
688
{
689
assert(job);
690
- if (!job->user_paused || job->pause_count <= 0) {
691
+ if (!job->user_paused || job->job.pause_count <= 0) {
692
error_setg(errp, "Can't resume a job that was not paused");
693
return;
694
}
695
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
696
return;
697
}
698
block_job_cancel_async(job, force);
699
- if (!block_job_started(job)) {
700
+ if (!job_started(&job->job)) {
701
block_job_completed(job, -ECANCELED);
702
} else if (job->job.deferred_to_main_loop) {
703
block_job_completed_txn_abort(job);
704
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
705
info->type = g_strdup(job_type_str(&job->job));
706
info->device = g_strdup(job->job.id);
707
info->len = job->len;
708
- info->busy = atomic_read(&job->busy);
709
- info->paused = job->pause_count > 0;
710
+ info->busy = atomic_read(&job->job.busy);
711
+ info->paused = job->job.pause_count > 0;
712
info->offset = job->offset;
713
info->speed = job->speed;
714
info->io_status = job->iostatus;
715
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
716
job->blk = blk;
717
job->cb = cb;
718
job->opaque = opaque;
719
- job->busy = false;
720
- job->paused = true;
721
- job->pause_count = 1;
722
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
723
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
724
- aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
725
+ aio_timer_init(qemu_get_aio_context(), &job->job.sleep_timer,
726
QEMU_CLOCK_REALTIME, SCALE_NS,
727
block_job_sleep_timer_cb, job);
728
729
@@ -XXX,XX +XXX,XX @@ void block_job_completed(BlockJob *job, int ret)
730
}
731
}
732
733
-static bool block_job_should_pause(BlockJob *job)
734
-{
735
- return job->pause_count > 0;
736
-}
737
-
738
-/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
739
- * Reentering the job coroutine with block_job_enter() before the timer has
740
- * expired is allowed and cancels the timer.
741
- *
742
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
743
- * called explicitly. */
744
-static void block_job_do_yield(BlockJob *job, uint64_t ns)
745
-{
746
- block_job_lock();
747
- if (ns != -1) {
748
- timer_mod(&job->sleep_timer, ns);
749
- }
750
- job->busy = false;
751
- block_job_unlock();
752
- qemu_coroutine_yield();
753
-
754
- /* Set by block_job_enter before re-entering the coroutine. */
755
- assert(job->busy);
756
-}
757
-
758
-void coroutine_fn block_job_pause_point(BlockJob *job)
759
-{
760
- assert(job && block_job_started(job));
761
-
762
- if (!block_job_should_pause(job)) {
763
- return;
764
- }
765
- if (job_is_cancelled(&job->job)) {
766
- return;
767
- }
768
-
769
- if (job->driver->pause) {
770
- job->driver->pause(job);
771
- }
772
-
773
- if (block_job_should_pause(job) && !job_is_cancelled(&job->job)) {
774
- JobStatus status = job->job.status;
775
- job_state_transition(&job->job, status == JOB_STATUS_READY
776
- ? JOB_STATUS_STANDBY
777
- : JOB_STATUS_PAUSED);
778
- job->paused = true;
779
- block_job_do_yield(job, -1);
780
- job->paused = false;
781
- job_state_transition(&job->job, status);
782
- }
783
-
784
- if (job->driver->resume) {
785
- job->driver->resume(job);
786
- }
787
-}
788
-
789
-/*
790
- * Conditionally enter a block_job pending a call to fn() while
791
- * under the block_job_lock critical section.
792
- */
793
-static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
794
-{
795
- if (!block_job_started(job)) {
796
- return;
797
- }
798
- if (job->job.deferred_to_main_loop) {
799
- return;
800
- }
801
-
802
- block_job_lock();
803
- if (job->busy) {
804
- block_job_unlock();
805
- return;
806
- }
807
-
808
- if (fn && !fn(job)) {
809
- block_job_unlock();
810
- return;
811
- }
812
-
813
- assert(!job->job.deferred_to_main_loop);
814
- timer_del(&job->sleep_timer);
815
- job->busy = true;
816
- block_job_unlock();
817
- aio_co_wake(job->co);
818
-}
819
-
820
void block_job_enter(BlockJob *job)
821
{
822
- block_job_enter_cond(job, NULL);
823
+ job_enter_cond(&job->job, NULL);
824
}
825
826
void block_job_sleep_ns(BlockJob *job, int64_t ns)
827
{
828
- assert(job->busy);
829
+ assert(job->job.busy);
830
831
/* Check cancellation *before* setting busy = false, too! */
832
if (job_is_cancelled(&job->job)) {
833
return;
834
}
835
836
- if (!block_job_should_pause(job)) {
837
- block_job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
838
+ if (!job_should_pause(&job->job)) {
839
+ job_do_yield(&job->job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
840
}
841
842
- block_job_pause_point(job);
843
+ job_pause_point(&job->job);
844
}
845
846
void block_job_yield(BlockJob *job)
847
{
848
- assert(job->busy);
849
+ assert(job->job.busy);
850
851
/* Check cancellation *before* setting busy = false, too! */
852
if (job_is_cancelled(&job->job)) {
853
return;
854
}
855
856
- if (!block_job_should_pause(job)) {
857
- block_job_do_yield(job, -1);
858
+ if (!job_should_pause(&job->job)) {
859
+ job_do_yield(&job->job, -1);
860
}
861
862
- block_job_pause_point(job);
863
+ job_pause_point(&job->job);
864
}
865
866
void block_job_iostatus_reset(BlockJob *job)
867
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
868
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
869
return;
870
}
871
- assert(job->user_paused && job->pause_count > 0);
872
+ assert(job->user_paused && job->job.pause_count > 0);
873
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
874
}
875
876
diff --git a/job.c b/job.c
877
index XXXXXXX..XXXXXXX 100644
878
--- a/job.c
879
+++ b/job.c
880
@@ -XXX,XX +XXX,XX @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
881
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
882
};
883
884
+/* Right now, this mutex is only needed to synchronize accesses to job->busy
885
+ * and job->sleep_timer, such as concurrent calls to job_do_yield and
886
+ * job_enter. */
887
+static QemuMutex job_mutex;
888
+
889
+static void job_lock(void)
890
+{
891
+ qemu_mutex_lock(&job_mutex);
892
+}
893
+
894
+static void job_unlock(void)
895
+{
896
+ qemu_mutex_unlock(&job_mutex);
897
+}
898
+
899
+static void __attribute__((__constructor__)) job_init(void)
900
+{
901
+ qemu_mutex_init(&job_mutex);
902
+}
903
+
904
/* TODO Make static once the whole state machine is in job.c */
905
void job_state_transition(Job *job, JobStatus s1)
906
{
907
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
908
return job->cancelled;
909
}
910
911
+bool job_started(Job *job)
912
+{
913
+ return job->co;
914
+}
915
+
916
+bool job_should_pause(Job *job)
917
+{
918
+ return job->pause_count > 0;
919
+}
920
+
921
Job *job_next(Job *job)
922
{
923
if (!job) {
924
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
925
job->id = g_strdup(job_id);
926
job->refcnt = 1;
927
job->aio_context = ctx;
928
+ job->busy = false;
929
+ job->paused = true;
930
+ job->pause_count = 1;
931
932
job_state_transition(job, JOB_STATUS_CREATED);
933
934
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
935
}
936
}
937
938
+void job_enter_cond(Job *job, bool(*fn)(Job *job))
939
+{
940
+ if (!job_started(job)) {
941
+ return;
942
+ }
943
+ if (job->deferred_to_main_loop) {
944
+ return;
27
+ return;
945
+ }
28
+ }
946
+
29
+
947
+ job_lock();
30
assert(s->num_children <= INT_MAX / sizeof(BdrvChild *));
948
+ if (job->busy) {
31
if (s->num_children == INT_MAX / sizeof(BdrvChild *) ||
949
+ job_unlock();
32
s->next_child_index == UINT_MAX) {
950
+ return;
33
@@ -XXX,XX +XXX,XX @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child,
951
+ }
34
return;
35
}
36
37
+ /* We know now that num_children > threshold, so blkverify must be false */
38
+ assert(!s->is_blkverify);
952
+
39
+
953
+ if (fn && !fn(job)) {
40
bdrv_drained_begin(bs);
954
+ job_unlock();
41
955
+ return;
42
/* We can safely remove this child now */
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
--
43
--
1239
2.13.6
44
2.19.1
1240
45
1241
46
diff view generated by jsdifflib
1
block_job_drain() contains a blk_drain() call which cannot be moved to
1
From: Alberto Garcia <berto@igalia.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
This patch tests that you can add and remove drives from a Quorum
4
using the x-blockdev-change command.
5
6
Signed-off-by: Alberto Garcia <berto@igalia.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
8
---
10
include/block/blockjob_int.h | 12 ++++++++++++
9
tests/qemu-iotests/081 | 86 ++++++++++++++++++++++++++++++++++++++
11
include/qemu/job.h | 13 +++++++++++++
10
tests/qemu-iotests/081.out | 54 ++++++++++++++++++++++++
12
block/backup.c | 1 +
11
2 files changed, 140 insertions(+)
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
12
23
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
13
diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081
14
index XXXXXXX..XXXXXXX 100755
15
--- a/tests/qemu-iotests/081
16
+++ b/tests/qemu-iotests/081
17
@@ -XXX,XX +XXX,XX @@ quorum="$quorum,file.children.2.driver=raw"
18
19
$QEMU_IO -c "open -o $quorum" | _filter_qemu_io
20
21
+echo
22
+echo "== dynamically adding a child to a quorum =="
23
+
24
+for verify in false true; do
25
+ run_qemu <<EOF
26
+ { "execute": "qmp_capabilities" }
27
+ { "execute": "blockdev-add",
28
+ "arguments": {
29
+ "driver": "quorum",
30
+ "node-name": "drive0-quorum",
31
+ "vote-threshold": 2,
32
+ "blkverify": ${verify},
33
+ "children": [
34
+ {
35
+ "driver": "$IMGFMT",
36
+ "file": {
37
+ "driver": "file",
38
+ "filename": "$TEST_DIR/1.raw"
39
+ }
40
+ },
41
+ {
42
+ "driver": "$IMGFMT",
43
+ "file": {
44
+ "driver": "file",
45
+ "filename": "$TEST_DIR/2.raw"
46
+ }
47
+ }
48
+ ]
49
+ }
50
+ }
51
+ { "execute": "blockdev-add",
52
+ "arguments": {
53
+ "node-name": "drive3",
54
+ "driver": "$IMGFMT",
55
+ "file": {
56
+ "driver": "file",
57
+ "filename": "$TEST_DIR/2.raw"
58
+ }
59
+ }
60
+ }
61
+ { "execute": "x-blockdev-change",
62
+ "arguments": { "parent": "drive0-quorum",
63
+ "node": "drive3" } }
64
+ { "execute": "quit" }
65
+EOF
66
+done
67
+
68
+echo
69
+echo "== dynamically removing a child from a quorum =="
70
+
71
+for verify in false true; do
72
+ for vote_threshold in 1 2; do
73
+ run_qemu <<EOF
74
+ { "execute": "qmp_capabilities" }
75
+ { "execute": "blockdev-add",
76
+ "arguments": {
77
+ "driver": "quorum",
78
+ "node-name": "drive0-quorum",
79
+ "vote-threshold": ${vote_threshold},
80
+ "blkverify": ${verify},
81
+ "children": [
82
+ {
83
+ "driver": "$IMGFMT",
84
+ "file": {
85
+ "driver": "file",
86
+ "filename": "$TEST_DIR/1.raw"
87
+ }
88
+ },
89
+ {
90
+ "driver": "$IMGFMT",
91
+ "file": {
92
+ "driver": "file",
93
+ "filename": "$TEST_DIR/2.raw"
94
+ }
95
+ }
96
+ ]
97
+ }
98
+ }
99
+ { "execute": "x-blockdev-change",
100
+ "arguments": { "parent": "drive0-quorum",
101
+ "child": "children.1" } }
102
+ { "execute": "quit" }
103
+EOF
104
+ done
105
+done
106
+
107
# success, all done
108
echo "*** done"
109
rm -f $seq.full
110
diff --git a/tests/qemu-iotests/081.out b/tests/qemu-iotests/081.out
24
index XXXXXXX..XXXXXXX 100644
111
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob_int.h
112
--- a/tests/qemu-iotests/081.out
26
+++ b/include/block/blockjob_int.h
113
+++ b/tests/qemu-iotests/081.out
27
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
114
@@ -XXX,XX +XXX,XX @@ read 10485760/10485760 bytes at offset 0
28
* If the callback is not NULL, it will be invoked when the job has to be
115
29
* synchronously cancelled or completed; it should drain BlockDriverStates
116
== checking the blkverify mode with invalid settings ==
30
* as required to ensure progress.
117
can't open: blkverify=on can only be set if there are exactly two files and vote-threshold is 2
31
+ *
32
+ * Block jobs must use the default implementation for job_driver.drain,
33
+ * which will in turn call this callback after doing generic block job
34
+ * stuff.
35
*/
36
void (*drain)(BlockJob *job);
37
};
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
+ */
47
+void block_job_drain(Job *job);
48
+
118
+
49
+/**
119
+== dynamically adding a child to a quorum ==
50
* block_job_yield:
120
+Testing:
51
* @job: The job that calls the function.
121
+QMP_VERSION
52
*
122
+{"return": {}}
53
diff --git a/include/qemu/job.h b/include/qemu/job.h
123
+{"return": {}}
54
index XXXXXXX..XXXXXXX 100644
124
+{"return": {}}
55
--- a/include/qemu/job.h
125
+{"return": {}}
56
+++ b/include/qemu/job.h
126
+{"return": {}}
57
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
127
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
58
*/
59
void (*user_resume)(Job *job);
60
61
+ /*
62
+ * If the callback is not NULL, it will be invoked when the job has to be
63
+ * synchronously cancelled or completed; it should drain any activities
64
+ * as required to ensure progress.
65
+ */
66
+ void (*drain)(Job *job);
67
+
128
+
68
/**
129
+Testing:
69
* If the callback is not NULL, it will be invoked when all the jobs
130
+QMP_VERSION
70
* belonging to the same transaction complete; or upon this job's
131
+{"return": {}}
71
@@ -XXX,XX +XXX,XX @@ bool job_user_paused(Job *job);
132
+{"return": {}}
72
*/
133
+{"return": {}}
73
void job_user_resume(Job *job, Error **errp);
134
+{"error": {"class": "GenericError", "desc": "Cannot add a child to a quorum in blkverify mode"}}
74
135
+{"return": {}}
75
+/*
136
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
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
+ }
212
+}
213
+
137
+
214
+
138
+
215
/**
139
+== dynamically removing a child from a quorum ==
216
* All jobs must allow a pause point before entering their job proper. This
140
+Testing:
217
* ensures that jobs can be paused prior to being started, then resumed later.
141
+QMP_VERSION
218
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
142
+{"return": {}}
219
index XXXXXXX..XXXXXXX 100644
143
+{"return": {}}
220
--- a/tests/test-bdrv-drain.c
144
+{"return": {}}
221
+++ b/tests/test-bdrv-drain.c
145
+{"return": {}}
222
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
146
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
223
.instance_size = sizeof(TestBlockJob),
147
+
224
.free = block_job_free,
148
+Testing:
225
.user_resume = block_job_user_resume,
149
+QMP_VERSION
226
+ .drain = block_job_drain,
150
+{"return": {}}
227
.start = test_job_start,
151
+{"return": {}}
228
},
152
+{"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
229
.complete = test_job_complete,
153
+{"return": {}}
230
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
154
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
231
index XXXXXXX..XXXXXXX 100644
155
+
232
--- a/tests/test-blockjob-txn.c
156
+Testing:
233
+++ b/tests/test-blockjob-txn.c
157
+QMP_VERSION
234
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
158
+{"return": {}}
235
.instance_size = sizeof(TestBlockJob),
159
+{"error": {"class": "GenericError", "desc": "blkverify=on can only be set if there are exactly two files and vote-threshold is 2"}}
236
.free = block_job_free,
160
+{"error": {"class": "GenericError", "desc": "Cannot find device=drive0-quorum nor node_name=drive0-quorum"}}
237
.user_resume = block_job_user_resume,
161
+{"return": {}}
238
+ .drain = block_job_drain,
162
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
239
.start = test_block_job_run,
163
+
240
},
164
+Testing:
241
};
165
+QMP_VERSION
242
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
166
+{"return": {}}
243
index XXXXXXX..XXXXXXX 100644
167
+{"return": {}}
244
--- a/tests/test-blockjob.c
168
+{"error": {"class": "GenericError", "desc": "The number of children cannot be lower than the vote threshold 2"}}
245
+++ b/tests/test-blockjob.c
169
+{"return": {}}
246
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
170
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
247
.instance_size = sizeof(BlockJob),
171
+
248
.free = block_job_free,
172
*** done
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
--
173
--
263
2.13.6
174
2.19.1
264
175
265
176
diff view generated by jsdifflib
1
This moves the top-level job completion and cancellation functions from
1
To fully change the read-only state of a node, we must not only change
2
BlockJob to Job.
2
bs->read_only, but also update bs->open_flags.
3
3
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Alberto Garcia <berto@igalia.com>
5
---
7
---
6
include/block/blockjob.h | 55 -----------------------------
8
block.c | 7 +++++++
7
include/block/blockjob_int.h | 18 ----------
9
1 file changed, 7 insertions(+)
8
include/qemu/job.h | 68 +++++++++++++++++++++++++++++------
9
block.c | 4 ++-
10
block/backup.c | 3 +-
11
block/commit.c | 6 ++--
12
block/mirror.c | 6 ++--
13
block/replication.c | 4 +--
14
block/stream.c | 2 +-
15
blockdev.c | 8 ++---
16
blockjob.c | 76 ---------------------------------------
17
job.c | 84 +++++++++++++++++++++++++++++++++++++++-----
18
qemu-img.c | 2 +-
19
tests/test-bdrv-drain.c | 5 ++-
20
tests/test-blockjob-txn.c | 14 ++++----
21
tests/test-blockjob.c | 21 ++++++-----
22
block/trace-events | 3 --
23
trace-events | 1 +
24
18 files changed, 171 insertions(+), 209 deletions(-)
25
10
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
27
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
29
+++ b/include/block/blockjob.h
30
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
31
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
32
33
/**
34
- * block_job_cancel:
35
- * @job: The job to be canceled.
36
- * @force: Quit a job without waiting for data to be in sync.
37
- *
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
+
178
/** Asynchronously complete the specified @job. */
179
-void job_complete(Job *job, Error **errp);;
180
+void job_complete(Job *job, Error **errp);
181
+
182
+/**
183
+ * Asynchronously cancel the specified @job. If @force is true, the job should
184
+ * be cancelled immediately without waiting for a consistent state.
185
+ */
186
+void job_cancel(Job *job, bool force);
187
+
188
+/**
189
+ * Cancels the specified job like job_cancel(), but may refuse to do so if the
190
+ * operation isn't meaningful in the current state of the job.
191
+ */
192
+void job_user_cancel(Job *job, bool force, Error **errp);
193
+
194
+/**
195
+ * Synchronously cancel the @job. The completion callback is called
196
+ * before the function returns. The job may actually complete
197
+ * instead of canceling itself; the circumstances under which this
198
+ * happens depend on the kind of job that is active.
199
+ *
200
+ * Returns the return value from the job if the job actually completed
201
+ * during the call, or -ECANCELED if it was canceled.
202
+ */
203
+int job_cancel_sync(Job *job);
204
+
205
+/** Synchronously cancels all jobs using job_cancel_sync(). */
206
+void job_cancel_sync_all(void);
207
+
208
+/**
209
+ * @job: The job to be completed.
210
+ * @errp: Error object which may be set by job_complete(); this is not
211
+ * necessarily set on every error, the job return value has to be
212
+ * checked as well.
213
+ *
214
+ * Synchronously complete the job. The completion callback is called before the
215
+ * function returns, unless it is NULL (which is permissible when using this
216
+ * function).
217
+ *
218
+ * Returns the return value from the job.
219
+ */
220
+int job_complete_sync(Job *job, Error **errp);
221
222
/**
223
* For a @job that has finished its work and is pending awaiting explicit
224
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
225
void job_state_transition(Job *job, JobStatus s1);
226
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
227
bool job_should_pause(Job *job);
228
-bool job_started(Job *job);
229
void job_do_dismiss(Job *job);
230
-void job_update_rc(Job *job);
231
-void job_cancel_async(Job *job, bool force);
232
-void job_completed_txn_abort(Job *job);
233
-void job_completed_txn_success(Job *job);
234
235
#endif
236
diff --git a/block.c b/block.c
11
diff --git a/block.c b/block.c
237
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
238
--- a/block.c
13
--- a/block.c
239
+++ b/block.c
14
+++ b/block.c
240
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
15
@@ -XXX,XX +XXX,XX @@ int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
241
242
void bdrv_close_all(void)
243
{
244
- block_job_cancel_sync_all();
245
+ /* TODO We do want to cancel all jobs instead of just block jobs on
246
+ * shutdown, but bdrv_close_all() isn't the right place any more. */
247
+ job_cancel_sync_all();
248
nbd_export_close_all();
249
250
/* Drop references from requests still in flight, such as canceled block
251
diff --git a/block/backup.c b/block/backup.c
252
index XXXXXXX..XXXXXXX 100644
253
--- a/block/backup.c
254
+++ b/block/backup.c
255
@@ -XXX,XX +XXX,XX @@ typedef struct {
256
257
static void backup_complete(Job *job, void *opaque)
258
{
259
- BlockJob *bjob = container_of(job, BlockJob, job);
260
BackupCompleteData *data = opaque;
261
262
- block_job_completed(bjob, data->ret);
263
+ job_completed(job, data->ret);
264
g_free(data);
265
}
266
267
diff --git a/block/commit.c b/block/commit.c
268
index XXXXXXX..XXXXXXX 100644
269
--- a/block/commit.c
270
+++ b/block/commit.c
271
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
272
blk_unref(s->top);
273
274
/* If there is more than one reference to the job (e.g. if called from
275
- * job_finish_sync()), block_job_completed() won't free it and therefore
276
- * the blockers on the intermediate nodes remain. This would cause
277
+ * job_finish_sync()), job_completed() won't free it and therefore the
278
+ * blockers on the intermediate nodes remain. This would cause
279
* bdrv_set_backing_hd() to fail. */
280
block_job_remove_all_bdrv(bjob);
281
282
- block_job_completed(&s->common, ret);
283
+ job_completed(job, ret);
284
g_free(data);
285
286
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
287
diff --git a/block/mirror.c b/block/mirror.c
288
index XXXXXXX..XXXXXXX 100644
289
--- a/block/mirror.c
290
+++ b/block/mirror.c
291
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
292
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
293
294
/* Make sure that the source BDS doesn't go away before we called
295
- * block_job_completed(). */
296
+ * job_completed(). */
297
bdrv_ref(src);
298
bdrv_ref(mirror_top_bs);
299
bdrv_ref(target_bs);
300
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
301
blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
302
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
303
304
- block_job_completed(&s->common, data->ret);
305
+ job_completed(job, data->ret);
306
307
g_free(data);
308
bdrv_drained_end(src);
309
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(Job *job, Error **errp)
310
}
16
}
311
17
312
s->should_complete = true;
18
bs->read_only = read_only;
313
- block_job_enter(&s->common);
19
+
314
+ job_enter(job);
20
+ if (read_only) {
315
}
21
+ bs->open_flags &= ~BDRV_O_RDWR;
316
22
+ } else {
317
static void mirror_pause(Job *job)
23
+ bs->open_flags |= BDRV_O_RDWR;
318
diff --git a/block/replication.c b/block/replication.c
24
+ }
319
index XXXXXXX..XXXXXXX 100644
25
+
320
--- a/block/replication.c
321
+++ b/block/replication.c
322
@@ -XXX,XX +XXX,XX @@ static void replication_close(BlockDriverState *bs)
323
replication_stop(s->rs, false, NULL);
324
}
325
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
326
- block_job_cancel_sync(s->active_disk->bs->job);
327
+ job_cancel_sync(&s->active_disk->bs->job->job);
328
}
329
330
if (s->mode == REPLICATION_MODE_SECONDARY) {
331
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
332
* disk, secondary disk in backup_job_completed().
333
*/
334
if (s->secondary_disk->bs->job) {
335
- block_job_cancel_sync(s->secondary_disk->bs->job);
336
+ job_cancel_sync(&s->secondary_disk->bs->job->job);
337
}
338
339
if (!failover) {
340
diff --git a/block/stream.c b/block/stream.c
341
index XXXXXXX..XXXXXXX 100644
342
--- a/block/stream.c
343
+++ b/block/stream.c
344
@@ -XXX,XX +XXX,XX @@ out:
345
}
346
347
g_free(s->backing_file_str);
348
- block_job_completed(&s->common, data->ret);
349
+ job_completed(job, data->ret);
350
g_free(data);
351
}
352
353
diff --git a/blockdev.c b/blockdev.c
354
index XXXXXXX..XXXXXXX 100644
355
--- a/blockdev.c
356
+++ b/blockdev.c
357
@@ -XXX,XX +XXX,XX @@ void blockdev_mark_auto_del(BlockBackend *blk)
358
aio_context_acquire(aio_context);
359
360
if (bs->job) {
361
- block_job_cancel(bs->job, false);
362
+ job_cancel(&bs->job->job, false);
363
}
364
365
aio_context_release(aio_context);
366
@@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(BlkActionState *common)
367
aio_context = bdrv_get_aio_context(state->bs);
368
aio_context_acquire(aio_context);
369
370
- block_job_cancel_sync(state->job);
371
+ job_cancel_sync(&state->job->job);
372
373
aio_context_release(aio_context);
374
}
375
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(BlkActionState *common)
376
aio_context = bdrv_get_aio_context(state->bs);
377
aio_context_acquire(aio_context);
378
379
- block_job_cancel_sync(state->job);
380
+ job_cancel_sync(&state->job->job);
381
382
aio_context_release(aio_context);
383
}
384
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
385
}
386
387
trace_qmp_block_job_cancel(job);
388
- block_job_user_cancel(job, force, errp);
389
+ job_user_cancel(&job->job, force, errp);
390
out:
391
aio_context_release(aio_context);
392
}
393
diff --git a/blockjob.c b/blockjob.c
394
index XXXXXXX..XXXXXXX 100644
395
--- a/blockjob.c
396
+++ b/blockjob.c
397
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
398
*jobptr = NULL;
399
}
400
401
-void block_job_cancel(BlockJob *job, bool force)
402
-{
403
- if (job->job.status == JOB_STATUS_CONCLUDED) {
404
- job_do_dismiss(&job->job);
405
- return;
406
- }
407
- job_cancel_async(&job->job, force);
408
- if (!job_started(&job->job)) {
409
- block_job_completed(job, -ECANCELED);
410
- } else if (job->job.deferred_to_main_loop) {
411
- job_completed_txn_abort(&job->job);
412
- } else {
413
- block_job_enter(job);
414
- }
415
-}
416
-
417
-void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
418
-{
419
- if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
420
- return;
421
- }
422
- block_job_cancel(job, force);
423
-}
424
-
425
-/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
426
- * used with job_finish_sync() without the need for (rather nasty) function
427
- * pointer casts there. */
428
-static void block_job_cancel_err(Job *job, Error **errp)
429
-{
430
- BlockJob *bjob = container_of(job, BlockJob, job);
431
- assert(is_block_job(job));
432
- block_job_cancel(bjob, false);
433
-}
434
-
435
-int block_job_cancel_sync(BlockJob *job)
436
-{
437
- return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
438
-}
439
-
440
-void block_job_cancel_sync_all(void)
441
-{
442
- BlockJob *job;
443
- AioContext *aio_context;
444
-
445
- while ((job = block_job_next(NULL))) {
446
- aio_context = blk_get_aio_context(job->blk);
447
- aio_context_acquire(aio_context);
448
- block_job_cancel_sync(job);
449
- aio_context_release(aio_context);
450
- }
451
-}
452
-
453
-int block_job_complete_sync(BlockJob *job, Error **errp)
454
-{
455
- return job_finish_sync(&job->job, job_complete, errp);
456
-}
457
-
458
void block_job_progress_update(BlockJob *job, uint64_t done)
459
{
460
job->offset += done;
461
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
462
return job;
463
}
464
465
-void block_job_completed(BlockJob *job, int ret)
466
-{
467
- assert(job && job->job.txn && !job_is_completed(&job->job));
468
- assert(blk_bs(job->blk)->job == job);
469
- job->job.ret = ret;
470
- job_update_rc(&job->job);
471
- trace_block_job_completed(job, ret, job->job.ret);
472
- if (job->job.ret) {
473
- job_completed_txn_abort(&job->job);
474
- } else {
475
- job_completed_txn_success(&job->job);
476
- }
477
-}
478
-
479
-void block_job_enter(BlockJob *job)
480
-{
481
- job_enter_cond(&job->job, NULL);
482
-}
483
-
484
void block_job_yield(BlockJob *job)
485
{
486
assert(job->job.busy);
487
diff --git a/job.c b/job.c
488
index XXXXXXX..XXXXXXX 100644
489
--- a/job.c
490
+++ b/job.c
491
@@ -XXX,XX +XXX,XX @@ bool job_is_completed(Job *job)
492
return false;
493
}
494
495
-bool job_started(Job *job)
496
+static bool job_started(Job *job)
497
{
498
return job->co;
499
}
500
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
501
}
502
503
/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
504
- * Reentering the job coroutine with block_job_enter() before the timer has
505
- * expired is allowed and cancels the timer.
506
+ * Reentering the job coroutine with job_enter() before the timer has expired
507
+ * is allowed and cancels the timer.
508
*
509
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
510
+ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
511
* called explicitly. */
512
void coroutine_fn job_do_yield(Job *job, uint64_t ns)
513
{
514
@@ -XXX,XX +XXX,XX @@ static void job_conclude(Job *job)
515
}
516
}
517
518
-void job_update_rc(Job *job)
519
+static void job_update_rc(Job *job)
520
{
521
if (!job->ret && job_is_cancelled(job)) {
522
job->ret = -ECANCELED;
523
@@ -XXX,XX +XXX,XX @@ static int job_finalize_single(Job *job)
524
return 0;
26
return 0;
525
}
27
}
526
28
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
+
567
+void job_cancel(Job *job, bool force)
568
+{
569
+ if (job->status == JOB_STATUS_CONCLUDED) {
570
+ job_do_dismiss(job);
571
+ return;
572
+ }
573
+ job_cancel_async(job, force);
574
+ if (!job_started(job)) {
575
+ job_completed(job, -ECANCELED);
576
+ } else if (job->deferred_to_main_loop) {
577
+ job_completed_txn_abort(job);
578
+ } else {
579
+ job_enter(job);
580
+ }
581
+}
582
+
583
+void job_user_cancel(Job *job, bool force, Error **errp)
584
+{
585
+ if (job_apply_verb(job, JOB_VERB_CANCEL, errp)) {
586
+ return;
587
+ }
588
+ job_cancel(job, force);
589
+}
590
+
591
+/* A wrapper around job_cancel() taking an Error ** parameter so it may be
592
+ * used with job_finish_sync() without the need for (rather nasty) function
593
+ * pointer casts there. */
594
+static void job_cancel_err(Job *job, Error **errp)
595
+{
596
+ job_cancel(job, false);
597
+}
598
+
599
+int job_cancel_sync(Job *job)
600
+{
601
+ return job_finish_sync(job, &job_cancel_err, NULL);
602
+}
603
+
604
+void job_cancel_sync_all(void)
605
+{
606
+ Job *job;
607
+ AioContext *aio_context;
608
+
609
+ while ((job = job_next(NULL))) {
610
+ aio_context = job->aio_context;
611
+ aio_context_acquire(aio_context);
612
+ job_cancel_sync(job);
613
+ aio_context_release(aio_context);
614
+ }
615
+}
616
+
617
+int job_complete_sync(Job *job, Error **errp)
618
+{
619
+ return job_finish_sync(job, job_complete, errp);
620
+}
621
+
622
void job_complete(Job *job, Error **errp)
623
{
624
/* Should not be reachable via external interface for internal jobs */
625
diff --git a/qemu-img.c b/qemu-img.c
626
index XXXXXXX..XXXXXXX 100644
627
--- a/qemu-img.c
628
+++ b/qemu-img.c
629
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
630
} while (!job->ready && !job_is_completed(&job->job));
631
632
if (!job_is_completed(&job->job)) {
633
- ret = block_job_complete_sync(job, errp);
634
+ ret = job_complete_sync(&job->job, errp);
635
} else {
636
ret = job->job.ret;
637
}
638
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
639
index XXXXXXX..XXXXXXX 100644
640
--- a/tests/test-bdrv-drain.c
641
+++ b/tests/test-bdrv-drain.c
642
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
643
644
static void test_job_completed(Job *job, void *opaque)
645
{
646
- BlockJob *bjob = container_of(job, BlockJob, job);
647
- block_job_completed(bjob, 0);
648
+ job_completed(job, 0);
649
}
650
651
static void coroutine_fn test_job_start(void *opaque)
652
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
653
g_assert_false(job->job.paused);
654
g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
655
656
- ret = block_job_complete_sync(job, &error_abort);
657
+ ret = job_complete_sync(&job->job, &error_abort);
658
g_assert_cmpint(ret, ==, 0);
659
660
blk_unref(blk_src);
661
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
662
index XXXXXXX..XXXXXXX 100644
663
--- a/tests/test-blockjob-txn.c
664
+++ b/tests/test-blockjob-txn.c
665
@@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque)
666
rc = -ECANCELED;
667
}
668
669
- block_job_completed(bjob, rc);
670
+ job_completed(job, rc);
671
bdrv_unref(bs);
672
}
673
674
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
675
job_start(&job->job);
676
677
if (expected == -ECANCELED) {
678
- block_job_cancel(job, false);
679
+ job_cancel(&job->job, false);
680
}
681
682
while (result == -EINPROGRESS) {
683
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
684
job_txn_unref(txn);
685
686
if (expected1 == -ECANCELED) {
687
- block_job_cancel(job1, false);
688
+ job_cancel(&job1->job, false);
689
}
690
if (expected2 == -ECANCELED) {
691
- block_job_cancel(job2, false);
692
+ job_cancel(&job2->job, false);
693
}
694
695
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
696
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
697
job_start(&job1->job);
698
job_start(&job2->job);
699
700
- block_job_cancel(job1, false);
701
+ job_cancel(&job1->job, false);
702
703
/* Now make job2 finish before the main loop kicks jobs. This simulates
704
* the race between a pending kick and another job completing.
705
*/
706
- block_job_enter(job2);
707
- block_job_enter(job2);
708
+ job_enter(&job2->job);
709
+ job_enter(&job2->job);
710
711
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
712
aio_poll(qemu_get_aio_context(), true);
713
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
714
index XXXXXXX..XXXXXXX 100644
715
--- a/tests/test-blockjob.c
716
+++ b/tests/test-blockjob.c
717
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
718
719
static void cancel_job_completed(Job *job, void *opaque)
720
{
721
- BlockJob *bjob = container_of(job, BlockJob, job);
722
CancelJob *s = opaque;
723
s->completed = true;
724
- block_job_completed(bjob, 0);
725
+ job_completed(job, 0);
726
}
727
728
static void cancel_job_complete(Job *job, Error **errp)
729
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
730
BlockBackend *blk = s->blk;
731
JobStatus sts = job->job.status;
732
733
- block_job_cancel_sync(job);
734
+ job_cancel_sync(&job->job);
735
if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
736
BlockJob *dummy = job;
737
block_job_dismiss(&dummy, &error_abort);
738
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
739
assert(job->job.status == JOB_STATUS_RUNNING);
740
741
job_user_pause(&job->job, &error_abort);
742
- block_job_enter(job);
743
+ job_enter(&job->job);
744
assert(job->job.status == JOB_STATUS_PAUSED);
745
746
cancel_common(s);
747
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
748
assert(job->job.status == JOB_STATUS_RUNNING);
749
750
s->should_converge = true;
751
- block_job_enter(job);
752
+ job_enter(&job->job);
753
assert(job->job.status == JOB_STATUS_READY);
754
755
cancel_common(s);
756
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
757
assert(job->job.status == JOB_STATUS_RUNNING);
758
759
s->should_converge = true;
760
- block_job_enter(job);
761
+ job_enter(&job->job);
762
assert(job->job.status == JOB_STATUS_READY);
763
764
job_user_pause(&job->job, &error_abort);
765
- block_job_enter(job);
766
+ job_enter(&job->job);
767
assert(job->job.status == JOB_STATUS_STANDBY);
768
769
cancel_common(s);
770
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
771
assert(job->job.status == JOB_STATUS_RUNNING);
772
773
s->should_converge = true;
774
- block_job_enter(job);
775
+ job_enter(&job->job);
776
assert(job->job.status == JOB_STATUS_READY);
777
778
job_complete(&job->job, &error_abort);
779
- block_job_enter(job);
780
+ job_enter(&job->job);
781
while (!s->completed) {
782
aio_poll(qemu_get_aio_context(), true);
783
}
784
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
785
assert(job->job.status == JOB_STATUS_RUNNING);
786
787
s->should_converge = true;
788
- block_job_enter(job);
789
+ job_enter(&job->job);
790
assert(job->job.status == JOB_STATUS_READY);
791
792
job_complete(&job->job, &error_abort);
793
- block_job_enter(job);
794
+ job_enter(&job->job);
795
while (!s->completed) {
796
aio_poll(qemu_get_aio_context(), true);
797
}
798
diff --git a/block/trace-events b/block/trace-events
799
index XXXXXXX..XXXXXXX 100644
800
--- a/block/trace-events
801
+++ b/block/trace-events
802
@@ -XXX,XX +XXX,XX @@
803
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
804
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
805
806
-# blockjob.c
807
-block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
808
-
809
# block/block-backend.c
810
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
811
blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
812
diff --git a/trace-events b/trace-events
813
index XXXXXXX..XXXXXXX 100644
814
--- a/trace-events
815
+++ b/trace-events
816
@@ -XXX,XX +XXX,XX @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe
817
# job.c
818
job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
819
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
820
+job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
821
822
### Guest events, keep at bottom
823
824
--
29
--
825
2.13.6
30
2.19.1
826
31
827
32
diff view generated by jsdifflib
1
This moves BlockJob.status and the closely related functions
1
If a management application builds the block graph node by node, the
2
(block_)job_state_transition() and (block_)job_apply_verb to Job. The
2
protocol layer doesn't inherit its read-only option from the format
3
two QAPI enums are renamed to JobStatus and JobVerb.
3
layer any more, so it must be set explicitly.
4
5
Backing files should work on read-only storage, but at the same time, a
6
block job like commit should be able to reopen them read-write if they
7
are on read-write storage. However, without option inheritance, reopen
8
only changes the read-only option for the root node (typically the
9
format layer), but not the protocol layer, so reopening fails (the
10
format layer wants to get write permissions, but the protocol layer is
11
still read-only).
12
13
A simple workaround for the problem in the management tool would be to
14
open the protocol layer always read-write and to make only the format
15
layer read-only for backing files. However, sometimes the file is
16
actually stored on read-only storage and we don't know whether the image
17
can be opened read-write (for example, for NBD it depends on the server
18
we're trying to connect to). This adds an option that makes QEMU try to
19
open the image read-write, but allows it to degrade to a read-only mode
20
without returning an error.
21
22
The documentation for this option is consciously phrased in a way that
23
allows QEMU to switch to a better model eventually: Instead of trying
24
when the image is first opened, making the read-only flag dynamic and
25
changing it automatically whenever the first BLK_PERM_WRITE user is
26
attached or the last one is detached would be much more useful
27
behaviour.
28
29
Unfortunately, this more useful behaviour is also a lot harder to
30
implement, and libvirt needs a solution now before it can switch to
31
-blockdev, so let's start with this easier approach for now.
32
33
Instead of adding a new auto-read-only option, turning the existing
34
read-only into an enum (with a bool alternate for compatibility) was
35
considered, but it complicated the implementation to the point that it
36
didn't seem to be worth it.
4
37
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
38
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>
39
Reviewed-by: Eric Blake <eblake@redhat.com>
9
---
40
---
10
qapi/block-core.json | 16 ++++----
41
qapi/block-core.json | 7 +++++++
11
include/block/blockjob.h | 3 --
42
include/block/block.h | 2 ++
12
include/qemu/job.h | 13 ++++++
43
block.c | 17 +++++++++++++++++
13
blockjob.c | 102 +++++++++++------------------------------------
44
block/vvfat.c | 1 +
14
job.c | 56 ++++++++++++++++++++++++++
45
blockdev.c | 2 +-
15
tests/test-blockjob.c | 39 +++++++++---------
46
5 files changed, 28 insertions(+), 1 deletion(-)
16
block/trace-events | 2 -
17
trace-events | 4 ++
18
8 files changed, 123 insertions(+), 112 deletions(-)
19
47
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
48
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
49
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
50
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
51
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
52
@@ -XXX,XX +XXX,XX @@
25
'data': ['commit', 'stream', 'mirror', 'backup'] }
53
# either generally or in certain configurations. In this case,
26
54
# the default value does not work and the option must be
27
##
55
# specified explicitly.
28
-# @BlockJobVerb:
56
+# @auto-read-only: if true and @read-only is false, QEMU may automatically
29
+# @JobVerb:
57
+# decide not to open the image read-write as requested, but
30
#
58
+# fall back to read-only instead (and switch between the modes
31
-# Represents command verbs that can be applied to a blockjob.
59
+# later), e.g. depending on whether the image file is writable
32
+# Represents command verbs that can be applied to a job.
60
+# or whether a writing user is attached to the node
33
#
61
+# (default: false, since 3.1)
34
# @cancel: see @block-job-cancel
62
# @detect-zeroes: detect and optimize zero writes (Since 2.1)
35
#
63
# (default: off)
64
# @force-share: force share all permission on added nodes.
36
@@ -XXX,XX +XXX,XX @@
65
@@ -XXX,XX +XXX,XX @@
37
#
66
'*discard': 'BlockdevDiscardOptions',
38
# Since: 2.12
67
'*cache': 'BlockdevCacheOptions',
39
##
68
'*read-only': 'bool',
40
-{ 'enum': 'BlockJobVerb',
69
+ '*auto-read-only': 'bool',
41
+{ 'enum': 'JobVerb',
70
'*force-share': 'bool',
42
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
71
'*detect-zeroes': 'BlockdevDetectZeroesOptions' },
43
'finalize' ] }
72
'discriminator': 'driver',
44
73
diff --git a/include/block/block.h b/include/block/block.h
45
##
46
-# @BlockJobStatus:
47
+# @JobStatus:
48
#
49
-# Indicates the present state of a given blockjob in its lifetime.
50
+# Indicates the present state of a given job in its lifetime.
51
#
52
# @undefined: Erroneous, default state. Should not ever be visible.
53
#
54
@@ -XXX,XX +XXX,XX @@
55
#
56
# Since: 2.12
57
##
58
-{ 'enum': 'BlockJobStatus',
59
+{ 'enum': 'JobStatus',
60
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
61
'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
62
63
@@ -XXX,XX +XXX,XX @@
64
'data': {'type': 'str', 'device': 'str', 'len': 'int',
65
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
66
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
67
- 'status': 'BlockJobStatus',
68
+ 'status': 'JobStatus',
69
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
70
'*error': 'str' } }
71
72
@@ -XXX,XX +XXX,XX @@
73
# QEMU 2.12+ job lifetime management semantics.
74
#
75
# This command will refuse to operate on any job that has not yet reached
76
-# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
77
+# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the
78
# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
79
# to be used as appropriate.
80
#
81
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
82
index XXXXXXX..XXXXXXX 100644
74
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob.h
75
--- a/include/block/block.h
84
+++ b/include/block/blockjob.h
76
+++ b/include/block/block.h
85
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
77
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
86
*/
78
select an appropriate protocol driver,
87
QEMUTimer sleep_timer;
79
ignoring the format layer */
88
80
#define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */
89
- /** Current state; See @BlockJobStatus for details. */
81
+#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening read-write fails */
90
- BlockJobStatus status;
82
91
-
83
#define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH)
92
/** True if this job should automatically finalize itself */
84
93
bool auto_finalize;
85
@@ -XXX,XX +XXX,XX @@ typedef struct HDGeometry {
94
86
#define BDRV_OPT_CACHE_DIRECT "cache.direct"
95
diff --git a/include/qemu/job.h b/include/qemu/job.h
87
#define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush"
88
#define BDRV_OPT_READ_ONLY "read-only"
89
+#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only"
90
#define BDRV_OPT_DISCARD "discard"
91
#define BDRV_OPT_FORCE_SHARE "force-share"
92
93
diff --git a/block.c b/block.c
96
index XXXXXXX..XXXXXXX 100644
94
index XXXXXXX..XXXXXXX 100644
97
--- a/include/qemu/job.h
95
--- a/block.c
98
+++ b/include/qemu/job.h
96
+++ b/block.c
99
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
97
@@ -XXX,XX +XXX,XX @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options,
100
/** The type of this job. */
98
101
const JobDriver *driver;
99
/* Inherit the read-only option from the parent if it's not set */
102
100
qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY);
103
+ /** Current state; See @JobStatus for details. */
101
+ qdict_copy_default(child_options, parent_options, BDRV_OPT_AUTO_READ_ONLY);
104
+ JobStatus status;
102
103
/* Our block drivers take care to send flushes and respect unmap policy,
104
* so we can default to enable both on lower layers regardless of the
105
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_options(int *child_flags, QDict *child_options,
106
107
/* backing files always opened read-only */
108
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on");
109
+ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
110
flags &= ~BDRV_O_COPY_ON_READ;
111
112
/* snapshot=on is handled on the top layer */
113
@@ -XXX,XX +XXX,XX @@ static void update_flags_from_options(int *flags, QemuOpts *opts)
114
*flags |= BDRV_O_RDWR;
115
}
116
117
+ assert(qemu_opt_find(opts, BDRV_OPT_AUTO_READ_ONLY));
118
+ if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) {
119
+ *flags |= BDRV_O_AUTO_RDONLY;
120
+ }
121
}
122
123
static void update_options_from_flags(QDict *options, int flags)
124
@@ -XXX,XX +XXX,XX @@ static void update_options_from_flags(QDict *options, int flags)
125
if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) {
126
qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR));
127
}
128
+ if (!qdict_haskey(options, BDRV_OPT_AUTO_READ_ONLY)) {
129
+ qdict_put_bool(options, BDRV_OPT_AUTO_READ_ONLY,
130
+ flags & BDRV_O_AUTO_RDONLY);
131
+ }
132
}
133
134
static void bdrv_assign_node_name(BlockDriverState *bs,
135
@@ -XXX,XX +XXX,XX @@ QemuOptsList bdrv_runtime_opts = {
136
.type = QEMU_OPT_BOOL,
137
.help = "Node is opened in read-only mode",
138
},
139
+ {
140
+ .name = BDRV_OPT_AUTO_READ_ONLY,
141
+ .type = QEMU_OPT_BOOL,
142
+ .help = "Node can become read-only if opening read-write fails",
143
+ },
144
{
145
.name = "detect-zeroes",
146
.type = QEMU_OPT_STRING,
147
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp)
148
qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off");
149
qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off");
150
qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off");
151
+ qdict_set_default_str(qdict, BDRV_OPT_AUTO_READ_ONLY, "off");
105
+
152
+
106
/** Element of the list of jobs */
153
}
107
QLIST_ENTRY(Job) job_list;
154
108
} Job;
155
bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp);
109
@@ -XXX,XX +XXX,XX @@ Job *job_next(Job *job);
156
diff --git a/block/vvfat.c b/block/vvfat.c
110
*/
111
Job *job_get(const char *id);
112
113
+/**
114
+ * Check whether the verb @verb can be applied to @job in its current state.
115
+ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM
116
+ * returned.
117
+ */
118
+int job_apply_verb(Job *job, JobVerb verb, Error **errp);
119
+
120
+/* TODO To be removed from the public interface */
121
+void job_state_transition(Job *job, JobStatus s1);
122
+
123
#endif
124
diff --git a/blockjob.c b/blockjob.c
125
index XXXXXXX..XXXXXXX 100644
157
index XXXXXXX..XXXXXXX 100644
126
--- a/blockjob.c
158
--- a/block/vvfat.c
127
+++ b/blockjob.c
159
+++ b/block/vvfat.c
128
@@ -XXX,XX +XXX,XX @@
160
@@ -XXX,XX +XXX,XX @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options,
129
* block_job_enter. */
161
int parent_flags, QDict *parent_options)
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
{
162
{
189
qemu_mutex_lock(&block_job_mutex);
163
qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off");
190
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque);
164
+ qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off");
191
void block_job_unref(BlockJob *job)
165
qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on");
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
}
166
}
207
167
208
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
168
diff --git a/blockdev.c b/blockdev.c
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
169
index XXXXXXX..XXXXXXX 100644
382
--- a/job.c
170
--- a/blockdev.c
383
+++ b/job.c
171
+++ b/blockdev.c
384
@@ -XXX,XX +XXX,XX @@
172
@@ -XXX,XX +XXX,XX @@ void qmp_blockdev_change_medium(bool has_device, const char *device,
385
#include "qapi/error.h"
173
386
#include "qemu/job.h"
174
bdrv_flags = blk_get_open_flags_from_root_state(blk);
387
#include "qemu/id.h"
175
bdrv_flags &= ~(BDRV_O_TEMPORARY | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING |
388
+#include "trace-root.h"
176
- BDRV_O_PROTOCOL);
389
177
+ BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY);
390
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
178
391
179
if (!has_read_only) {
392
+/* Job State Transition Table */
180
read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN;
393
+bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = {
394
+ /* U, C, R, P, Y, S, W, D, X, E, N */
395
+ /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
396
+ /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
397
+ /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
398
+ /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
399
+ /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
400
+ /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
401
+ /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
402
+ /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
403
+ /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
404
+ /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
405
+ /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
406
+};
407
+
408
+bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
409
+ /* U, C, R, P, Y, S, W, D, X, E, N */
410
+ [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
411
+ [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
412
+ [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
413
+ [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
414
+ [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
415
+ [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
416
+ [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
417
+};
418
+
419
+/* TODO Make static once the whole state machine is in job.c */
420
+void job_state_transition(Job *job, JobStatus s1)
421
+{
422
+ JobStatus s0 = job->status;
423
+ assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
424
+ trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
425
+ JobSTT[s0][s1] ? "allowed" : "disallowed",
426
+ JobStatus_str(s0), JobStatus_str(s1));
427
+ assert(JobSTT[s0][s1]);
428
+ job->status = s1;
429
+}
430
+
431
+int job_apply_verb(Job *job, JobVerb verb, Error **errp)
432
+{
433
+ JobStatus s0 = job->status;
434
+ assert(verb >= 0 && verb <= JOB_VERB__MAX);
435
+ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
436
+ JobVerbTable[verb][s0] ? "allowed" : "prohibited");
437
+ if (JobVerbTable[verb][s0]) {
438
+ return 0;
439
+ }
440
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
441
+ job->id, JobStatus_str(s0), JobVerb_str(verb));
442
+ return -EPERM;
443
+}
444
+
445
JobType job_type(const Job *job)
446
{
447
return job->driver->job_type;
448
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
449
job->driver = driver;
450
job->id = g_strdup(job_id);
451
452
+ job_state_transition(job, JOB_STATUS_CREATED);
453
+
454
QLIST_INSERT_HEAD(&jobs, job, job_list);
455
456
return job;
457
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
458
index XXXXXXX..XXXXXXX 100644
459
--- a/tests/test-blockjob.c
460
+++ b/tests/test-blockjob.c
461
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
462
job = mk_job(blk, "Steve", &test_cancel_driver, true,
463
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
464
block_job_ref(job);
465
- assert(job->status == BLOCK_JOB_STATUS_CREATED);
466
+ assert(job->job.status == JOB_STATUS_CREATED);
467
s = container_of(job, CancelJob, common);
468
s->blk = blk;
469
470
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
471
{
472
BlockJob *job = &s->common;
473
BlockBackend *blk = s->blk;
474
- BlockJobStatus sts = job->status;
475
+ JobStatus sts = job->job.status;
476
477
block_job_cancel_sync(job);
478
- if ((sts != BLOCK_JOB_STATUS_CREATED) &&
479
- (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
480
+ if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
481
BlockJob *dummy = job;
482
block_job_dismiss(&dummy, &error_abort);
483
}
484
- assert(job->status == BLOCK_JOB_STATUS_NULL);
485
+ assert(job->job.status == JOB_STATUS_NULL);
486
block_job_unref(job);
487
destroy_blk(blk);
488
}
489
@@ -XXX,XX +XXX,XX @@ static void test_cancel_running(void)
490
s = create_common(&job);
491
492
block_job_start(job);
493
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
494
+ assert(job->job.status == JOB_STATUS_RUNNING);
495
496
cancel_common(s);
497
}
498
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
499
s = create_common(&job);
500
501
block_job_start(job);
502
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
503
+ assert(job->job.status == JOB_STATUS_RUNNING);
504
505
block_job_user_pause(job, &error_abort);
506
block_job_enter(job);
507
- assert(job->status == BLOCK_JOB_STATUS_PAUSED);
508
+ assert(job->job.status == JOB_STATUS_PAUSED);
509
510
cancel_common(s);
511
}
512
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
513
s = create_common(&job);
514
515
block_job_start(job);
516
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
517
+ assert(job->job.status == JOB_STATUS_RUNNING);
518
519
s->should_converge = true;
520
block_job_enter(job);
521
- assert(job->status == BLOCK_JOB_STATUS_READY);
522
+ assert(job->job.status == JOB_STATUS_READY);
523
524
cancel_common(s);
525
}
526
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
527
s = create_common(&job);
528
529
block_job_start(job);
530
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
531
+ assert(job->job.status == JOB_STATUS_RUNNING);
532
533
s->should_converge = true;
534
block_job_enter(job);
535
- assert(job->status == BLOCK_JOB_STATUS_READY);
536
+ assert(job->job.status == JOB_STATUS_READY);
537
538
block_job_user_pause(job, &error_abort);
539
block_job_enter(job);
540
- assert(job->status == BLOCK_JOB_STATUS_STANDBY);
541
+ assert(job->job.status == JOB_STATUS_STANDBY);
542
543
cancel_common(s);
544
}
545
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
546
s = create_common(&job);
547
548
block_job_start(job);
549
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
550
+ assert(job->job.status == JOB_STATUS_RUNNING);
551
552
s->should_converge = true;
553
block_job_enter(job);
554
- assert(job->status == BLOCK_JOB_STATUS_READY);
555
+ assert(job->job.status == JOB_STATUS_READY);
556
557
block_job_complete(job, &error_abort);
558
block_job_enter(job);
559
while (!s->completed) {
560
aio_poll(qemu_get_aio_context(), true);
561
}
562
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
563
+ assert(job->job.status == JOB_STATUS_PENDING);
564
565
cancel_common(s);
566
}
567
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
568
s = create_common(&job);
569
570
block_job_start(job);
571
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
572
+ assert(job->job.status == JOB_STATUS_RUNNING);
573
574
s->should_converge = true;
575
block_job_enter(job);
576
- assert(job->status == BLOCK_JOB_STATUS_READY);
577
+ assert(job->job.status == JOB_STATUS_READY);
578
579
block_job_complete(job, &error_abort);
580
block_job_enter(job);
581
while (!s->completed) {
582
aio_poll(qemu_get_aio_context(), true);
583
}
584
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
585
+ assert(job->job.status == JOB_STATUS_PENDING);
586
587
block_job_finalize(job, &error_abort);
588
- assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
589
+ assert(job->job.status == JOB_STATUS_CONCLUDED);
590
591
cancel_common(s);
592
}
593
diff --git a/block/trace-events b/block/trace-events
594
index XXXXXXX..XXXXXXX 100644
595
--- a/block/trace-events
596
+++ b/block/trace-events
597
@@ -XXX,XX +XXX,XX @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
598
599
# blockjob.c
600
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
601
-block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
602
-block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
603
604
# block/block-backend.c
605
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
606
diff --git a/trace-events b/trace-events
607
index XXXXXXX..XXXXXXX 100644
608
--- a/trace-events
609
+++ b/trace-events
610
@@ -XXX,XX +XXX,XX @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
611
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
612
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
613
614
+# job.c
615
+job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
616
+job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
617
+
618
### Guest events, keep at bottom
619
620
621
--
181
--
622
2.13.6
182
2.19.1
623
183
624
184
diff view generated by jsdifflib
1
Commit 0ec4dfb8d changed block-job_pause/resume so that they return an
1
Commit e2b8247a322 introduced an error path in qemu_rbd_open() after
2
error if they don't do anything because the job is already
2
calling rbd_open(), but neglected to close the image again in this error
3
paused/running. It forgot to update the documentation, so do that now.
3
path. The error path should contain everything that the regular close
4
function qemu_rbd_close() contains.
5
6
This adds the missing rbd_close() call.
4
7
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
10
---
9
qapi/block-core.json | 5 ++---
11
block/rbd.c | 1 +
10
1 file changed, 2 insertions(+), 3 deletions(-)
12
1 file changed, 1 insertion(+)
11
13
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/block/rbd.c b/block/rbd.c
13
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
16
--- a/block/rbd.c
15
+++ b/qapi/block-core.json
17
+++ b/block/rbd.c
16
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
17
#
19
"automatically marking the image read-only.");
18
# This command returns immediately after marking the active background block
20
r = bdrv_set_read_only(bs, true, &local_err);
19
# operation for pausing. It is an error to call this command if no
21
if (r < 0) {
20
-# operation is in progress. Pausing an already paused job has no cumulative
22
+ rbd_close(s->image);
21
-# effect; a single block-job-resume command will resume the job.
23
error_propagate(errp, local_err);
22
+# operation is in progress or if the job is already paused.
24
goto failed_open;
23
#
25
}
24
# The operation will pause as soon as possible. No event is emitted when
25
# the operation is actually paused. Cancelling a paused job automatically
26
@@ -XXX,XX +XXX,XX @@
27
#
28
# This command returns immediately after resuming a paused background block
29
# operation. It is an error to call this command if no operation is in
30
-# progress. Resuming an already running job is not an error.
31
+# progress or if the job is not paused.
32
#
33
# This command also clears the error status of the job.
34
#
35
--
26
--
36
2.13.6
27
2.19.1
37
28
38
29
diff view generated by jsdifflib
1
This moves the logic that implements job transactions from BlockJob to
1
Some block drivers have traditionally changed their node to read-only
2
Job.
2
mode without asking the user. This behaviour has been marked deprecated
3
since 2.11, expecting users to provide an explicit read-only=on option.
4
5
Now that we have auto-read-only=on, enable these drivers to make use of
6
the option.
7
8
This is the only use of bdrv_set_read_only(), so we can make it a bit
9
more specific and turn it into a bdrv_apply_auto_read_only() that is
10
more convenient for drivers to use.
3
11
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
6
---
14
---
7
include/block/blockjob.h | 54 ----------
15
include/block/block.h | 3 ++-
8
include/block/blockjob_int.h | 10 --
16
block.c | 42 +++++++++++++++++++++++++++---------------
9
include/qemu/job.h | 71 +++++++++++--
17
block/bochs.c | 17 ++++++-----------
10
blockdev.c | 6 +-
18
block/cloop.c | 16 +++++-----------
11
blockjob.c | 238 +------------------------------------------
19
block/dmg.c | 16 +++++-----------
12
job.c | 234 ++++++++++++++++++++++++++++++++++++++++--
20
block/rbd.c | 15 ++++-----------
13
tests/test-blockjob-txn.c | 12 +--
21
block/vvfat.c | 10 ++--------
14
tests/test-blockjob.c | 2 +-
22
7 files changed, 51 insertions(+), 68 deletions(-)
15
8 files changed, 303 insertions(+), 324 deletions(-)
23
16
24
diff --git a/include/block/block.h b/include/block/block.h
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
25
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/block.h
19
--- a/include/block/blockjob.h
27
+++ b/include/block/block.h
20
+++ b/include/block/blockjob.h
28
@@ -XXX,XX +XXX,XX @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base,
21
@@ -XXX,XX +XXX,XX @@
29
bool bdrv_is_read_only(BlockDriverState *bs);
22
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
30
int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
23
31
bool ignore_allow_rdw, Error **errp);
24
typedef struct BlockJobDriver BlockJobDriver;
32
-int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp);
25
-typedef struct JobTxn JobTxn;
33
+int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
26
34
+ Error **errp);
27
/**
35
bool bdrv_is_writable(BlockDriverState *bs);
28
* BlockJob:
36
bool bdrv_is_sg(BlockDriverState *bs);
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
37
bool bdrv_is_inserted(BlockDriverState *bs);
30
38
diff --git a/block.c b/block.c
31
/** BlockDriverStates that are involved in this block job */
39
index XXXXXXX..XXXXXXX 100644
32
GSList *nodes;
40
--- a/block.c
33
-
41
+++ b/block.c
34
- JobTxn *txn;
42
@@ -XXX,XX +XXX,XX @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only,
35
} BlockJob;
43
return 0;
36
44
}
37
/**
45
38
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
46
-/* TODO Remove (deprecated since 2.11)
39
void block_job_cancel(BlockJob *job, bool force);
47
- * Block drivers are not supposed to automatically change bs->read_only.
40
48
- * Instead, they should just check whether they can provide what the user
41
/**
49
- * explicitly requested and error out if read-write is requested, but they can
42
- * block_job_finalize:
50
- * only provide read-only access. */
43
- * @job: The job to fully commit and finish.
51
-int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp)
44
- * @errp: Error object.
52
+/*
45
- *
53
+ * Called by a driver that can only provide a read-only image.
46
- * For jobs that have finished their work and are pending
47
- * awaiting explicit acknowledgement to commit their work,
48
- * This will commit that work.
49
- *
50
- * FIXME: Make the below statement universally true:
51
- * For jobs that support the manual workflow mode, all graph
52
- * changes that occur as a result will occur after this command
53
- * and before a successful reply.
54
- */
55
-void block_job_finalize(BlockJob *job, Error **errp);
56
-
57
-/**
58
* block_job_dismiss:
59
* @job: The job to be dismissed.
60
* @errp: Error object.
61
@@ -XXX,XX +XXX,XX @@ int block_job_complete_sync(BlockJob *job, Error **errp);
62
void block_job_iostatus_reset(BlockJob *job);
63
64
/**
65
- * block_job_txn_new:
66
- *
67
- * Allocate and return a new block job transaction. Jobs can be added to the
68
- * transaction using block_job_txn_add_job().
69
- *
70
- * The transaction is automatically freed when the last job completes or is
71
- * cancelled.
72
- *
73
- * All jobs in the transaction either complete successfully or fail/cancel as a
74
- * group. Jobs wait for each other before completing. Cancelling one job
75
- * cancels all jobs in the transaction.
76
- */
77
-JobTxn *block_job_txn_new(void);
78
-
79
-/**
80
- * block_job_txn_unref:
81
- *
82
- * Release a reference that was previously acquired with block_job_txn_add_job
83
- * or block_job_txn_new. If it's the last reference to the object, it will be
84
- * freed.
85
- */
86
-void block_job_txn_unref(JobTxn *txn);
87
-
88
-/**
89
- * block_job_txn_add_job:
90
- * @txn: The transaction (may be NULL)
91
- * @job: Job to add to the transaction
92
- *
93
- * Add @job to the transaction. The @job must not already be in a transaction.
94
- * The caller must call either block_job_txn_unref() or block_job_completed()
95
- * to release the reference that is automatically grabbed here.
96
- */
97
-void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
98
-
99
-/**
100
* block_job_is_internal:
101
* @job: The job to determine if it is user-visible or not.
102
*
103
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/blockjob_int.h
106
+++ b/include/block/blockjob_int.h
107
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
108
/** Generic JobDriver callbacks and settings */
109
JobDriver job_driver;
110
111
- /**
112
- * If the callback is not NULL, prepare will be invoked when all the jobs
113
- * belonging to the same transaction complete; or upon this job's completion
114
- * if it is not in a transaction.
115
- *
116
- * This callback will not be invoked if the job has already failed.
117
- * If it fails, abort and then clean will be called.
118
- */
119
- int (*prepare)(BlockJob *job);
120
-
121
/*
122
* If the callback is not NULL, it will be invoked before the job is
123
* resumed in a new AioContext. This is the place to move any resources
124
diff --git a/include/qemu/job.h b/include/qemu/job.h
125
index XXXXXXX..XXXXXXX 100644
126
--- a/include/qemu/job.h
127
+++ b/include/qemu/job.h
128
@@ -XXX,XX +XXX,XX @@
129
#include "block/aio.h"
130
131
typedef struct JobDriver JobDriver;
132
+typedef struct JobTxn JobTxn;
133
+
134
135
/**
136
* Long-running operation.
137
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
138
/** Element of the list of jobs */
139
QLIST_ENTRY(Job) job_list;
140
141
+ /** Transaction this job is part of */
142
+ JobTxn *txn;
143
+
144
/** Element of the list of jobs in a job transaction */
145
QLIST_ENTRY(Job) txn_list;
146
} Job;
147
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
148
void (*drain)(Job *job);
149
150
/**
151
+ * If the callback is not NULL, prepare will be invoked when all the jobs
152
+ * belonging to the same transaction complete; or upon this job's completion
153
+ * if it is not in a transaction.
154
+ *
155
+ * This callback will not be invoked if the job has already failed.
156
+ * If it fails, abort and then clean will be called.
157
+ */
158
+ int (*prepare)(Job *job);
159
+
160
+ /**
161
* If the callback is not NULL, it will be invoked when all the jobs
162
* belonging to the same transaction complete; or upon this job's
163
* completion if it is not in a transaction. Skipped if NULL.
164
@@ -XXX,XX +XXX,XX @@ typedef enum JobCreateFlags {
165
JOB_MANUAL_DISMISS = 0x04,
166
} JobCreateFlags;
167
168
+/**
169
+ * Allocate and return a new job transaction. Jobs can be added to the
170
+ * transaction using job_txn_add_job().
171
+ *
54
+ *
172
+ * The transaction is automatically freed when the last job completes or is
55
+ * Returns 0 if the node is already read-only or it could switch the node to
173
+ * cancelled.
56
+ * read-only because BDRV_O_AUTO_RDONLY is set.
174
+ *
57
+ *
175
+ * All jobs in the transaction either complete successfully or fail/cancel as a
58
+ * Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set
176
+ * group. Jobs wait for each other before completing. Cancelling one job
59
+ * or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg
177
+ * cancels all jobs in the transaction.
60
+ * is not NULL, it is used as the error message for the Error object.
178
+ */
61
+ */
179
+JobTxn *job_txn_new(void);
62
+int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg,
180
+
63
+ Error **errp)
181
+/**
64
{
182
+ * Release a reference that was previously acquired with job_txn_add_job or
65
int ret = 0;
183
+ * job_txn_new. If it's the last reference to the object, it will be freed.
66
184
+ */
67
- ret = bdrv_can_set_read_only(bs, read_only, false, errp);
185
+void job_txn_unref(JobTxn *txn);
68
- if (ret < 0) {
186
+
69
- return ret;
187
+/**
70
+ if (!(bs->open_flags & BDRV_O_RDWR)) {
188
+ * @txn: The transaction (may be NULL)
71
+ return 0;
189
+ * @job: Job to add to the transaction
72
+ }
190
+ *
73
+ if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) {
191
+ * Add @job to the transaction. The @job must not already be in a transaction.
74
+ goto fail;
192
+ * The caller must call either job_txn_unref() or block_job_completed() to
75
}
193
+ * release the reference that is automatically grabbed here.
76
194
+ *
77
- bs->read_only = read_only;
195
+ * If @txn is NULL, the function does nothing.
78
-
196
+ */
79
- if (read_only) {
197
+void job_txn_add_job(JobTxn *txn, Job *job);
80
- bs->open_flags &= ~BDRV_O_RDWR;
198
81
- } else {
199
/**
82
- bs->open_flags |= BDRV_O_RDWR;
200
* Create a new long-running job and return it.
83
+ ret = bdrv_can_set_read_only(bs, true, false, NULL);
201
*
84
+ if (ret < 0) {
202
* @job_id: The id of the newly-created job, or %NULL for internal jobs
85
+ goto fail;
203
* @driver: The class object for the newly-created job.
86
}
204
+ * @txn: The transaction this job belongs to, if any. %NULL otherwise.
87
205
* @ctx: The AioContext to run the job coroutine in.
88
+ bs->read_only = true;
206
* @flags: Creation flags for the job. See @JobCreateFlags.
89
+ bs->open_flags &= ~BDRV_O_RDWR;
207
* @cb: Completion function for the job.
90
+
208
* @opaque: Opaque pointer value passed to @cb.
91
return 0;
209
* @errp: Error object.
92
+
210
*/
93
+fail:
211
-void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
94
+ error_setg(errp, "%s", errmsg ?: "Image is read-only");
212
- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
95
+ return -EACCES;
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
}
96
}
280
97
281
void qmp_eject(bool has_device, const char *device,
98
void bdrv_get_full_backing_filename_from_filename(const char *backed,
282
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
99
diff --git a/block/bochs.c b/block/bochs.c
283
}
100
index XXXXXXX..XXXXXXX 100644
284
101
--- a/block/bochs.c
285
trace_qmp_block_job_finalize(job);
102
+++ b/block/bochs.c
286
- block_job_finalize(job, errp);
103
@@ -XXX,XX +XXX,XX @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags,
287
+ job_finalize(&job->job, errp);
104
struct bochs_header bochs;
288
aio_context_release(aio_context);
105
int ret;
289
}
106
290
107
+ /* No write support yet */
291
diff --git a/blockjob.c b/blockjob.c
108
+ ret = bdrv_apply_auto_read_only(bs, NULL, errp);
292
index XXXXXXX..XXXXXXX 100644
109
+ if (ret < 0) {
293
--- a/blockjob.c
110
+ return ret;
294
+++ b/blockjob.c
111
+ }
295
@@ -XXX,XX +XXX,XX @@
112
+
296
#include "qemu/coroutine.h"
113
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
297
#include "qemu/timer.h"
114
false, errp);
298
115
if (!bs->file) {
299
-/* Transactional group of block jobs */
116
return -EINVAL;
300
-struct JobTxn {
117
}
301
-
118
302
- /* Is this txn being cancelled? */
119
- if (!bdrv_is_read_only(bs)) {
303
- bool aborting;
120
- error_report("Opening bochs images without an explicit read-only=on "
304
-
121
- "option is deprecated. Future versions will refuse to "
305
- /* List of jobs */
122
- "open the image instead of automatically marking the "
306
- QLIST_HEAD(, Job) jobs;
123
- "image read-only.");
307
-
124
- ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */
308
- /* Reference count */
125
- if (ret < 0) {
309
- int refcnt;
126
- return ret;
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
- }
127
- }
423
- }
128
- }
424
- return rc;
129
-
425
-}
130
ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs));
426
-
131
if (ret < 0) {
427
-static void block_job_completed_txn_abort(BlockJob *job)
132
return ret;
428
-{
133
diff --git a/block/cloop.c b/block/cloop.c
429
- AioContext *ctx;
134
index XXXXXXX..XXXXXXX 100644
430
- JobTxn *txn = job->txn;
135
--- a/block/cloop.c
431
- Job *other_job;
136
+++ b/block/cloop.c
432
-
137
@@ -XXX,XX +XXX,XX @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags,
433
- if (txn->aborting) {
138
uint32_t offsets_size, max_compressed_block_size = 1, i;
434
- /*
139
int ret;
435
- * We are cancelled by another job, which will handle everything.
140
436
- */
141
+ ret = bdrv_apply_auto_read_only(bs, NULL, errp);
437
- return;
142
+ if (ret < 0) {
438
- }
143
+ return ret;
439
- txn->aborting = true;
144
+ }
440
- block_job_txn_ref(txn);
145
+
441
-
146
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
442
- /* We are the first failed job. Cancel other jobs. */
147
false, errp);
443
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
148
if (!bs->file) {
444
- ctx = other_job->aio_context;
149
return -EINVAL;
445
- aio_context_acquire(ctx);
150
}
446
- }
151
447
-
152
- if (!bdrv_is_read_only(bs)) {
448
- /* Other jobs are effectively cancelled by us, set the status for
153
- error_report("Opening cloop images without an explicit read-only=on "
449
- * them; this job, however, may or may not be cancelled, depending
154
- "option is deprecated. Future versions will refuse to "
450
- * on the caller, so leave it. */
155
- "open the image instead of automatically marking the "
451
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
156
- "image read-only.");
452
- if (other_job != &job->job) {
157
- ret = bdrv_set_read_only(bs, true, errp);
453
- job_cancel_async(other_job, false);
158
- if (ret < 0) {
159
- return ret;
454
- }
160
- }
455
- }
161
- }
456
- while (!QLIST_EMPTY(&txn->jobs)) {
162
-
457
- other_job = QLIST_FIRST(&txn->jobs);
163
/* read header */
458
- ctx = other_job->aio_context;
164
ret = bdrv_pread(bs->file, 128, &s->block_size, 4);
459
- if (!job_is_completed(other_job)) {
165
if (ret < 0) {
460
- assert(job_is_cancelled(other_job));
166
diff --git a/block/dmg.c b/block/dmg.c
461
- job_finish_sync(other_job, NULL, NULL);
167
index XXXXXXX..XXXXXXX 100644
168
--- a/block/dmg.c
169
+++ b/block/dmg.c
170
@@ -XXX,XX +XXX,XX @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags,
171
int64_t offset;
172
int ret;
173
174
+ ret = bdrv_apply_auto_read_only(bs, NULL, errp);
175
+ if (ret < 0) {
176
+ return ret;
177
+ }
178
+
179
bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file,
180
false, errp);
181
if (!bs->file) {
182
return -EINVAL;
183
}
184
185
- if (!bdrv_is_read_only(bs)) {
186
- error_report("Opening dmg images without an explicit read-only=on "
187
- "option is deprecated. Future versions will refuse to "
188
- "open the image instead of automatically marking the "
189
- "image read-only.");
190
- ret = bdrv_set_read_only(bs, true, errp);
191
- if (ret < 0) {
192
- return ret;
462
- }
193
- }
463
- job_finalize_single(other_job);
464
- aio_context_release(ctx);
465
- }
194
- }
466
-
195
-
467
- block_job_txn_unref(txn);
196
block_module_load_one("dmg-bz2");
468
-}
197
469
-
198
s->n_chunks = 0;
470
-static int block_job_needs_finalize(BlockJob *job)
199
diff --git a/block/rbd.c b/block/rbd.c
471
-{
200
index XXXXXXX..XXXXXXX 100644
472
- return !job->job.auto_finalize;
201
--- a/block/rbd.c
473
-}
202
+++ b/block/rbd.c
474
-
203
@@ -XXX,XX +XXX,XX @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags,
475
-static int block_job_finalize_single(BlockJob *job)
204
/* If we are using an rbd snapshot, we must be r/o, otherwise
476
-{
205
* leave as-is */
477
- return job_finalize_single(&job->job);
206
if (s->snap != NULL) {
478
-}
207
- if (!bdrv_is_read_only(bs)) {
479
-
208
- error_report("Opening rbd snapshots without an explicit "
480
-static void block_job_do_finalize(BlockJob *job)
209
- "read-only=on option is deprecated. Future versions "
481
-{
210
- "will refuse to open the image instead of "
482
- int rc;
211
- "automatically marking the image read-only.");
483
- assert(job && job->txn);
212
- r = bdrv_set_read_only(bs, true, &local_err);
484
-
213
- if (r < 0) {
485
- /* prepare the transaction to complete */
214
- rbd_close(s->image);
486
- rc = block_job_txn_apply(job->txn, block_job_prepare, true);
215
- error_propagate(errp, local_err);
487
- if (rc) {
216
- goto failed_open;
488
- block_job_completed_txn_abort(job);
217
- }
489
- } else {
218
+ r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp);
490
- block_job_txn_apply(job->txn, block_job_finalize_single, true);
219
+ if (r < 0) {
491
- }
220
+ rbd_close(s->image);
492
-}
221
+ goto failed_open;
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
}
222
}
568
}
223
}
569
224
570
- /* Single jobs are modeled as single-job transactions for sake of
225
diff --git a/block/vvfat.c b/block/vvfat.c
571
- * consolidating the job management logic */
226
index XXXXXXX..XXXXXXX 100644
572
- if (!txn) {
227
--- a/block/vvfat.c
573
- txn = block_job_txn_new();
228
+++ b/block/vvfat.c
574
- block_job_txn_add_job(txn, job);
229
@@ -XXX,XX +XXX,XX @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags,
575
- block_job_txn_unref(txn);
230
"Unable to set VVFAT to 'rw' when drive is read-only");
576
- } else {
231
goto fail;
577
- block_job_txn_add_job(txn, job);
232
}
578
- }
233
- } else if (!bdrv_is_read_only(bs)) {
579
-
234
- error_report("Opening non-rw vvfat images without an explicit "
580
return job;
235
- "read-only=on option is deprecated. Future versions "
581
}
236
- "will refuse to open the image instead of "
582
237
- "automatically marking the image read-only.");
583
void block_job_completed(BlockJob *job, int ret)
238
- /* read only is the default for safety */
584
{
239
- ret = bdrv_set_read_only(bs, true, &local_err);
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
+ }
646
+}
647
+
648
+void job_txn_add_job(JobTxn *txn, Job *job)
649
+{
650
+ if (!txn) {
651
+ return;
652
+ }
653
+
654
+ assert(!job->txn);
655
+ job->txn = txn;
656
+
657
+ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
658
+ job_txn_ref(txn);
659
+}
660
+
661
+static void job_txn_del_job(Job *job)
662
+{
663
+ if (job->txn) {
664
+ QLIST_REMOVE(job, txn_list);
665
+ job_txn_unref(job->txn);
666
+ job->txn = NULL;
667
+ }
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 {
240
+ } else {
719
+ job_txn_add_job(txn, job);
241
+ ret = bdrv_apply_auto_read_only(bs, NULL, errp);
720
+ }
242
if (ret < 0) {
721
+
243
- error_propagate(errp, local_err);
722
return job;
244
goto fail;
723
}
724
725
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
726
if (--job->refcnt == 0) {
727
assert(job->status == JOB_STATUS_NULL);
728
assert(!timer_pending(&job->sleep_timer));
729
+ assert(!job->txn);
730
731
if (job->driver->free) {
732
job->driver->free(job);
733
@@ -XXX,XX +XXX,XX @@ void job_event_completed(Job *job)
734
notifier_list_notify(&job->on_finalize_completed, job);
735
}
736
737
-void job_event_pending(Job *job)
738
+static void job_event_pending(Job *job)
739
{
740
notifier_list_notify(&job->on_pending, job);
741
}
742
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
743
job->paused = false;
744
job->deferred_to_main_loop = true;
745
746
- /* TODO Don't assume it's a BlockJob */
747
- block_job_txn_del_job((BlockJob*) job);
748
+ job_txn_del_job(job);
749
750
job_state_transition(job, JOB_STATUS_NULL);
751
job_unref(job);
752
@@ -XXX,XX +XXX,XX @@ static void job_clean(Job *job)
753
}
754
}
755
756
-int job_finalize_single(Job *job)
757
+static int job_finalize_single(Job *job)
758
{
759
assert(job_is_completed(job));
760
761
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
762
}
245
}
763
}
246
}
764
765
- /* TODO Don't assume it's a BlockJob */
766
- block_job_txn_del_job((BlockJob*) job);
767
+ job_txn_del_job(job);
768
job_conclude(job);
769
return 0;
770
}
771
772
+void job_cancel_async(Job *job, bool force)
773
+{
774
+ if (job->user_paused) {
775
+ /* Do not call job_enter here, the caller will handle it. */
776
+ job->user_paused = false;
777
+ if (job->driver->user_resume) {
778
+ job->driver->user_resume(job);
779
+ }
780
+ assert(job->pause_count > 0);
781
+ job->pause_count--;
782
+ }
783
+ job->cancelled = true;
784
+ /* To prevent 'force == false' overriding a previous 'force == true' */
785
+ job->force_cancel |= force;
786
+}
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
907
--- a/tests/test-blockjob-txn.c
908
+++ b/tests/test-blockjob-txn.c
909
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
910
JobTxn *txn;
911
int result = -EINPROGRESS;
912
913
- txn = block_job_txn_new();
914
+ txn = job_txn_new();
915
job = test_block_job_start(1, true, expected, &result, txn);
916
job_start(&job->job);
917
918
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
919
}
920
g_assert_cmpint(result, ==, expected);
921
922
- block_job_txn_unref(txn);
923
+ job_txn_unref(txn);
924
}
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
--
247
--
977
2.13.6
248
2.19.1
978
249
979
250
diff view generated by jsdifflib
1
This adds a QMP event that is emitted whenever a job transitions from
1
If read-only=off, but auto-read-only=on is given, open a read-write NBD
2
one status to another.
2
connection if the server provides a read-write export, but instead of
3
erroring out for read-only exports, just degrade to read-only.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
6
---
7
---
7
qapi/job.json | 14 ++++
8
block/nbd-client.c | 10 +++++-----
8
job.c | 10 +++
9
1 file changed, 5 insertions(+), 5 deletions(-)
9
tests/qemu-iotests/030 | 17 +++-
10
tests/qemu-iotests/040 | 2 +
11
tests/qemu-iotests/041 | 17 +++-
12
tests/qemu-iotests/094.out | 7 ++
13
tests/qemu-iotests/095 | 2 +-
14
tests/qemu-iotests/095.out | 6 ++
15
tests/qemu-iotests/109 | 2 +-
16
tests/qemu-iotests/109.out | 178 ++++++++++++++++++++++++++++++++++++------
17
tests/qemu-iotests/124 | 8 ++
18
tests/qemu-iotests/127.out | 7 ++
19
tests/qemu-iotests/141 | 13 ++-
20
tests/qemu-iotests/141.out | 29 +++++++
21
tests/qemu-iotests/144 | 2 +-
22
tests/qemu-iotests/144.out | 7 ++
23
tests/qemu-iotests/156 | 2 +-
24
tests/qemu-iotests/156.out | 7 ++
25
tests/qemu-iotests/185 | 12 +--
26
tests/qemu-iotests/185.out | 10 +++
27
tests/qemu-iotests/191 | 4 +-
28
tests/qemu-iotests/191.out | 132 +++++++++++++++++++++++++++++++
29
tests/qemu-iotests/iotests.py | 5 ++
30
23 files changed, 449 insertions(+), 44 deletions(-)
31
10
32
diff --git a/qapi/job.json b/qapi/job.json
11
diff --git a/block/nbd-client.c b/block/nbd-client.c
33
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
34
--- a/qapi/job.json
13
--- a/block/nbd-client.c
35
+++ b/qapi/job.json
14
+++ b/block/nbd-client.c
36
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ int nbd_client_init(BlockDriverState *bs,
37
{ 'enum': 'JobVerb',
16
logout("Failed to negotiate with the NBD server\n");
38
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
17
return ret;
39
'finalize' ] }
40
+
41
+##
42
+# @JOB_STATUS_CHANGE:
43
+#
44
+# Emitted when a job transitions to a different status.
45
+#
46
+# @id: The job identifier
47
+# @status: The new job status
48
+#
49
+# Since: 2.13
50
+##
51
+{ 'event': 'JOB_STATUS_CHANGE',
52
+ 'data': { 'id': 'str',
53
+ 'status': 'JobStatus' } }
54
diff --git a/job.c b/job.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/job.c
57
+++ b/job.c
58
@@ -XXX,XX +XXX,XX @@
59
#include "qemu/id.h"
60
#include "qemu/main-loop.h"
61
#include "trace-root.h"
62
+#include "qapi/qapi-events-job.h"
63
64
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
65
66
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
67
return rc;
68
}
69
70
+static bool job_is_internal(Job *job)
71
+{
72
+ return (job->id == NULL);
73
+}
74
+
75
static void job_state_transition(Job *job, JobStatus s1)
76
{
77
JobStatus s0 = job->status;
78
@@ -XXX,XX +XXX,XX @@ static void job_state_transition(Job *job, JobStatus s1)
79
JobStatus_str(s0), JobStatus_str(s1));
80
assert(JobSTT[s0][s1]);
81
job->status = s1;
82
+
83
+ if (!job_is_internal(job) && s1 != s0) {
84
+ qapi_event_send_job_status_change(job->id, job->status, &error_abort);
85
+ }
86
}
87
88
int job_apply_verb(Job *job, JobVerb verb, Error **errp)
89
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
90
index XXXXXXX..XXXXXXX 100755
91
--- a/tests/qemu-iotests/030
92
+++ b/tests/qemu-iotests/030
93
@@ -XXX,XX +XXX,XX @@ class TestParallelOps(iotests.QMPTestCase):
94
result = self.vm.qmp('block-stream', device='node5', base=self.imgs[3], job_id='stream-node6')
95
self.assert_qmp(result, 'error/class', 'GenericError')
96
97
- event = self.vm.get_qmp_event(wait=True)
98
- self.assertEqual(event['event'], 'BLOCK_JOB_READY')
99
+ event = self.vm.event_wait(name='BLOCK_JOB_READY')
100
self.assert_qmp(event, 'data/device', 'commit-drive0')
101
self.assert_qmp(event, 'data/type', 'commit')
102
self.assert_qmp_absent(event, 'data/error')
103
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
104
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
105
self.assert_qmp(event, 'data/len', self.image_len)
106
completed = True
107
+ elif event['event'] == 'JOB_STATUS_CHANGE':
108
+ self.assert_qmp(event, 'data/id', 'drive0')
109
110
self.assert_no_active_block_jobs()
111
self.vm.shutdown()
112
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
113
self.assert_qmp(event, 'data/offset', self.image_len)
114
self.assert_qmp(event, 'data/len', self.image_len)
115
completed = True
116
+ elif event['event'] == 'JOB_STATUS_CHANGE':
117
+ self.assert_qmp(event, 'data/id', 'drive0')
118
119
self.assert_no_active_block_jobs()
120
self.vm.shutdown()
121
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
122
self.assert_qmp(event, 'data/offset', self.image_len)
123
self.assert_qmp(event, 'data/len', self.image_len)
124
completed = True
125
+ elif event['event'] == 'JOB_STATUS_CHANGE':
126
+ self.assert_qmp(event, 'data/id', 'drive0')
127
128
self.assert_no_active_block_jobs()
129
self.vm.shutdown()
130
@@ -XXX,XX +XXX,XX @@ class TestEIO(TestErrors):
131
self.assert_qmp(event, 'data/offset', self.STREAM_BUFFER_SIZE)
132
self.assert_qmp(event, 'data/len', self.image_len)
133
completed = True
134
+ elif event['event'] == 'JOB_STATUS_CHANGE':
135
+ self.assert_qmp(event, 'data/id', 'drive0')
136
137
self.assert_no_active_block_jobs()
138
self.vm.shutdown()
139
@@ -XXX,XX +XXX,XX @@ class TestENOSPC(TestErrors):
140
self.assert_qmp(event, 'data/offset', self.image_len)
141
self.assert_qmp(event, 'data/len', self.image_len)
142
completed = True
143
+ elif event['event'] == 'JOB_STATUS_CHANGE':
144
+ self.assert_qmp(event, 'data/id', 'drive0')
145
146
self.assert_no_active_block_jobs()
147
self.vm.shutdown()
148
@@ -XXX,XX +XXX,XX @@ class TestStreamStop(iotests.QMPTestCase):
149
150
time.sleep(0.1)
151
events = self.vm.get_qmp_events(wait=False)
152
- self.assertEqual(events, [], 'unexpected QMP event: %s' % events)
153
+ for e in events:
154
+ self.assert_qmp(e, 'event', 'JOB_STATUS_CHANGE')
155
+ self.assert_qmp(e, 'data/id', 'drive0')
156
157
self.cancel_and_wait(resume=True)
158
159
diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040
160
index XXXXXXX..XXXXXXX 100755
161
--- a/tests/qemu-iotests/040
162
+++ b/tests/qemu-iotests/040
163
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(ImageCommitTestCase):
164
elif event['event'] == 'BLOCK_JOB_CANCELLED':
165
self.assert_qmp(event, 'data/device', 'drive0')
166
cancelled = True
167
+ elif event['event'] == 'JOB_STATUS_CHANGE':
168
+ self.assert_qmp(event, 'data/id', 'drive0')
169
else:
170
self.fail("Unexpected event %s" % (event['event']))
171
172
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
173
index XXXXXXX..XXXXXXX 100755
174
--- a/tests/qemu-iotests/041
175
+++ b/tests/qemu-iotests/041
176
@@ -XXX,XX +XXX,XX @@ new_state = "1"
177
self.assert_qmp(event, 'data/device', 'drive0')
178
self.assert_qmp(event, 'data/error', 'Input/output error')
179
completed = True
180
+ elif event['event'] == 'JOB_STATUS_CHANGE':
181
+ self.assert_qmp(event, 'data/id', 'drive0')
182
183
self.assert_no_active_block_jobs()
184
self.vm.shutdown()
185
@@ -XXX,XX +XXX,XX @@ new_state = "1"
186
self.assert_qmp(result, 'return', {})
187
188
event = self.vm.get_qmp_event(wait=True)
189
+ while event['event'] == 'JOB_STATUS_CHANGE':
190
+ self.assert_qmp(event, 'data/id', 'drive0')
191
+ event = self.vm.get_qmp_event(wait=True)
192
+
193
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
194
self.assert_qmp(event, 'data/device', 'drive0')
195
self.assert_qmp(event, 'data/operation', 'read')
196
@@ -XXX,XX +XXX,XX @@ new_state = "1"
197
self.assert_qmp(result, 'return', {})
198
199
event = self.vm.get_qmp_event(wait=True)
200
+ while event['event'] == 'JOB_STATUS_CHANGE':
201
+ self.assert_qmp(event, 'data/id', 'drive0')
202
+ event = self.vm.get_qmp_event(wait=True)
203
+
204
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
205
self.assert_qmp(event, 'data/device', 'drive0')
206
self.assert_qmp(event, 'data/operation', 'read')
207
@@ -XXX,XX +XXX,XX @@ new_state = "1"
208
on_target_error='ignore')
209
self.assert_qmp(result, 'return', {})
210
211
- event = self.vm.get_qmp_event(wait=True)
212
+ event = self.vm.event_wait(name='BLOCK_JOB_ERROR')
213
self.assertEquals(event['event'], 'BLOCK_JOB_ERROR')
214
self.assert_qmp(event, 'data/device', 'drive0')
215
self.assert_qmp(event, 'data/operation', 'write')
216
@@ -XXX,XX +XXX,XX @@ class TestGranularity(iotests.QMPTestCase):
217
sync='full', target=target_img,
218
mode='absolute-paths', granularity=8192)
219
self.assert_qmp(result, 'return', {})
220
+
221
event = self.vm.get_qmp_event(wait=60.0)
222
+ while event['event'] == 'JOB_STATUS_CHANGE':
223
+ self.assert_qmp(event, 'data/id', 'drive0')
224
+ event = self.vm.get_qmp_event(wait=60.0)
225
+
226
# Failures will manifest as COMPLETED/ERROR.
227
self.assert_qmp(event, 'event', 'BLOCK_JOB_READY')
228
self.complete_and_wait(drive='drive0', wait_ready=False)
229
diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out
230
index XXXXXXX..XXXXXXX 100644
231
--- a/tests/qemu-iotests/094.out
232
+++ b/tests/qemu-iotests/094.out
233
@@ -XXX,XX +XXX,XX @@ QA output created by 094
234
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
235
Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864
236
{"return": {}}
237
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
238
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
239
{"return": {}}
240
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
241
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
242
{"return": {}}
243
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
244
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
245
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 67108864, "offset": 67108864, "speed": 0, "type": "mirror"}}
246
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
247
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
248
{"return": {}}
249
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
250
*** done
251
diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095
252
index XXXXXXX..XXXXXXX 100755
253
--- a/tests/qemu-iotests/095
254
+++ b/tests/qemu-iotests/095
255
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return"
256
257
_send_qemu_cmd $h "{ 'execute': 'block-commit',
258
'arguments': { 'device': 'test',
259
- 'top': '"${TEST_IMG}.snp1"' } }" "BLOCK_JOB_COMPLETED"
260
+ 'top': '"${TEST_IMG}.snp1"' } }" '"status": "null"'
261
262
_cleanup_qemu
263
264
diff --git a/tests/qemu-iotests/095.out b/tests/qemu-iotests/095.out
265
index XXXXXXX..XXXXXXX 100644
266
--- a/tests/qemu-iotests/095.out
267
+++ b/tests/qemu-iotests/095.out
268
@@ -XXX,XX +XXX,XX @@ virtual size: 5.0M (5242880 bytes)
269
=== Running QEMU Live Commit Test ===
270
271
{"return": {}}
272
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}}
273
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}}
274
{"return": {}}
275
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}}
276
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}}
277
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 104857600, "offset": 104857600, "speed": 0, "type": "commit"}}
278
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}}
279
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}}
280
281
=== Base image info after commit and resize ===
282
image: TEST_DIR/t.IMGFMT.base
283
diff --git a/tests/qemu-iotests/109 b/tests/qemu-iotests/109
284
index XXXXXXX..XXXXXXX 100755
285
--- a/tests/qemu-iotests/109
286
+++ b/tests/qemu-iotests/109
287
@@ -XXX,XX +XXX,XX @@ function run_qemu()
288
289
_send_qemu_cmd $QEMU_HANDLE '' "$qmp_event"
290
if test "$qmp_event" = BLOCK_JOB_ERROR; then
291
- _send_qemu_cmd $QEMU_HANDLE '' "BLOCK_JOB_COMPLETED"
292
+ _send_qemu_cmd $QEMU_HANDLE '' '"status": "null"'
293
fi
294
_send_qemu_cmd $QEMU_HANDLE '{"execute":"query-block-jobs"}' "return"
295
_send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return"
296
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
297
index XXXXXXX..XXXXXXX 100644
298
--- a/tests/qemu-iotests/109.out
299
+++ b/tests/qemu-iotests/109.out
300
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
301
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
302
{"return": {}}
303
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
304
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
305
-Specify the 'raw' format explicitly to remove the restrictions.
306
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
307
+ Specify the 'raw' format explicitly to remove the restrictions.
308
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
309
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
310
{"return": {}}
311
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
312
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
313
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
314
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
315
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
316
{"return": []}
317
{"return": {}}
318
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
319
read 65536/65536 bytes at offset 0
320
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
321
{"return": {}}
322
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
323
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
324
{"return": {}}
325
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
326
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
327
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
328
{"return": {}}
329
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
330
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
331
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
332
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
333
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
334
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
335
Warning: Image size mismatch!
336
Images are identical.
337
338
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
339
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
340
{"return": {}}
341
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
342
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
343
-Specify the 'raw' format explicitly to remove the restrictions.
344
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
345
+ Specify the 'raw' format explicitly to remove the restrictions.
346
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
347
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
348
{"return": {}}
349
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
350
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
351
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 512, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
352
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
353
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
354
{"return": []}
355
{"return": {}}
356
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
357
read 65536/65536 bytes at offset 0
358
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
359
{"return": {}}
360
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
361
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
362
{"return": {}}
363
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
364
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
365
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
366
{"return": {}}
367
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
368
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
369
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
370
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
371
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
372
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
373
Warning: Image size mismatch!
374
Images are identical.
375
376
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
377
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
378
{"return": {}}
379
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
380
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
381
-Specify the 'raw' format explicitly to remove the restrictions.
382
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
383
+ Specify the 'raw' format explicitly to remove the restrictions.
384
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
385
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
386
{"return": {}}
387
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
388
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
389
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 262144, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
390
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
391
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
392
{"return": []}
393
{"return": {}}
394
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
395
read 65536/65536 bytes at offset 0
396
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
397
{"return": {}}
398
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
399
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
400
{"return": {}}
401
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
402
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
403
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
404
{"return": {}}
405
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
406
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
407
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
408
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
409
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
410
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
411
Warning: Image size mismatch!
412
Images are identical.
413
414
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
415
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
416
{"return": {}}
417
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
418
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
419
-Specify the 'raw' format explicitly to remove the restrictions.
420
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
421
+ Specify the 'raw' format explicitly to remove the restrictions.
422
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
423
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
424
{"return": {}}
425
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
426
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
427
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
428
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
429
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
430
{"return": []}
431
{"return": {}}
432
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
433
read 65536/65536 bytes at offset 0
434
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
435
{"return": {}}
436
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
437
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
438
{"return": {}}
439
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
440
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
441
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
442
{"return": {}}
443
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
444
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
445
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
446
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
447
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
448
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
449
Warning: Image size mismatch!
450
Images are identical.
451
452
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
453
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
454
{"return": {}}
455
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
456
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
457
-Specify the 'raw' format explicitly to remove the restrictions.
458
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
459
+ Specify the 'raw' format explicitly to remove the restrictions.
460
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
461
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
462
{"return": {}}
463
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
464
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
465
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
466
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
467
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
468
{"return": []}
469
{"return": {}}
470
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
471
read 65536/65536 bytes at offset 0
472
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
473
{"return": {}}
474
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
475
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
476
{"return": {}}
477
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
478
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
479
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
480
{"return": {}}
481
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
482
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
483
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
484
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
485
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
486
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
487
Warning: Image size mismatch!
488
Images are identical.
489
490
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
491
Formatting 'TEST_DIR/t.raw.src', fmt=IMGFMT size=67108864
492
{"return": {}}
493
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
494
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
495
-Specify the 'raw' format explicitly to remove the restrictions.
496
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
497
+ Specify the 'raw' format explicitly to remove the restrictions.
498
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
499
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
500
{"return": {}}
501
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
502
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
503
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": 0, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
504
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
505
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
506
{"return": []}
507
{"return": {}}
508
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
509
read 65536/65536 bytes at offset 0
510
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
511
{"return": {}}
512
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
513
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
514
{"return": {}}
515
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
516
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
517
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
518
{"return": {}}
519
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
520
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
521
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
522
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
523
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
524
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
525
Warning: Image size mismatch!
526
Images are identical.
527
528
@@ -XXX,XX +XXX,XX @@ Images are identical.
529
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
530
{"return": {}}
531
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
532
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
533
-Specify the 'raw' format explicitly to remove the restrictions.
534
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
535
+ Specify the 'raw' format explicitly to remove the restrictions.
536
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
537
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
538
{"return": {}}
539
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
540
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
541
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
542
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
543
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
544
{"return": []}
545
{"return": {}}
546
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
547
read 65536/65536 bytes at offset 0
548
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
549
{"return": {}}
550
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
551
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
552
{"return": {}}
553
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
554
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
555
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
556
{"return": {}}
557
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
558
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
559
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
560
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
561
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
562
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
563
Warning: Image size mismatch!
564
Images are identical.
565
566
@@ -XXX,XX +XXX,XX @@ Images are identical.
567
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
568
{"return": {}}
569
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
570
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
571
-Specify the 'raw' format explicitly to remove the restrictions.
572
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
573
+ Specify the 'raw' format explicitly to remove the restrictions.
574
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
575
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
576
{"return": {}}
577
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
578
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
579
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
580
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
581
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
582
{"return": []}
583
{"return": {}}
584
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
585
read 65536/65536 bytes at offset 0
586
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
587
{"return": {}}
588
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
589
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
590
{"return": {}}
591
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
592
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
593
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
594
{"return": {}}
595
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
596
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
597
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
598
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
599
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
600
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
601
Warning: Image size mismatch!
602
Images are identical.
603
604
@@ -XXX,XX +XXX,XX @@ Images are identical.
605
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
606
{"return": {}}
607
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
608
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
609
-Specify the 'raw' format explicitly to remove the restrictions.
610
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
611
+ Specify the 'raw' format explicitly to remove the restrictions.
612
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
613
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
614
{"return": {}}
615
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
616
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
617
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
618
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
619
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
620
{"return": []}
621
{"return": {}}
622
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
623
read 65536/65536 bytes at offset 0
624
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
625
{"return": {}}
626
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
627
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
628
{"return": {}}
629
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
630
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
631
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
632
{"return": {}}
633
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
634
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
635
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
636
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
637
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
638
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
639
Warning: Image size mismatch!
640
Images are identical.
641
642
@@ -XXX,XX +XXX,XX @@ Images are identical.
643
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
644
{"return": {}}
645
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
646
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
647
-Specify the 'raw' format explicitly to remove the restrictions.
648
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
649
+ Specify the 'raw' format explicitly to remove the restrictions.
650
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
651
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
652
{"return": {}}
653
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "src", "operation": "write", "action": "report"}}
654
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "src"}}
655
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror", "error": "Operation not permitted"}}
656
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
657
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
658
{"return": []}
659
{"return": {}}
660
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
661
read 65536/65536 bytes at offset 0
662
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
663
{"return": {}}
664
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
665
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
666
{"return": {}}
667
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
668
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
669
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
670
{"return": {}}
671
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
672
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
673
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
674
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
675
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
676
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
677
Warning: Image size mismatch!
678
Images are identical.
679
680
@@ -XXX,XX +XXX,XX @@ Images are identical.
681
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864
682
{"return": {}}
683
WARNING: Image format was not specified for 'TEST_DIR/t.raw' and probing guessed raw.
684
-Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
685
-Specify the 'raw' format explicitly to remove the restrictions.
686
+ Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
687
+ Specify the 'raw' format explicitly to remove the restrictions.
688
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
689
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
690
{"return": {}}
691
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
692
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
693
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
694
{"return": {}}
695
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
696
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
697
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
698
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
699
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
700
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
701
Warning: Image size mismatch!
702
Images are identical.
703
{"return": {}}
704
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "src"}}
705
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "src"}}
706
{"return": {}}
707
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "src"}}
708
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
709
{"return": [{"auto-finalize": true, "io-status": "ok", "device": "src", "auto-dismiss": true, "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
710
{"return": {}}
711
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
712
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "src"}}
713
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "src"}}
714
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
715
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}}
716
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}}
717
Warning: Image size mismatch!
718
Images are identical.
719
*** done
720
diff --git a/tests/qemu-iotests/124 b/tests/qemu-iotests/124
721
index XXXXXXX..XXXXXXX 100755
722
--- a/tests/qemu-iotests/124
723
+++ b/tests/qemu-iotests/124
724
@@ -XXX,XX +XXX,XX @@ class TestIncrementalBackupBase(iotests.QMPTestCase):
725
return self.wait_qmp_backup(kwargs['device'], error)
726
727
728
+ def ignore_job_status_change_events(self):
729
+ while True:
730
+ e = self.vm.event_wait(name="JOB_STATUS_CHANGE")
731
+ if e['data']['status'] == 'null':
732
+ break
733
+
734
def wait_qmp_backup(self, device, error='Input/output error'):
735
event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
736
match={'data': {'device': device}})
737
self.assertNotEqual(event, None)
738
+ self.ignore_job_status_change_events()
739
740
try:
741
failure = self.dictpath(event, 'data/error')
742
@@ -XXX,XX +XXX,XX @@ class TestIncrementalBackupBase(iotests.QMPTestCase):
743
event = self.vm.event_wait(name='BLOCK_JOB_CANCELLED',
744
match={'data': {'device': device}})
745
self.assertNotEqual(event, None)
746
+ self.ignore_job_status_change_events()
747
748
749
def create_anchor_backup(self, drive=None):
750
diff --git a/tests/qemu-iotests/127.out b/tests/qemu-iotests/127.out
751
index XXXXXXX..XXXXXXX 100644
752
--- a/tests/qemu-iotests/127.out
753
+++ b/tests/qemu-iotests/127.out
754
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT.overlay1', fmt=IMGFMT size=65536 backing_file=TEST
755
wrote 42/42 bytes at offset 0
756
42 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
757
{"return": {}}
758
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "mirror"}}
759
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "mirror"}}
760
{"return": {}}
761
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "mirror"}}
762
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
763
{"return": {}}
764
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "mirror"}}
765
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "mirror"}}
766
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "mirror", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
767
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "mirror"}}
768
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "mirror"}}
769
{"return": {}}
770
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
771
*** done
772
diff --git a/tests/qemu-iotests/141 b/tests/qemu-iotests/141
773
index XXXXXXX..XXXXXXX 100755
774
--- a/tests/qemu-iotests/141
775
+++ b/tests/qemu-iotests/141
776
@@ -XXX,XX +XXX,XX @@ test_blockjob \
777
'format': '$IMGFMT',
778
'sync': 'none'}}" \
779
'return' \
780
- 'BLOCK_JOB_CANCELLED'
781
+ '"status": "null"'
782
783
echo
784
echo '=== Testing drive-mirror ==='
785
@@ -XXX,XX +XXX,XX @@ test_blockjob \
786
'format': '$IMGFMT',
787
'sync': 'none'}}" \
788
'BLOCK_JOB_READY' \
789
- 'BLOCK_JOB_COMPLETED'
790
+ '"status": "null"'
791
792
echo
793
echo '=== Testing active block-commit ==='
794
@@ -XXX,XX +XXX,XX @@ test_blockjob \
795
"{'execute': 'block-commit',
796
'arguments': {'job-id': 'job0', 'device': 'drv0'}}" \
797
'BLOCK_JOB_READY' \
798
- 'BLOCK_JOB_COMPLETED'
799
+ '"status": "null"'
800
801
echo
802
echo '=== Testing non-active block-commit ==='
803
@@ -XXX,XX +XXX,XX @@ test_blockjob \
804
'top': '$TEST_DIR/m.$IMGFMT',
805
'speed': 1}}" \
806
'return' \
807
- 'BLOCK_JOB_CANCELLED'
808
+ '"status": "null"'
809
810
echo
811
echo '=== Testing block-stream ==='
812
@@ -XXX,XX +XXX,XX @@ echo
813
$QEMU_IO -c 'write 0 1M' "$TEST_DIR/b.$IMGFMT" | _filter_qemu_io
814
815
# With some data to stream (and @speed set to 1), block-stream will not complete
816
-# until we send the block-job-cancel command. Therefore, no event other than
817
-# BLOCK_JOB_CANCELLED will be emitted.
818
+# until we send the block-job-cancel command.
819
820
test_blockjob \
821
"{'execute': 'block-stream',
822
@@ -XXX,XX +XXX,XX @@ test_blockjob \
823
'device': 'drv0',
824
'speed': 1}}" \
825
'return' \
826
- 'BLOCK_JOB_CANCELLED'
827
+ '"status": "null"'
828
829
_cleanup_qemu
830
831
diff --git a/tests/qemu-iotests/141.out b/tests/qemu-iotests/141.out
832
index XXXXXXX..XXXXXXX 100644
833
--- a/tests/qemu-iotests/141.out
834
+++ b/tests/qemu-iotests/141.out
835
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/m.
836
837
{"return": {}}
838
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
839
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
840
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
841
{"return": {}}
842
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
843
{"return": {}}
844
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
845
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 0, "speed": 0, "type": "backup"}}
846
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
847
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
848
{"return": {}}
849
850
=== Testing drive-mirror ===
851
852
{"return": {}}
853
Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT backing_fmt=IMGFMT
854
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
855
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
856
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
857
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
858
{"return": {}}
859
{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
860
{"return": {}}
861
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
862
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
863
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "mirror"}}
864
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
865
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
866
{"return": {}}
867
868
=== Testing active block-commit ===
869
870
{"return": {}}
871
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
872
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
873
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job0"}}
874
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
875
{"return": {}}
876
{"error": {"class": "GenericError", "desc": "Node 'drv0' is busy: node is used as backing hd of 'NODE_NAME'"}}
877
{"return": {}}
878
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job0"}}
879
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job0"}}
880
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
881
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
882
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
883
{"return": {}}
884
885
=== Testing non-active block-commit ===
886
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/o.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.
887
wrote 1048576/1048576 bytes at offset 0
888
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
889
{"return": {}}
890
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
891
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
892
{"return": {}}
893
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
894
{"return": {}}
895
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
896
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "commit"}}
897
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
898
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
899
{"return": {}}
900
901
=== Testing block-stream ===
902
@@ -XXX,XX +XXX,XX @@ wrote 1048576/1048576 bytes at offset 0
903
wrote 1048576/1048576 bytes at offset 0
904
1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
905
{"return": {}}
906
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job0"}}
907
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job0"}}
908
{"return": {}}
909
{"error": {"class": "GenericError", "desc": "Node drv0 is in use"}}
910
{"return": {}}
911
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "job0"}}
912
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "job0", "len": 1048576, "offset": 524288, "speed": 1, "type": "stream"}}
913
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job0"}}
914
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job0"}}
915
{"return": {}}
916
*** done
917
diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144
918
index XXXXXXX..XXXXXXX 100755
919
--- a/tests/qemu-iotests/144
920
+++ b/tests/qemu-iotests/144
921
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h "{ 'execute': 'block-job-complete',
922
'arguments': {
923
'device': 'virtio0'
924
}
925
- }" "COMPLETED"
926
+ }" '"status": "null"'
927
928
echo
929
echo === Performing Live Snapshot 2 ===
930
diff --git a/tests/qemu-iotests/144.out b/tests/qemu-iotests/144.out
931
index XXXXXXX..XXXXXXX 100644
932
--- a/tests/qemu-iotests/144.out
933
+++ b/tests/qemu-iotests/144.out
934
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/tmp.qcow2', fmt=qcow2 size=536870912 backing_file=TEST_DIR/
935
936
=== Performing block-commit on active layer ===
937
938
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "virtio0"}}
939
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "virtio0"}}
940
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "virtio0"}}
941
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
942
{"return": {}}
943
{"return": {}}
944
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "virtio0"}}
945
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "virtio0"}}
946
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "virtio0", "len": 0, "offset": 0, "speed": 0, "type": "commit"}}
947
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "virtio0"}}
948
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "virtio0"}}
949
950
=== Performing Live Snapshot 2 ===
951
952
diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156
953
index XXXXXXX..XXXXXXX 100755
954
--- a/tests/qemu-iotests/156
955
+++ b/tests/qemu-iotests/156
956
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $QEMU_HANDLE \
957
958
_send_qemu_cmd $QEMU_HANDLE \
959
'' \
960
- 'BLOCK_JOB_COMPLETED'
961
+ '"status": "null"'
962
963
# Remove the source images
964
rm -f "$TEST_IMG{,.backing,.overlay}"
965
diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out
966
index XXXXXXX..XXXXXXX 100644
967
--- a/tests/qemu-iotests/156.out
968
+++ b/tests/qemu-iotests/156.out
969
@@ -XXX,XX +XXX,XX @@ wrote 131072/131072 bytes at offset 131072
970
128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
971
{"return": ""}
972
Formatting 'TEST_DIR/t.IMGFMT.target.overlay', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.target
973
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "source"}}
974
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "source"}}
975
{"return": {}}
976
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "source"}}
977
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "source", "len": 131072, "offset": 131072, "speed": 0, "type": "mirror"}}
978
wrote 65536/65536 bytes at offset 196608
979
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
980
{"return": ""}
981
{"return": {}}
982
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "source"}}
983
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "source"}}
984
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "source", "len": 196608, "offset": 196608, "speed": 0, "type": "mirror"}}
985
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "source"}}
986
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "source"}}
987
988
read 65536/65536 bytes at offset 0
989
64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
990
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
991
index XXXXXXX..XXXXXXX 100755
992
--- a/tests/qemu-iotests/185
993
+++ b/tests/qemu-iotests/185
994
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
995
# If we don't sleep here 'quit' command races with disk I/O
996
sleep 0.5
997
998
+# Ignore the JOB_STATUS_CHANGE events while shutting down the VM. Depending on
999
+# the timing, jobs may or may not transition through a paused state.
1000
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1001
-wait=1 _cleanup_qemu
1002
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1003
1004
echo
1005
echo === Start active commit job and exit qemu ===
1006
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1007
sleep 0.5
1008
1009
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1010
-wait=1 _cleanup_qemu
1011
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1012
1013
echo
1014
echo === Start mirror job and exit qemu ===
1015
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1016
sleep 0.5
1017
1018
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1019
-wait=1 _cleanup_qemu
1020
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1021
1022
echo
1023
echo === Start backup job and exit qemu ===
1024
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1025
sleep 0.5
1026
1027
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1028
-wait=1 _cleanup_qemu
1029
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1030
1031
echo
1032
echo === Start streaming job and exit qemu ===
1033
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1034
sleep 0.5
1035
1036
_send_qemu_cmd $h "{ 'execute': 'quit' }" "return"
1037
-wait=1 _cleanup_qemu
1038
+wait=1 _cleanup_qemu | grep -v 'JOB_STATUS_CHANGE'
1039
1040
_check_test_img
1041
1042
diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out
1043
index XXXXXXX..XXXXXXX 100644
1044
--- a/tests/qemu-iotests/185.out
1045
+++ b/tests/qemu-iotests/185.out
1046
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1047
1048
=== Start commit job and exit qemu ===
1049
1050
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1051
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1052
{"return": {}}
1053
{"return": {}}
1054
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1055
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1056
=== Start active commit job and exit qemu ===
1057
1058
{"return": {}}
1059
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1060
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1061
{"return": {}}
1062
{"return": {}}
1063
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1064
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q
1065
1066
{"return": {}}
1067
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
1068
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1069
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1070
{"return": {}}
1071
{"return": {}}
1072
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1073
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
1074
1075
{"return": {}}
1076
Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 lazy_refcounts=off refcount_bits=16
1077
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1078
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1079
{"return": {}}
1080
{"return": {}}
1081
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1082
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l
1083
=== Start streaming job and exit qemu ===
1084
1085
{"return": {}}
1086
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "disk"}}
1087
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}}
1088
{"return": {}}
1089
{"return": {}}
1090
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
1091
diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191
1092
index XXXXXXX..XXXXXXX 100755
1093
--- a/tests/qemu-iotests/191
1094
+++ b/tests/qemu-iotests/191
1095
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1096
'device': 'top',
1097
'base':'$TEST_IMG.base',
1098
'top': '$TEST_IMG.mid' } }" \
1099
- "BLOCK_JOB_COMPLETED"
1100
+ '"status": "null"'
1101
_send_qemu_cmd $h "" "^}"
1102
1103
echo
1104
@@ -XXX,XX +XXX,XX @@ _send_qemu_cmd $h \
1105
'device': 'top',
1106
'base':'$TEST_IMG.base',
1107
'top': '$TEST_IMG.mid' } }" \
1108
- "BLOCK_JOB_COMPLETED"
1109
+ '"status": "null"'
1110
_send_qemu_cmd $h "" "^}"
1111
1112
echo
1113
diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out
1114
index XXXXXXX..XXXXXXX 100644
1115
--- a/tests/qemu-iotests/191.out
1116
+++ b/tests/qemu-iotests/191.out
1117
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1118
=== Perform commit job ===
1119
1120
{
1121
+ "timestamp": {
1122
+ "seconds": TIMESTAMP,
1123
+ "microseconds": TIMESTAMP
1124
+ },
1125
+ "event": "JOB_STATUS_CHANGE",
1126
+ "data": {
1127
+ "status": "created",
1128
+ "id": "commit0"
1129
+ }
1130
+}
1131
+{
1132
+ "timestamp": {
1133
+ "seconds": TIMESTAMP,
1134
+ "microseconds": TIMESTAMP
1135
+ },
1136
+ "event": "JOB_STATUS_CHANGE",
1137
+ "data": {
1138
+ "status": "running",
1139
+ "id": "commit0"
1140
+ }
1141
+}
1142
+{
1143
"return": {
1144
}
18
}
1145
}
19
- if (client->info.flags & NBD_FLAG_READ_ONLY &&
1146
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
20
- !bdrv_is_read_only(bs)) {
1147
"seconds": TIMESTAMP,
21
- error_setg(errp,
1148
"microseconds": TIMESTAMP
22
- "request for write access conflicts with read-only export");
1149
},
23
- return -EACCES;
1150
+ "event": "JOB_STATUS_CHANGE",
24
+ if (client->info.flags & NBD_FLAG_READ_ONLY) {
1151
+ "data": {
25
+ ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp);
1152
+ "status": "waiting",
26
+ if (ret < 0) {
1153
+ "id": "commit0"
27
+ return ret;
1154
+ }
28
+ }
1155
+}
1156
+{
1157
+ "timestamp": {
1158
+ "seconds": TIMESTAMP,
1159
+ "microseconds": TIMESTAMP
1160
+ },
1161
+ "event": "JOB_STATUS_CHANGE",
1162
+ "data": {
1163
+ "status": "pending",
1164
+ "id": "commit0"
1165
+ }
1166
+}
1167
+{
1168
+ "timestamp": {
1169
+ "seconds": TIMESTAMP,
1170
+ "microseconds": TIMESTAMP
1171
+ },
1172
"event": "BLOCK_JOB_COMPLETED",
1173
"data": {
1174
"device": "commit0",
1175
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1176
"type": "commit"
1177
}
29
}
1178
}
30
if (client->info.flags & NBD_FLAG_SEND_FUA) {
1179
+{
31
bs->supported_write_flags = BDRV_REQ_FUA;
1180
+ "timestamp": {
1181
+ "seconds": TIMESTAMP,
1182
+ "microseconds": TIMESTAMP
1183
+ },
1184
+ "event": "JOB_STATUS_CHANGE",
1185
+ "data": {
1186
+ "status": "concluded",
1187
+ "id": "commit0"
1188
+ }
1189
+}
1190
+{
1191
+ "timestamp": {
1192
+ "seconds": TIMESTAMP,
1193
+ "microseconds": TIMESTAMP
1194
+ },
1195
+ "event": "JOB_STATUS_CHANGE",
1196
+ "data": {
1197
+ "status": "null",
1198
+ "id": "commit0"
1199
+ }
1200
+}
1201
1202
=== Check that both top and top2 point to base now ===
1203
1204
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1205
=== Perform commit job ===
1206
1207
{
1208
+ "timestamp": {
1209
+ "seconds": TIMESTAMP,
1210
+ "microseconds": TIMESTAMP
1211
+ },
1212
+ "event": "JOB_STATUS_CHANGE",
1213
+ "data": {
1214
+ "status": "created",
1215
+ "id": "commit0"
1216
+ }
1217
+}
1218
+{
1219
+ "timestamp": {
1220
+ "seconds": TIMESTAMP,
1221
+ "microseconds": TIMESTAMP
1222
+ },
1223
+ "event": "JOB_STATUS_CHANGE",
1224
+ "data": {
1225
+ "status": "running",
1226
+ "id": "commit0"
1227
+ }
1228
+}
1229
+{
1230
"return": {
1231
}
1232
}
1233
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1234
"seconds": TIMESTAMP,
1235
"microseconds": TIMESTAMP
1236
},
1237
+ "event": "JOB_STATUS_CHANGE",
1238
+ "data": {
1239
+ "status": "waiting",
1240
+ "id": "commit0"
1241
+ }
1242
+}
1243
+{
1244
+ "timestamp": {
1245
+ "seconds": TIMESTAMP,
1246
+ "microseconds": TIMESTAMP
1247
+ },
1248
+ "event": "JOB_STATUS_CHANGE",
1249
+ "data": {
1250
+ "status": "pending",
1251
+ "id": "commit0"
1252
+ }
1253
+}
1254
+{
1255
+ "timestamp": {
1256
+ "seconds": TIMESTAMP,
1257
+ "microseconds": TIMESTAMP
1258
+ },
1259
"event": "BLOCK_JOB_COMPLETED",
1260
"data": {
1261
"device": "commit0",
1262
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1263
"type": "commit"
1264
}
1265
}
1266
+{
1267
+ "timestamp": {
1268
+ "seconds": TIMESTAMP,
1269
+ "microseconds": TIMESTAMP
1270
+ },
1271
+ "event": "JOB_STATUS_CHANGE",
1272
+ "data": {
1273
+ "status": "concluded",
1274
+ "id": "commit0"
1275
+ }
1276
+}
1277
+{
1278
+ "timestamp": {
1279
+ "seconds": TIMESTAMP,
1280
+ "microseconds": TIMESTAMP
1281
+ },
1282
+ "event": "JOB_STATUS_CHANGE",
1283
+ "data": {
1284
+ "status": "null",
1285
+ "id": "commit0"
1286
+ }
1287
+}
1288
1289
=== Check that both top and top2 point to base now ===
1290
1291
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
1292
index XXXXXXX..XXXXXXX 100644
1293
--- a/tests/qemu-iotests/iotests.py
1294
+++ b/tests/qemu-iotests/iotests.py
1295
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1296
self.assert_qmp(event, 'data/device', drive)
1297
result = event
1298
cancelled = True
1299
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1300
+ self.assert_qmp(event, 'data/id', drive)
1301
+
1302
1303
self.assert_no_active_block_jobs()
1304
return result
1305
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1306
self.assert_qmp(event, 'data/offset', event['data']['len'])
1307
self.assert_no_active_block_jobs()
1308
return event
1309
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1310
+ self.assert_qmp(event, 'data/id', drive)
1311
1312
def wait_ready(self, drive='drive0'):
1313
'''Wait until a block job BLOCK_JOB_READY event'''
1314
--
32
--
1315
2.13.6
33
2.19.1
1316
34
1317
35
diff view generated by jsdifflib
1
This is the first step towards creating an infrastructure for generic
1
If read-only=off, but auto-read-only=on is given, open the file
2
background jobs that aren't tied to a block device. For now, Job only
2
read-write if we have the permissions, but instead of erroring out for
3
stores its ID and JobDriver, the rest stays in BlockJob.
3
read-only files, just degrade to read-only.
4
5
The following patches will move over more parts of BlockJob to Job if
6
they are meaningful outside the context of a block job.
7
8
BlockJob.driver is now redundant, but this patch leaves it around to
9
avoid unnecessary churn. The next patches will get rid of almost all of
10
its uses anyway so that it can be removed later with much less churn.
11
4
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Reviewed-by: John Snow <jsnow@redhat.com>
15
---
7
---
16
include/block/blockjob.h | 9 +++----
8
block/file-posix.c | 19 ++++++++++++++++---
17
include/block/blockjob_int.h | 4 +--
9
1 file changed, 16 insertions(+), 3 deletions(-)
18
include/qemu/job.h | 60 ++++++++++++++++++++++++++++++++++++++++++++
19
block/backup.c | 4 ++-
20
block/commit.c | 4 ++-
21
block/mirror.c | 10 +++++---
22
block/stream.c | 4 ++-
23
blockjob.c | 46 ++++++++++++++++-----------------
24
job.c | 48 +++++++++++++++++++++++++++++++++++
25
tests/test-bdrv-drain.c | 4 ++-
26
tests/test-blockjob-txn.c | 4 ++-
27
tests/test-blockjob.c | 12 ++++++---
28
MAINTAINERS | 2 ++
29
Makefile.objs | 2 +-
30
14 files changed, 169 insertions(+), 44 deletions(-)
31
create mode 100644 include/qemu/job.h
32
create mode 100644 job.c
33
10
34
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
11
diff --git a/block/file-posix.c b/block/file-posix.c
35
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/blockjob.h
13
--- a/block/file-posix.c
37
+++ b/include/block/blockjob.h
14
+++ b/block/file-posix.c
38
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static int raw_open_common(BlockDriverState *bs, QDict *options,
39
#ifndef BLOCKJOB_H
16
40
#define BLOCKJOB_H
17
s->fd = -1;
41
18
fd = qemu_open(filename, s->open_flags, 0644);
42
+#include "qemu/job.h"
19
- if (fd < 0) {
43
#include "block/block.h"
20
- ret = -errno;
44
#include "qemu/ratelimit.h"
21
- error_setg_errno(errp, errno, "Could not open '%s'", filename);
45
22
+ ret = fd < 0 ? -errno : 0;
46
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJobTxn BlockJobTxn;
47
* Long-running operation on a BlockDriverState.
48
*/
49
typedef struct BlockJob {
50
+ /** Data belonging to the generic Job infrastructure */
51
+ Job job;
52
+
23
+
53
/** The job type, including the job vtable. */
24
+ if (ret == -EACCES || ret == -EROFS) {
54
const BlockJobDriver *driver;
25
+ /* Try to degrade to read-only, but if it doesn't work, still use the
55
26
+ * normal error message. */
56
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
27
+ if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
57
BlockBackend *blk;
28
+ bdrv_flags &= ~BDRV_O_RDWR;
58
29
+ raw_parse_flags(bdrv_flags, &s->open_flags);
59
/**
30
+ assert(!(s->open_flags & O_CREAT));
60
- * The ID of the block job. May be NULL for internal jobs.
31
+ fd = qemu_open(filename, s->open_flags);
61
- */
32
+ ret = fd < 0 ? -errno : 0;
62
- char *id;
63
-
64
- /**
65
* The coroutine that executes the job. If not NULL, it is
66
* reentered when busy is false and the job is cancelled.
67
*/
68
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
69
index XXXXXXX..XXXXXXX 100644
70
--- a/include/block/blockjob_int.h
71
+++ b/include/block/blockjob_int.h
72
@@ -XXX,XX +XXX,XX @@
73
* A class type for block job driver.
74
*/
75
struct BlockJobDriver {
76
- /** Derived BlockJob struct size */
77
- size_t instance_size;
78
+ /** Generic JobDriver callbacks and settings */
79
+ JobDriver job_driver;
80
81
/** String describing the operation, part of query-block-jobs QMP API */
82
BlockJobType job_type;
83
diff --git a/include/qemu/job.h b/include/qemu/job.h
84
new file mode 100644
85
index XXXXXXX..XXXXXXX
86
--- /dev/null
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@
89
+/*
90
+ * Declarations for background jobs
91
+ *
92
+ * Copyright (c) 2011 IBM Corp.
93
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
94
+ *
95
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
96
+ * of this software and associated documentation files (the "Software"), to deal
97
+ * in the Software without restriction, including without limitation the rights
98
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
+ * copies of the Software, and to permit persons to whom the Software is
100
+ * furnished to do so, subject to the following conditions:
101
+ *
102
+ * The above copyright notice and this permission notice shall be included in
103
+ * all copies or substantial portions of the Software.
104
+ *
105
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
108
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
110
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
111
+ * THE SOFTWARE.
112
+ */
113
+
114
+#ifndef JOB_H
115
+#define JOB_H
116
+
117
+typedef struct JobDriver JobDriver;
118
+
119
+/**
120
+ * Long-running operation.
121
+ */
122
+typedef struct Job {
123
+ /** The ID of the job. May be NULL for internal jobs. */
124
+ char *id;
125
+
126
+ /** The type of this job. */
127
+ const JobDriver *driver;
128
+} Job;
129
+
130
+/**
131
+ * Callbacks and other information about a Job driver.
132
+ */
133
+struct JobDriver {
134
+ /** Derived Job struct size */
135
+ size_t instance_size;
136
+};
137
+
138
+
139
+/**
140
+ * Create a new long-running job and return it.
141
+ *
142
+ * @job_id: The id of the newly-created job, or %NULL for internal jobs
143
+ * @driver: The class object for the newly-created job.
144
+ * @errp: Error object.
145
+ */
146
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
147
+
148
+#endif
149
diff --git a/block/backup.c b/block/backup.c
150
index XXXXXXX..XXXXXXX 100644
151
--- a/block/backup.c
152
+++ b/block/backup.c
153
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
154
}
155
156
static const BlockJobDriver backup_job_driver = {
157
- .instance_size = sizeof(BackupBlockJob),
158
+ .job_driver = {
159
+ .instance_size = sizeof(BackupBlockJob),
160
+ },
161
.job_type = BLOCK_JOB_TYPE_BACKUP,
162
.start = backup_run,
163
.commit = backup_commit,
164
diff --git a/block/commit.c b/block/commit.c
165
index XXXXXXX..XXXXXXX 100644
166
--- a/block/commit.c
167
+++ b/block/commit.c
168
@@ -XXX,XX +XXX,XX @@ out:
169
}
170
171
static const BlockJobDriver commit_job_driver = {
172
- .instance_size = sizeof(CommitBlockJob),
173
+ .job_driver = {
174
+ .instance_size = sizeof(CommitBlockJob),
175
+ },
176
.job_type = BLOCK_JOB_TYPE_COMMIT,
177
.start = commit_run,
178
};
179
diff --git a/block/mirror.c b/block/mirror.c
180
index XXXXXXX..XXXXXXX 100644
181
--- a/block/mirror.c
182
+++ b/block/mirror.c
183
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
184
185
if (!s->synced) {
186
error_setg(errp, "The active block job '%s' cannot be completed",
187
- job->id);
188
+ job->job.id);
189
return;
190
}
191
192
@@ -XXX,XX +XXX,XX @@ static void mirror_drain(BlockJob *job)
193
}
194
195
static const BlockJobDriver mirror_job_driver = {
196
- .instance_size = sizeof(MirrorBlockJob),
197
+ .job_driver = {
198
+ .instance_size = sizeof(MirrorBlockJob),
199
+ },
200
.job_type = BLOCK_JOB_TYPE_MIRROR,
201
.start = mirror_run,
202
.complete = mirror_complete,
203
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
204
};
205
206
static const BlockJobDriver commit_active_job_driver = {
207
- .instance_size = sizeof(MirrorBlockJob),
208
+ .job_driver = {
209
+ .instance_size = sizeof(MirrorBlockJob),
210
+ },
211
.job_type = BLOCK_JOB_TYPE_COMMIT,
212
.start = mirror_run,
213
.complete = mirror_complete,
214
diff --git a/block/stream.c b/block/stream.c
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/stream.c
217
+++ b/block/stream.c
218
@@ -XXX,XX +XXX,XX @@ out:
219
}
220
221
static const BlockJobDriver stream_job_driver = {
222
- .instance_size = sizeof(StreamBlockJob),
223
+ .job_driver = {
224
+ .instance_size = sizeof(StreamBlockJob),
225
+ },
226
.job_type = BLOCK_JOB_TYPE_STREAM,
227
.start = stream_run,
228
};
229
diff --git a/blockjob.c b/blockjob.c
230
index XXXXXXX..XXXXXXX 100644
231
--- a/blockjob.c
232
+++ b/blockjob.c
233
@@ -XXX,XX +XXX,XX @@
234
#include "qapi/qapi-events-block-core.h"
235
#include "qapi/qmp/qerror.h"
236
#include "qemu/coroutine.h"
237
-#include "qemu/id.h"
238
#include "qemu/timer.h"
239
240
/* Right now, this mutex is only needed to synchronize accesses to job->busy
241
@@ -XXX,XX +XXX,XX @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
242
return 0;
243
}
244
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
245
- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv));
246
+ job->job.id, BlockJobStatus_str(job->status),
247
+ BlockJobVerb_str(bv));
248
return -EPERM;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
252
BlockJob *job;
253
254
QLIST_FOREACH(job, &block_jobs, job_list) {
255
- if (job->id && !strcmp(id, job->id)) {
256
+ if (job->job.id && !strcmp(id, job->job.id)) {
257
return job;
258
}
259
}
260
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
261
block_job_detach_aio_context, job);
262
blk_unref(job->blk);
263
error_free(job->blocker);
264
- g_free(job->id);
265
+ g_free(job->job.id);
266
assert(!timer_pending(&job->sleep_timer));
267
g_free(job);
268
}
269
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
270
BlockJob *job = c->opaque;
271
return g_strdup_printf("%s job '%s'",
272
BlockJobType_str(job->driver->job_type),
273
- job->id);
274
+ job->job.id);
275
}
276
277
static void child_job_drained_begin(BdrvChild *c)
278
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
279
280
bool block_job_is_internal(BlockJob *job)
281
{
282
- return (job->id == NULL);
283
+ return (job->job.id == NULL);
284
}
285
286
static bool block_job_started(BlockJob *job)
287
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
288
void block_job_complete(BlockJob *job, Error **errp)
289
{
290
/* Should not be reachable via external interface for internal jobs */
291
- assert(job->id);
292
+ assert(job->job.id);
293
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
294
return;
295
}
296
if (job->pause_count || job->cancelled || !job->driver->complete) {
297
error_setg(errp, "The active block job '%s' cannot be completed",
298
- job->id);
299
+ job->job.id);
300
return;
301
}
302
303
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
304
305
void block_job_finalize(BlockJob *job, Error **errp)
306
{
307
- assert(job && job->id);
308
+ assert(job && job->job.id);
309
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
310
return;
311
}
312
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
313
{
314
BlockJob *job = *jobptr;
315
/* similarly to _complete, this is QMP-interface only. */
316
- assert(job->id);
317
+ assert(job->job.id);
318
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
319
return;
320
}
321
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
322
}
323
info = g_new0(BlockJobInfo, 1);
324
info->type = g_strdup(BlockJobType_str(job->driver->job_type));
325
- info->device = g_strdup(job->id);
326
+ info->device = g_strdup(job->job.id);
327
info->len = job->len;
328
info->busy = atomic_read(&job->busy);
329
info->paused = job->pause_count > 0;
330
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
331
}
332
333
qapi_event_send_block_job_cancelled(job->driver->job_type,
334
- job->id,
335
+ job->job.id,
336
job->len,
337
job->offset,
338
job->speed,
339
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
340
}
341
342
qapi_event_send_block_job_completed(job->driver->job_type,
343
- job->id,
344
+ job->job.id,
345
job->len,
346
job->offset,
347
job->speed,
348
@@ -XXX,XX +XXX,XX @@ static int block_job_event_pending(BlockJob *job)
349
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
350
if (!job->auto_finalize && !block_job_is_internal(job)) {
351
qapi_event_send_block_job_pending(job->driver->job_type,
352
- job->id,
353
+ job->job.id,
354
&error_abort);
355
}
356
return 0;
357
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
358
error_setg(errp, "Cannot specify job ID for internal block job");
359
return NULL;
360
}
361
-
362
- if (!id_wellformed(job_id)) {
363
- error_setg(errp, "Invalid job ID '%s'", job_id);
364
- return NULL;
365
- }
366
-
367
if (block_job_get(job_id)) {
368
error_setg(errp, "Job ID '%s' already in use", job_id);
369
return NULL;
370
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
371
return NULL;
372
}
373
374
- job = g_malloc0(driver->instance_size);
375
+ job = job_create(job_id, &driver->job_driver, errp);
376
+ if (job == NULL) {
377
+ blk_unref(blk);
378
+ return NULL;
379
+ }
380
+
381
job->driver = driver;
382
- job->id = g_strdup(job_id);
383
job->blk = blk;
384
job->cb = cb;
385
job->opaque = opaque;
386
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job)
387
}
388
389
qapi_event_send_block_job_ready(job->driver->job_type,
390
- job->id,
391
+ job->job.id,
392
job->len,
393
job->offset,
394
job->speed, &error_abort);
395
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
396
abort();
397
}
398
if (!block_job_is_internal(job)) {
399
- qapi_event_send_block_job_error(job->id,
400
+ qapi_event_send_block_job_error(job->job.id,
401
is_read ? IO_OPERATION_TYPE_READ :
402
IO_OPERATION_TYPE_WRITE,
403
action, &error_abort);
404
diff --git a/job.c b/job.c
405
new file mode 100644
406
index XXXXXXX..XXXXXXX
407
--- /dev/null
408
+++ b/job.c
409
@@ -XXX,XX +XXX,XX @@
410
+/*
411
+ * Background jobs (long-running operations)
412
+ *
413
+ * Copyright (c) 2011 IBM Corp.
414
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
415
+ *
416
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
417
+ * of this software and associated documentation files (the "Software"), to deal
418
+ * in the Software without restriction, including without limitation the rights
419
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
420
+ * copies of the Software, and to permit persons to whom the Software is
421
+ * furnished to do so, subject to the following conditions:
422
+ *
423
+ * The above copyright notice and this permission notice shall be included in
424
+ * all copies or substantial portions of the Software.
425
+ *
426
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
427
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
428
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
429
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
430
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
431
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
432
+ * THE SOFTWARE.
433
+ */
434
+
435
+#include "qemu/osdep.h"
436
+#include "qemu-common.h"
437
+#include "qapi/error.h"
438
+#include "qemu/job.h"
439
+#include "qemu/id.h"
440
+
441
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
442
+{
443
+ Job *job;
444
+
445
+ if (job_id) {
446
+ if (!id_wellformed(job_id)) {
447
+ error_setg(errp, "Invalid job ID '%s'", job_id);
448
+ return NULL;
449
+ }
33
+ }
450
+ }
34
+ }
451
+
35
+
452
+ job = g_malloc0(driver->instance_size);
36
+ if (ret < 0) {
453
+ job->driver = driver;
37
+ error_setg_errno(errp, -ret, "Could not open '%s'", filename);
454
+ job->id = g_strdup(job_id);
38
if (ret == -EROFS) {
455
+
39
ret = -EACCES;
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
}
40
}
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
--
41
--
553
2.13.6
42
2.19.1
554
43
555
44
diff view generated by jsdifflib
1
This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL
1
If read-only=off, but auto-read-only=on is given, just degrade to
2
checks to job_create() and the auto_{finalize,dismiss} fields from
2
read-only.
3
BlockJob to Job.
4
3
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
7
---
6
---
8
include/block/blockjob.h | 17 -----------------
7
block/curl.c | 8 ++++----
9
include/block/blockjob_int.h | 3 +--
8
1 file changed, 4 insertions(+), 4 deletions(-)
10
include/qemu/job.h | 20 +++++++++++++++++++-
11
block/commit.c | 2 +-
12
block/mirror.c | 2 +-
13
block/replication.c | 4 ++--
14
block/stream.c | 2 +-
15
blockdev.c | 14 +++++++-------
16
blockjob.c | 27 +++++++--------------------
17
job.c | 11 ++++++++++-
18
qemu-img.c | 2 +-
19
tests/test-blockjob-txn.c | 2 +-
20
tests/test-blockjob.c | 4 ++--
21
13 files changed, 53 insertions(+), 57 deletions(-)
22
9
23
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
10
diff --git a/block/curl.c b/block/curl.c
24
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob.h
12
--- a/block/curl.c
26
+++ b/include/block/blockjob.h
13
+++ b/block/curl.c
27
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
14
@@ -XXX,XX +XXX,XX @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags,
28
/** ret code passed to block_job_completed. */
15
const char *protocol_delimiter;
29
int ret;
16
int ret;
30
17
31
- /** True if this job should automatically finalize itself */
32
- bool auto_finalize;
33
-
18
-
34
- /** True if this job should automatically dismiss itself */
19
- if (flags & BDRV_O_RDWR) {
35
- bool auto_dismiss;
20
- error_setg(errp, "curl block device does not support writes");
36
-
21
- return -EROFS;
37
BlockJobTxn *txn;
22
+ ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes",
38
QLIST_ENTRY(BlockJob) txn_list;
23
+ errp);
39
} BlockJob;
24
+ if (ret < 0) {
40
25
+ return ret;
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
}
26
}
123
27
124
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
28
if (!libcurl_initialized) {
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
357
index XXXXXXX..XXXXXXX 100644
358
--- a/qemu-img.c
359
+++ b/qemu-img.c
360
@@ -XXX,XX +XXX,XX @@ static int img_commit(int argc, char **argv)
361
362
aio_context = bdrv_get_aio_context(bs);
363
aio_context_acquire(aio_context);
364
- commit_active_start("commit", bs, base_bs, BLOCK_JOB_DEFAULT, 0,
365
+ commit_active_start("commit", bs, base_bs, JOB_DEFAULT, 0,
366
BLOCKDEV_ON_ERROR_REPORT, NULL, common_block_job_cb,
367
&cbi, false, &local_err);
368
aio_context_release(aio_context);
369
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
370
index XXXXXXX..XXXXXXX 100644
371
--- a/tests/test-blockjob-txn.c
372
+++ b/tests/test-blockjob-txn.c
373
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
374
375
snprintf(job_id, sizeof(job_id), "job%u", counter++);
376
s = block_job_create(job_id, &test_block_job_driver, txn, bs,
377
- 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
378
+ 0, BLK_PERM_ALL, 0, JOB_DEFAULT,
379
test_block_job_cb, data, &error_abort);
380
s->iterations = iterations;
381
s->use_timer = use_timer;
382
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
383
index XXXXXXX..XXXXXXX 100644
384
--- a/tests/test-blockjob.c
385
+++ b/tests/test-blockjob.c
386
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
387
bool should_succeed)
388
{
389
return mk_job(blk, id, &test_block_job_driver,
390
- should_succeed, BLOCK_JOB_DEFAULT);
391
+ should_succeed, JOB_DEFAULT);
392
}
393
394
/* This creates a BlockBackend (optionally with a name) with a
395
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
396
397
blk = create_blk(NULL);
398
job = mk_job(blk, "Steve", &test_cancel_driver, true,
399
- BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
400
+ JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS);
401
job_ref(&job->job);
402
assert(job->job.status == JOB_STATUS_CREATED);
403
s = container_of(job, CancelJob, common);
404
--
29
--
405
2.13.6
30
2.19.1
406
31
407
32
diff view generated by jsdifflib
1
This moves the job list from BlockJob to Job. Now we can check for
1
If read-only=off, but auto-read-only=on is given, open the file
2
duplicate IDs in job_create().
2
read-write if we have the permissions, but instead of erroring out for
3
read-only files, just degrade to read-only.
3
4
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Niels de Vos <ndevos@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
---
7
---
8
include/block/blockjob.h | 3 ---
8
block/gluster.c | 12 ++++++++++--
9
include/qemu/job.h | 19 +++++++++++++++++++
9
1 file changed, 10 insertions(+), 2 deletions(-)
10
blockjob.c | 46 ++++++++++++++++++++++++----------------------
11
job.c | 31 +++++++++++++++++++++++++++++++
12
4 files changed, 74 insertions(+), 25 deletions(-)
13
10
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
11
diff --git a/block/gluster.c b/block/gluster.c
15
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
13
--- a/block/gluster.c
17
+++ b/include/block/blockjob.h
14
+++ b/block/gluster.c
18
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
15
@@ -XXX,XX +XXX,XX @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options,
19
*/
16
qemu_gluster_parse_flags(bdrv_flags, &open_flags);
20
bool deferred_to_main_loop;
17
21
18
s->fd = glfs_open(s->glfs, gconf->path, open_flags);
22
- /** Element of the list of block jobs */
19
- if (!s->fd) {
23
- QLIST_ENTRY(BlockJob) job_list;
20
- ret = -errno;
24
-
21
+ ret = s->fd ? 0 : -errno;
25
/** Status that is published by the query-block-jobs QMP API */
26
BlockDeviceIoStatus iostatus;
27
28
diff --git a/include/qemu/job.h b/include/qemu/job.h
29
index XXXXXXX..XXXXXXX 100644
30
--- a/include/qemu/job.h
31
+++ b/include/qemu/job.h
32
@@ -XXX,XX +XXX,XX @@
33
#define JOB_H
34
35
#include "qapi/qapi-types-block-core.h"
36
+#include "qemu/queue.h"
37
38
typedef struct JobDriver JobDriver;
39
40
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
41
42
/** The type of this job. */
43
const JobDriver *driver;
44
+
22
+
45
+ /** Element of the list of jobs */
23
+ if (ret == -EACCES || ret == -EROFS) {
46
+ QLIST_ENTRY(Job) job_list;
24
+ /* Try to degrade to read-only, but if it doesn't work, still use the
47
} Job;
25
+ * normal error message. */
48
26
+ if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) {
49
/**
27
+ open_flags = (open_flags & ~O_RDWR) | O_RDONLY;
50
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
28
+ s->fd = glfs_open(s->glfs, gconf->path, open_flags);
51
/** Returns the enum string for the JobType of a given Job. */
29
+ ret = s->fd ? 0 : -errno;
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
{
90
- if (!job) {
91
- return QLIST_FIRST(&block_jobs);
92
- }
93
- return QLIST_NEXT(job, job_list);
94
+ return job_type(job) == JOB_TYPE_BACKUP ||
95
+ job_type(job) == JOB_TYPE_COMMIT ||
96
+ job_type(job) == JOB_TYPE_MIRROR ||
97
+ job_type(job) == JOB_TYPE_STREAM;
98
+}
99
+
100
+BlockJob *block_job_next(BlockJob *bjob)
101
+{
102
+ Job *job = bjob ? &bjob->job : NULL;
103
+
104
+ do {
105
+ job = job_next(job);
106
+ } while (job && !is_block_job(job));
107
+
108
+ return job ? container_of(job, BlockJob, job) : NULL;
109
}
110
111
BlockJob *block_job_get(const char *id)
112
{
113
- BlockJob *job;
114
+ Job *job = job_get(id);
115
116
- QLIST_FOREACH(job, &block_jobs, job_list) {
117
- if (job->job.id && !strcmp(id, job->job.id)) {
118
- return job;
119
- }
120
+ if (job && is_block_job(job)) {
121
+ return container_of(job, BlockJob, job);
122
+ } else {
123
+ return NULL;
124
}
125
-
126
- return NULL;
127
}
128
129
BlockJobTxn *block_job_txn_new(void)
130
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
131
assert(job->status == BLOCK_JOB_STATUS_NULL);
132
assert(!job->txn);
133
BlockDriverState *bs = blk_bs(job->blk);
134
- QLIST_REMOVE(job, job_list);
135
bs->job = NULL;
136
block_job_remove_all_bdrv(job);
137
blk_remove_aio_context_notifier(job->blk,
138
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
139
BlockJob *job;
140
AioContext *aio_context;
141
142
- while ((job = QLIST_FIRST(&block_jobs))) {
143
+ while ((job = block_job_next(NULL))) {
144
aio_context = blk_get_aio_context(job->blk);
145
aio_context_acquire(aio_context);
146
block_job_cancel_sync(job);
147
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
148
error_setg(errp, "Cannot specify job ID for internal block job");
149
return NULL;
150
}
151
- if (block_job_get(job_id)) {
152
- error_setg(errp, "Job ID '%s' already in use", job_id);
153
- return NULL;
154
- }
155
}
156
157
blk = blk_new(perm, shared_perm);
158
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
159
return NULL;
160
}
161
162
+ assert(is_block_job(&job->job));
163
+
164
job->driver = driver;
165
job->blk = blk;
166
job->cb = cb;
167
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
168
169
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
170
171
- QLIST_INSERT_HEAD(&block_jobs, job, job_list);
172
-
173
blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
174
block_job_detach_aio_context, job);
175
176
diff --git a/job.c b/job.c
177
index XXXXXXX..XXXXXXX 100644
178
--- a/job.c
179
+++ b/job.c
180
@@ -XXX,XX +XXX,XX @@
181
#include "qemu/job.h"
182
#include "qemu/id.h"
183
184
+static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
185
+
186
JobType job_type(const Job *job)
187
{
188
return job->driver->job_type;
189
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job)
190
return JobType_str(job_type(job));
191
}
192
193
+Job *job_next(Job *job)
194
+{
195
+ if (!job) {
196
+ return QLIST_FIRST(&jobs);
197
+ }
198
+ return QLIST_NEXT(job, job_list);
199
+}
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
+ }
210
+
211
+ return NULL;
212
+}
213
+
214
void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
215
{
216
Job *job;
217
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
218
error_setg(errp, "Invalid job ID '%s'", job_id);
219
return NULL;
220
}
221
+ if (job_get(job_id)) {
222
+ error_setg(errp, "Job ID '%s' already in use", job_id);
223
+ return NULL;
224
+ }
30
+ }
225
}
31
}
226
32
227
job = g_malloc0(driver->instance_size);
33
s->supports_seek_data = qemu_gluster_test_seek(s->fd);
228
job->driver = driver;
229
job->id = g_strdup(job_id);
230
231
+ QLIST_INSERT_HEAD(&jobs, job, job_list);
232
+
233
return job;
234
}
235
236
void job_delete(Job *job)
237
{
238
+ QLIST_REMOVE(job, job_list);
239
+
240
g_free(job->id);
241
g_free(job);
242
}
243
--
34
--
244
2.13.6
35
2.19.1
245
36
246
37
diff view generated by jsdifflib
1
This adds a separate schema file for all job-related definitions that
1
If read-only=off, but auto-read-only=on is given, open the volume
2
aren't tied to the block layer.
2
read-write if we have the permissions, but instead of erroring out for
3
3
read-only volumes, just degrade to read-only.
4
For a start, move the enums JobType, JobStatus and JobVerb.
5
4
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
7
---
9
qapi/block-core.json | 90 +-----------------------------------------------
8
block/iscsi.c | 8 +++++---
10
qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
9
1 file changed, 5 insertions(+), 3 deletions(-)
11
qapi/qapi-schema.json | 1 +
12
MAINTAINERS | 1 +
13
Makefile | 9 +++++
14
Makefile.objs | 4 +++
15
6 files changed, 110 insertions(+), 89 deletions(-)
16
create mode 100644 qapi/job.json
17
10
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
11
diff --git a/block/iscsi.c b/block/iscsi.c
19
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
13
--- a/block/iscsi.c
21
+++ b/qapi/block-core.json
14
+++ b/block/iscsi.c
22
@@ -XXX,XX +XXX,XX @@
15
@@ -XXX,XX +XXX,XX @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags,
23
16
/* Check the write protect flag of the LUN if we want to write */
24
{ 'include': 'common.json' }
17
if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) &&
25
{ 'include': 'crypto.json' }
18
iscsilun->write_protected) {
26
+{ 'include': 'job.json' }
19
- error_setg(errp, "Cannot open a write protected LUN as read-write");
27
{ 'include': 'sockets.json' }
20
- ret = -EACCES;
28
21
- goto out;
29
##
22
+ ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp);
30
@@ -XXX,XX +XXX,XX @@
23
+ if (ret < 0) {
31
'data': ['top', 'full', 'none', 'incremental'] }
24
+ goto out;
32
25
+ }
33
##
26
+ flags &= ~BDRV_O_RDWR;
34
-# @JobType:
27
}
35
-#
28
36
-# Type of a background job.
29
iscsi_readcapacity_sync(iscsilun, &local_err);
37
-#
38
-# @commit: block commit job type, see "block-commit"
39
-#
40
-# @stream: block stream job type, see "block-stream"
41
-#
42
-# @mirror: drive mirror job type, see "drive-mirror"
43
-#
44
-# @backup: drive backup job type, see "drive-backup"
45
-#
46
-# Since: 1.7
47
-##
48
-{ 'enum': 'JobType',
49
- 'data': ['commit', 'stream', 'mirror', 'backup'] }
50
-
51
-##
52
-# @JobVerb:
53
-#
54
-# Represents command verbs that can be applied to a job.
55
-#
56
-# @cancel: see @block-job-cancel
57
-#
58
-# @pause: see @block-job-pause
59
-#
60
-# @resume: see @block-job-resume
61
-#
62
-# @set-speed: see @block-job-set-speed
63
-#
64
-# @complete: see @block-job-complete
65
-#
66
-# @dismiss: see @block-job-dismiss
67
-#
68
-# @finalize: see @block-job-finalize
69
-#
70
-# Since: 2.12
71
-##
72
-{ 'enum': 'JobVerb',
73
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
74
- 'finalize' ] }
75
-
76
-##
77
-# @JobStatus:
78
-#
79
-# Indicates the present state of a given job in its lifetime.
80
-#
81
-# @undefined: Erroneous, default state. Should not ever be visible.
82
-#
83
-# @created: The job has been created, but not yet started.
84
-#
85
-# @running: The job is currently running.
86
-#
87
-# @paused: The job is running, but paused. The pause may be requested by
88
-# either the QMP user or by internal processes.
89
-#
90
-# @ready: The job is running, but is ready for the user to signal completion.
91
-# This is used for long-running jobs like mirror that are designed to
92
-# run indefinitely.
93
-#
94
-# @standby: The job is ready, but paused. This is nearly identical to @paused.
95
-# The job may return to @ready or otherwise be canceled.
96
-#
97
-# @waiting: The job is waiting for other jobs in the transaction to converge
98
-# to the waiting state. This status will likely not be visible for
99
-# the last job in a transaction.
100
-#
101
-# @pending: The job has finished its work, but has finalization steps that it
102
-# needs to make prior to completing. These changes may require
103
-# manual intervention by the management process if manual was set
104
-# to true. These changes may still fail.
105
-#
106
-# @aborting: The job is in the process of being aborted, and will finish with
107
-# an error. The job will afterwards report that it is @concluded.
108
-# This status may not be visible to the management process.
109
-#
110
-# @concluded: The job has finished all work. If manual was set to true, the job
111
-# will remain in the query list until it is dismissed.
112
-#
113
-# @null: The job is in the process of being dismantled. This state should not
114
-# ever be visible externally.
115
-#
116
-# Since: 2.12
117
-##
118
-{ 'enum': 'JobStatus',
119
- 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
120
- 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
121
-
122
-##
123
# @BlockJobInfo:
124
#
125
# Information about a long-running block device operation.
126
diff --git a/qapi/job.json b/qapi/job.json
127
new file mode 100644
128
index XXXXXXX..XXXXXXX
129
--- /dev/null
130
+++ b/qapi/job.json
131
@@ -XXX,XX +XXX,XX @@
132
+# -*- Mode: Python -*-
133
+
134
+##
135
+# == Background jobs
136
+##
137
+
138
+##
139
+# @JobType:
140
+#
141
+# Type of a background job.
142
+#
143
+# @commit: block commit job type, see "block-commit"
144
+#
145
+# @stream: block stream job type, see "block-stream"
146
+#
147
+# @mirror: drive mirror job type, see "drive-mirror"
148
+#
149
+# @backup: drive backup job type, see "drive-backup"
150
+#
151
+# Since: 1.7
152
+##
153
+{ 'enum': 'JobType',
154
+ 'data': ['commit', 'stream', 'mirror', 'backup'] }
155
+
156
+##
157
+# @JobStatus:
158
+#
159
+# Indicates the present state of a given job in its lifetime.
160
+#
161
+# @undefined: Erroneous, default state. Should not ever be visible.
162
+#
163
+# @created: The job has been created, but not yet started.
164
+#
165
+# @running: The job is currently running.
166
+#
167
+# @paused: The job is running, but paused. The pause may be requested by
168
+# either the QMP user or by internal processes.
169
+#
170
+# @ready: The job is running, but is ready for the user to signal completion.
171
+# This is used for long-running jobs like mirror that are designed to
172
+# run indefinitely.
173
+#
174
+# @standby: The job is ready, but paused. This is nearly identical to @paused.
175
+# The job may return to @ready or otherwise be canceled.
176
+#
177
+# @waiting: The job is waiting for other jobs in the transaction to converge
178
+# to the waiting state. This status will likely not be visible for
179
+# the last job in a transaction.
180
+#
181
+# @pending: The job has finished its work, but has finalization steps that it
182
+# needs to make prior to completing. These changes may require
183
+# manual intervention by the management process if manual was set
184
+# to true. These changes may still fail.
185
+#
186
+# @aborting: The job is in the process of being aborted, and will finish with
187
+# an error. The job will afterwards report that it is @concluded.
188
+# This status may not be visible to the management process.
189
+#
190
+# @concluded: The job has finished all work. If manual was set to true, the job
191
+# will remain in the query list until it is dismissed.
192
+#
193
+# @null: The job is in the process of being dismantled. This state should not
194
+# ever be visible externally.
195
+#
196
+# Since: 2.12
197
+##
198
+{ 'enum': 'JobStatus',
199
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
200
+ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
201
+
202
+##
203
+# @JobVerb:
204
+#
205
+# Represents command verbs that can be applied to a job.
206
+#
207
+# @cancel: see @block-job-cancel
208
+#
209
+# @pause: see @block-job-pause
210
+#
211
+# @resume: see @block-job-resume
212
+#
213
+# @set-speed: see @block-job-set-speed
214
+#
215
+# @complete: see @block-job-complete
216
+#
217
+# @dismiss: see @block-job-dismiss
218
+#
219
+# @finalize: see @block-job-finalize
220
+#
221
+# Since: 2.12
222
+##
223
+{ 'enum': 'JobVerb',
224
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
225
+ 'finalize' ] }
226
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
227
index XXXXXXX..XXXXXXX 100644
228
--- a/qapi/qapi-schema.json
229
+++ b/qapi/qapi-schema.json
230
@@ -XXX,XX +XXX,XX @@
231
{ 'include': 'crypto.json' }
232
{ 'include': 'block.json' }
233
{ 'include': 'char.json' }
234
+{ 'include': 'job.json' }
235
{ 'include': 'net.json' }
236
{ 'include': 'rocker.json' }
237
{ 'include': 'tpm.json' }
238
diff --git a/MAINTAINERS b/MAINTAINERS
239
index XXXXXXX..XXXXXXX 100644
240
--- a/MAINTAINERS
241
+++ b/MAINTAINERS
242
@@ -XXX,XX +XXX,XX @@ F: block/backup.c
243
F: block/commit.c
244
F: block/stream.c
245
F: block/mirror.c
246
+F: qapi/job.json
247
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
248
249
Block QAPI, monitor, command line
250
diff --git a/Makefile b/Makefile
251
index XXXXXXX..XXXXXXX 100644
252
--- a/Makefile
253
+++ b/Makefile
254
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
255
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
256
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
257
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
258
+GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
259
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
260
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
261
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
262
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
263
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
264
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
265
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
266
+GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
267
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
268
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
269
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
270
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
271
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
272
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
273
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
274
+GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c
275
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
276
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
277
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
278
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
279
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
280
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
281
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
282
+GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c
283
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
284
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
285
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
286
@@ -XXX,XX +XXX,XX @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
287
$(SRC_PATH)/qapi/char.json \
288
$(SRC_PATH)/qapi/crypto.json \
289
$(SRC_PATH)/qapi/introspect.json \
290
+ $(SRC_PATH)/qapi/job.json \
291
$(SRC_PATH)/qapi/migration.json \
292
$(SRC_PATH)/qapi/misc.json \
293
$(SRC_PATH)/qapi/net.json \
294
@@ -XXX,XX +XXX,XX @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \
295
qapi/qapi-types-common.c qapi/qapi-types-common.h \
296
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
297
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
298
+qapi/qapi-types-job.c qapi/qapi-types-job.h \
299
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
300
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
301
qapi/qapi-types-net.c qapi/qapi-types-net.h \
302
@@ -XXX,XX +XXX,XX @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
303
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
304
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
305
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
306
+qapi/qapi-visit-job.c qapi/qapi-visit-job.h \
307
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
308
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
309
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
310
@@ -XXX,XX +XXX,XX @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
311
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
312
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
313
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
314
+qapi/qapi-commands-job.c qapi/qapi-commands-job.h \
315
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
316
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
317
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
318
@@ -XXX,XX +XXX,XX @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \
319
qapi/qapi-events-common.c qapi/qapi-events-common.h \
320
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
321
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
322
+qapi/qapi-events-job.c qapi/qapi-events-job.h \
323
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
324
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
325
qapi/qapi-events-net.c qapi/qapi-events-net.h \
326
diff --git a/Makefile.objs b/Makefile.objs
327
index XXXXXXX..XXXXXXX 100644
328
--- a/Makefile.objs
329
+++ b/Makefile.objs
330
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-types-char.o
331
util-obj-y += qapi/qapi-types-common.o
332
util-obj-y += qapi/qapi-types-crypto.o
333
util-obj-y += qapi/qapi-types-introspect.o
334
+util-obj-y += qapi/qapi-types-job.o
335
util-obj-y += qapi/qapi-types-migration.o
336
util-obj-y += qapi/qapi-types-misc.o
337
util-obj-y += qapi/qapi-types-net.o
338
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-visit-char.o
339
util-obj-y += qapi/qapi-visit-common.o
340
util-obj-y += qapi/qapi-visit-crypto.o
341
util-obj-y += qapi/qapi-visit-introspect.o
342
+util-obj-y += qapi/qapi-visit-job.o
343
util-obj-y += qapi/qapi-visit-migration.o
344
util-obj-y += qapi/qapi-visit-misc.o
345
util-obj-y += qapi/qapi-visit-net.o
346
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-events-char.o
347
util-obj-y += qapi/qapi-events-common.o
348
util-obj-y += qapi/qapi-events-crypto.o
349
util-obj-y += qapi/qapi-events-introspect.o
350
+util-obj-y += qapi/qapi-events-job.o
351
util-obj-y += qapi/qapi-events-migration.o
352
util-obj-y += qapi/qapi-events-misc.o
353
util-obj-y += qapi/qapi-events-net.o
354
@@ -XXX,XX +XXX,XX @@ common-obj-y += qapi/qapi-commands-char.o
355
common-obj-y += qapi/qapi-commands-common.o
356
common-obj-y += qapi/qapi-commands-crypto.o
357
common-obj-y += qapi/qapi-commands-introspect.o
358
+common-obj-y += qapi/qapi-commands-job.o
359
common-obj-y += qapi/qapi-commands-migration.o
360
common-obj-y += qapi/qapi-commands-misc.o
361
common-obj-y += qapi/qapi-commands-net.o
362
--
30
--
363
2.13.6
31
2.19.1
364
32
365
33
diff view generated by jsdifflib
1
This moves the .complete callback that tells a READY job to complete
1
While we want machine interfaces like -blockdev and QMP blockdev-add to
2
from BlockJobDriver to JobDriver. The wrapper function job_complete()
2
add as little auto-detection as possible so that management tools are
3
doesn't require anything block job specific any more and can be moved
3
explicit about their needs, -drive is a convenience option for human
4
to Job.
4
users. Enabling auto-read-only=on by default there enables users to use
5
read-only images for read-only guest devices without having to specify
6
read-only=on explicitly. If they try to attach the image to a read-write
7
device, they will still get an error message.
5
8
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
11
---
9
include/block/blockjob.h | 10 ----------
12
blockdev.c | 1 +
10
include/block/blockjob_int.h | 6 ------
13
1 file changed, 1 insertion(+)
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
14
20
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
21
index XXXXXXX..XXXXXXX 100644
22
--- a/include/block/blockjob.h
23
+++ b/include/block/blockjob.h
24
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
25
void block_job_cancel(BlockJob *job, bool force);
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
}
91
92
-static void mirror_complete(BlockJob *job, Error **errp)
93
+static void mirror_complete(Job *job, Error **errp)
94
{
95
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
96
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
97
BlockDriverState *target;
98
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
}
107
108
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
109
.drain = block_job_drain,
110
.start = mirror_run,
111
.pause = mirror_pause,
112
+ .complete = mirror_complete,
113
},
114
- .complete = mirror_complete,
115
.attached_aio_context = mirror_attached_aio_context,
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
15
diff --git a/blockdev.c b/blockdev.c
129
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
130
--- a/blockdev.c
17
--- a/blockdev.c
131
+++ b/blockdev.c
18
+++ b/blockdev.c
132
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_complete(const char *device, Error **errp)
19
@@ -XXX,XX +XXX,XX @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts,
133
}
20
qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off");
134
21
qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY,
135
trace_qmp_block_job_complete(job);
22
read_only ? "on" : "off");
136
- block_job_complete(job, errp);
23
+ qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on");
137
+ job_complete(&job->job, errp);
24
assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0);
138
aio_context_release(aio_context);
25
139
}
26
if (runstate_check(RUN_STATE_INMIGRATE)) {
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
--
27
--
280
2.13.6
28
2.19.1
281
29
282
30
diff view generated by jsdifflib
1
This adds a test case that tests the new job-* QMP commands with
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
mirror and backup block jobs.
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
---
4
tests/qemu-iotests/232 | 147 +++++++++++++++++++++++++++++++++++++
5
tests/qemu-iotests/232.out | 59 +++++++++++++++
6
tests/qemu-iotests/group | 1 +
7
3 files changed, 207 insertions(+)
8
create mode 100755 tests/qemu-iotests/232
9
create mode 100644 tests/qemu-iotests/232.out
3
10
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232
5
---
6
tests/qemu-iotests/219 | 209 +++++++++++++++++++++++++++++
7
tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++++++++++
8
tests/qemu-iotests/group | 1 +
9
3 files changed, 537 insertions(+)
10
create mode 100755 tests/qemu-iotests/219
11
create mode 100644 tests/qemu-iotests/219.out
12
13
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
14
new file mode 100755
12
new file mode 100755
15
index XXXXXXX..XXXXXXX
13
index XXXXXXX..XXXXXXX
16
--- /dev/null
14
--- /dev/null
17
+++ b/tests/qemu-iotests/219
15
+++ b/tests/qemu-iotests/232
18
@@ -XXX,XX +XXX,XX @@
16
@@ -XXX,XX +XXX,XX @@
19
+#!/usr/bin/env python
17
+#!/bin/bash
18
+#
19
+# Test for auto-read-only
20
+#
20
+#
21
+# Copyright (C) 2018 Red Hat, Inc.
21
+# Copyright (C) 2018 Red Hat, Inc.
22
+#
22
+#
23
+# This program is free software; you can redistribute it and/or modify
23
+# 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
24
+# it under the terms of the GNU General Public License as published by
...
...
31
+# GNU General Public License for more details.
31
+# GNU General Public License for more details.
32
+#
32
+#
33
+# You should have received a copy of the GNU General Public License
33
+# 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/>.
34
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
35
+#
36
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
36
+
37
+#
37
+# creator
38
+# Check using the job-* QMP commands with block jobs
38
+owner=kwolf@redhat.com
39
+
39
+
40
+import iotests
40
+seq=`basename $0`
41
+
41
+echo "QA output created by $seq"
42
+iotests.verify_image_format(supported_fmts=['qcow2'])
42
+
43
+
43
+here=`pwd`
44
+def pause_wait(vm, job_id):
44
+status=1    # failure is the default!
45
+ with iotests.Timeout(3, "Timeout waiting for job to pause"):
45
+
46
+ while True:
46
+_cleanup()
47
+ result = vm.qmp('query-jobs')
47
+{
48
+ for job in result['return']:
48
+ _cleanup_test_img
49
+ if job['id'] == job_id and job['status'] in ['paused', 'standby']:
49
+ rm -f $TEST_IMG.snap
50
+ return job
50
+}
51
+
51
+trap "_cleanup; exit \$status" 0 1 2 3 15
52
+# Test that block-job-pause/resume and job-pause/resume can be mixed
52
+
53
+def test_pause_resume(vm):
53
+# get standard environment, filters and checks
54
+ for pause_cmd, pause_arg in [('block-job-pause', 'device'),
54
+. ./common.rc
55
+ ('job-pause', 'id')]:
55
+. ./common.filter
56
+ for resume_cmd, resume_arg in [('block-job-resume', 'device'),
56
+
57
+ ('job-resume', 'id')]:
57
+_supported_fmt generic
58
+ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
58
+_supported_proto file
59
+
59
+_supported_os Linux
60
+ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
60
+
61
+ pause_wait(vm, 'job0')
61
+function do_run_qemu()
62
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
62
+{
63
+ iotests.log(vm.qmp('query-jobs'))
63
+ echo Testing: "$@"
64
+
64
+ (
65
+ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
65
+ if ! test -t 0; then
66
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
66
+ while read cmd; do
67
+ iotests.log(vm.qmp('query-jobs'))
67
+ echo $cmd
68
+
68
+ done
69
+def test_job_lifecycle(vm, job, job_args, has_ready=False):
69
+ fi
70
+ iotests.log('')
70
+ echo quit
71
+ iotests.log('')
71
+ ) | $QEMU -nographic -monitor stdio -nodefaults "$@"
72
+ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
72
+ echo
73
+ (job,
73
+}
74
+ job_args.get('auto-finalize', True),
74
+
75
+ job_args.get('auto-dismiss', True)))
75
+function run_qemu()
76
+ iotests.log(vm.qmp(job, job_id='job0', **job_args))
76
+{
77
+
77
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp |
78
+ # Depending on the storage, the first request may or may not have completed
78
+ _filter_generated_node_ids | _filter_imgfmt
79
+ # yet, so filter out the progress. Later query-job calls don't need the
79
+}
80
+ # filtering because the progress is made deterministic by the block job
80
+
81
+ # speed
81
+function run_qemu_info_block()
82
+ result = vm.qmp('query-jobs')
82
+{
83
+ for j in result['return']:
83
+ echo "info block -n" | run_qemu "$@" | grep -e "(file" -e "QEMU_PROG"
84
+ del j['current-progress']
84
+}
85
+ iotests.log(result)
85
+
86
+
86
+size=128M
87
+ # undefined -> created -> running
87
+
88
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
88
+_make_test_img $size
89
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
89
+
90
+
90
+echo
91
+ # RUNNING state:
91
+echo "=== -drive with read-write image: read-only/auto-read-only combinations ==="
92
+ # pause/resume should work, complete/finalize/dismiss should error out
92
+echo
93
+ iotests.log('')
93
+
94
+ iotests.log('Pause/resume in RUNNING')
94
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off
95
+ test_pause_resume(vm)
95
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on
96
+
96
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on
97
+ iotests.log(vm.qmp('job-complete', id='job0'))
97
+echo
98
+ iotests.log(vm.qmp('job-finalize', id='job0'))
98
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off
99
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
99
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on
100
+
100
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off
101
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
101
+echo
102
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
102
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off
103
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
103
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on
104
+
104
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none
105
+ # Let the job complete (or transition to READY if it supports that)
105
+
106
+ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
106
+echo
107
+ if has_ready:
107
+echo "=== -drive with read-only image: read-only/auto-read-only combinations ==="
108
+ iotests.log('')
108
+echo
109
+ iotests.log('Waiting for READY state...')
109
+
110
+ vm.event_wait('BLOCK_JOB_READY')
110
+chmod a-w $TEST_IMG
111
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
111
+
112
+ iotests.log(vm.qmp('query-jobs'))
112
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off
113
+
113
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on
114
+ # READY state:
114
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on
115
+ # pause/resume/complete should work, finalize/dismiss should error out
115
+echo
116
+ iotests.log('')
116
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off
117
+ iotests.log('Pause/resume in READY')
117
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on
118
+ test_pause_resume(vm)
118
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off
119
+
119
+echo
120
+ iotests.log(vm.qmp('job-finalize', id='job0'))
120
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off
121
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
121
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on
122
+
122
+run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none
123
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
123
+
124
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
124
+echo
125
+
125
+echo "=== -blockdev with read-write image: read-only/auto-read-only combinations ==="
126
+ # Transition to WAITING
126
+echo
127
+ iotests.log(vm.qmp('job-complete', id='job0'))
127
+
128
+
128
+chmod a+w $TEST_IMG
129
+ # Move to WAITING and PENDING state
129
+
130
+ iotests.log('')
130
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off
131
+ iotests.log('Waiting for PENDING state...')
131
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on
132
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
132
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on
133
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
133
+echo
134
+
134
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off
135
+ if not job_args.get('auto-finalize', True):
135
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on
136
+ # PENDING state:
136
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off
137
+ # finalize should work, pause/complete/dismiss should error out
137
+echo
138
+ iotests.log(vm.qmp('query-jobs'))
138
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off
139
+
139
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
140
+ iotests.log(vm.qmp('job-pause', id='job0'))
140
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
141
+ iotests.log(vm.qmp('job-complete', id='job0'))
141
+
142
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
142
+echo
143
+
143
+echo "=== -blockdev with read-only image: read-only/auto-read-only combinations ==="
144
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
144
+echo
145
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
145
+
146
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
146
+chmod a-w $TEST_IMG
147
+
147
+
148
+ # Transition to CONCLUDED
148
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off
149
+ iotests.log(vm.qmp('job-finalize', id='job0'))
149
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on
150
+
150
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on
151
+
151
+echo
152
+ # Move to CONCLUDED state
152
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off
153
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
153
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on
154
+
154
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off
155
+ if not job_args.get('auto-dismiss', True):
155
+echo
156
+ # CONCLUDED state:
156
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off
157
+ # dismiss should work, pause/complete/finalize should error out
157
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on
158
+ iotests.log(vm.qmp('query-jobs'))
158
+run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0
159
+
159
+
160
+ iotests.log(vm.qmp('job-pause', id='job0'))
160
+# success, all done
161
+ iotests.log(vm.qmp('job-complete', id='job0'))
161
+echo "*** done"
162
+ iotests.log(vm.qmp('job-finalize', id='job0'))
162
+rm -f $seq.full
163
+
163
+status=0
164
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
164
diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out
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
165
new file mode 100644
230
index XXXXXXX..XXXXXXX
166
index XXXXXXX..XXXXXXX
231
--- /dev/null
167
--- /dev/null
232
+++ b/tests/qemu-iotests/219.out
168
+++ b/tests/qemu-iotests/232.out
233
@@ -XXX,XX +XXX,XX @@
169
@@ -XXX,XX +XXX,XX @@
234
+Launching VM...
170
+QA output created by 232
235
+
171
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728
236
+
172
+
237
+Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
173
+=== -drive with read-write image: read-only/auto-read-only combinations ===
238
+{u'return': {}}
174
+
239
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
175
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
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'}
176
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
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'}
177
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
242
+
178
+
243
+Pause/resume in RUNNING
179
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
244
+=== Testing block-job-pause/block-job-resume ===
180
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
245
+{u'return': {}}
181
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
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'}
182
+
247
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
183
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
248
+{u'return': {}}
184
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
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'}
185
+NODE_NAME: TEST_DIR/t.IMGFMT (file)
250
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
186
+
251
+=== Testing block-job-pause/job-resume ===
187
+=== -drive with read-only image: read-only/auto-read-only combinations ===
252
+{u'return': {}}
188
+
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'}
189
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
254
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
190
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
255
+{u'return': {}}
191
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
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'}
192
+
257
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
193
+QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
258
+=== Testing job-pause/block-job-resume ===
194
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
259
+{u'return': {}}
195
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
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'}
196
+
261
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
197
+QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
262
+{u'return': {}}
198
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
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'}
199
+NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only)
264
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
200
+
265
+=== Testing job-pause/job-resume ===
201
+=== -blockdev with read-write image: read-only/auto-read-only combinations ===
266
+{u'return': {}}
202
+
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'}
203
+node0: TEST_DIR/t.IMGFMT (file, read-only)
268
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
204
+node0: TEST_DIR/t.IMGFMT (file, read-only)
269
+{u'return': {}}
205
+node0: TEST_DIR/t.IMGFMT (file, read-only)
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'}
206
+
271
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
207
+node0: TEST_DIR/t.IMGFMT (file)
272
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
208
+node0: TEST_DIR/t.IMGFMT (file)
273
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
209
+node0: TEST_DIR/t.IMGFMT (file)
274
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
210
+
275
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
211
+node0: TEST_DIR/t.IMGFMT (file)
276
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
212
+node0: TEST_DIR/t.IMGFMT (file)
277
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
213
+node0: TEST_DIR/t.IMGFMT (file)
278
+{u'return': {}}
214
+
279
+
215
+=== -blockdev with read-only image: read-only/auto-read-only combinations ===
280
+Waiting for READY state...
216
+
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'}
217
+node0: TEST_DIR/t.IMGFMT (file, read-only)
282
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
218
+node0: TEST_DIR/t.IMGFMT (file, read-only)
283
+
219
+node0: TEST_DIR/t.IMGFMT (file, read-only)
284
+Pause/resume in READY
220
+
285
+=== Testing block-job-pause/block-job-resume ===
221
+QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
286
+{u'return': {}}
222
+node0: TEST_DIR/t.IMGFMT (file, read-only)
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'}
223
+QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
288
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
224
+
289
+{u'return': {}}
225
+QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
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'}
226
+node0: TEST_DIR/t.IMGFMT (file, read-only)
291
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
227
+QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied
292
+=== Testing block-job-pause/job-resume ===
228
+*** done
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
229
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
562
index XXXXXXX..XXXXXXX 100644
230
index XXXXXXX..XXXXXXX 100644
563
--- a/tests/qemu-iotests/group
231
--- a/tests/qemu-iotests/group
564
+++ b/tests/qemu-iotests/group
232
+++ b/tests/qemu-iotests/group
565
@@ -XXX,XX +XXX,XX @@
233
@@ -XXX,XX +XXX,XX @@
566
215 rw auto quick
234
227 auto quick
567
216 rw auto quick
235
229 auto quick
568
218 rw auto quick
236
231 auto quick
569
+219 rw auto
237
+232 auto quick
570
--
238
--
571
2.13.6
239
2.19.1
572
240
573
241
diff view generated by jsdifflib
1
This moves reference counting from BlockJob to Job.
1
From: Max Reitz <mreitz@redhat.com>
2
2
3
In order to keep calling the BlockJob cleanup code when the job is
3
This adds some whitespace into the option help (including indentation)
4
deleted via job_unref(), introduce a new JobDriver.free callback. Every
4
and puts angle brackets around the type names. Furthermore, the list
5
block job must use block_job_free() for this callback, this is asserted
5
name is no longer printed as part of every line, but only once in
6
in block_job_create().
6
advance, and only if the caller did not print a caption already.
7
7
8
This patch also restores the description alignment we had before commit
9
9cbef9d68ee1d8d0, just at 24 instead of 16 characters like we used to.
10
This increase is because now we have the type and two spaces of
11
indentation before the description, and with a usual type name length of
12
three chracters, this sums up to eight additional characters -- which
13
means that we now need 24 characters to get the same amount of padding
14
for most options. Also, 24 is a third of 80, which makes it kind of a
15
round number in terminal terms.
16
17
Finally, this patch amends the reference output of iotest 082 to match
18
the changes (and thus makes it pass again).
19
20
Signed-off-by: Max Reitz <mreitz@redhat.com>
21
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
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
---
23
---
12
include/block/blockjob.h | 21 -------------------
24
include/qemu/option.h | 2 +-
13
include/block/blockjob_int.h | 7 +++++++
25
qemu-img.c | 4 +-
14
include/qemu/job.h | 19 ++++++++++++++++--
26
util/qemu-option.c | 32 +-
15
block/backup.c | 1 +
27
tests/qemu-iotests/082.out | 956 ++++++++++++++++++-------------------
16
block/commit.c | 1 +
28
4 files changed, 507 insertions(+), 487 deletions(-)
17
block/mirror.c | 2 ++
18
block/stream.c | 1 +
19
blockjob.c | 48 +++++++++++++++++++-------------------------
20
job.c | 22 ++++++++++++++++----
21
qemu-img.c | 4 ++--
22
tests/test-bdrv-drain.c | 1 +
23
tests/test-blockjob-txn.c | 1 +
24
tests/test-blockjob.c | 6 ++++--
25
13 files changed, 76 insertions(+), 58 deletions(-)
26
29
27
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
30
diff --git a/include/qemu/option.h b/include/qemu/option.h
28
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/blockjob.h
32
--- a/include/qemu/option.h
30
+++ b/include/block/blockjob.h
33
+++ b/include/qemu/option.h
31
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
34
@@ -XXX,XX +XXX,XX @@ typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp);
32
/** The opaque value that is passed to the completion function. */
35
int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func,
33
void *opaque;
36
void *opaque, Error **errp);
34
37
void qemu_opts_print(QemuOpts *opts, const char *sep);
35
- /** Reference count of the block job */
38
-void qemu_opts_print_help(QemuOptsList *list);
36
- int refcnt;
39
+void qemu_opts_print_help(QemuOptsList *list, bool print_caption);
37
-
40
void qemu_opts_free(QemuOptsList *list);
38
/** True when job has reported completion by calling block_job_completed. */
41
QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list);
39
bool completed;
42
40
41
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
42
BlockJobTxn *block_job_txn_new(void);
43
44
/**
45
- * block_job_ref:
46
- *
47
- * Add a reference to BlockJob refcnt, it will be decreased with
48
- * block_job_unref, and then be freed if it comes to be the last
49
- * reference.
50
- */
51
-void block_job_ref(BlockJob *job);
52
-
53
-/**
54
- * block_job_unref:
55
- *
56
- * Release a reference that was previously acquired with block_job_ref
57
- * or block_job_create. If it's the last reference to the object, it will be
58
- * freed.
59
- */
60
-void block_job_unref(BlockJob *job);
61
-
62
-/**
63
* block_job_txn_unref:
64
*
65
* Release a reference that was previously acquired with block_job_txn_add_job
66
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
67
index XXXXXXX..XXXXXXX 100644
68
--- a/include/block/blockjob_int.h
69
+++ b/include/block/blockjob_int.h
70
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
71
BlockCompletionFunc *cb, void *opaque, Error **errp);
72
73
/**
74
+ * block_job_free:
75
+ * Callback to be used for JobDriver.free in all block jobs. Frees block job
76
+ * specific resources in @job.
77
+ */
78
+void block_job_free(Job *job);
79
+
80
+/**
81
* block_job_sleep_ns:
82
* @job: The job that calls the function.
83
* @ns: How many nanoseconds to stop for.
84
diff --git a/include/qemu/job.h b/include/qemu/job.h
85
index XXXXXXX..XXXXXXX 100644
86
--- a/include/qemu/job.h
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
89
/** The type of this job. */
90
const JobDriver *driver;
91
92
+ /** Reference count of the block job */
93
+ int refcnt;
94
+
95
/** Current state; See @JobStatus for details. */
96
JobStatus status;
97
98
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
99
100
/** Enum describing the operation */
101
JobType job_type;
102
+
103
+ /** Called when the job is freed */
104
+ void (*free)(Job *job);
105
};
106
107
108
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
109
*/
110
void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
111
112
-/** Frees the @job object. */
113
-void job_delete(Job *job);
114
+/**
115
+ * Add a reference to Job refcnt, it will be decreased with job_unref, and then
116
+ * be freed if it comes to be the last reference.
117
+ */
118
+void job_ref(Job *job);
119
+
120
+/**
121
+ * Release a reference that was previously acquired with job_ref() or
122
+ * job_create(). If it's the last reference to the object, it will be freed.
123
+ */
124
+void job_unref(Job *job);
125
126
/** Returns the JobType of a given Job. */
127
JobType job_type(const Job *job);
128
diff --git a/block/backup.c b/block/backup.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/block/backup.c
131
+++ b/block/backup.c
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
133
.job_driver = {
134
.instance_size = sizeof(BackupBlockJob),
135
.job_type = JOB_TYPE_BACKUP,
136
+ .free = block_job_free,
137
},
138
.start = backup_run,
139
.commit = backup_commit,
140
diff --git a/block/commit.c b/block/commit.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/block/commit.c
143
+++ b/block/commit.c
144
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
145
.job_driver = {
146
.instance_size = sizeof(CommitBlockJob),
147
.job_type = JOB_TYPE_COMMIT,
148
+ .free = block_job_free,
149
},
150
.start = commit_run,
151
};
152
diff --git a/block/mirror.c b/block/mirror.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/mirror.c
155
+++ b/block/mirror.c
156
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
157
.job_driver = {
158
.instance_size = sizeof(MirrorBlockJob),
159
.job_type = JOB_TYPE_MIRROR,
160
+ .free = block_job_free,
161
},
162
.start = mirror_run,
163
.complete = mirror_complete,
164
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
165
.job_driver = {
166
.instance_size = sizeof(MirrorBlockJob),
167
.job_type = JOB_TYPE_COMMIT,
168
+ .free = block_job_free,
169
},
170
.start = mirror_run,
171
.complete = mirror_complete,
172
diff --git a/block/stream.c b/block/stream.c
173
index XXXXXXX..XXXXXXX 100644
174
--- a/block/stream.c
175
+++ b/block/stream.c
176
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
177
.job_driver = {
178
.instance_size = sizeof(StreamBlockJob),
179
.job_type = JOB_TYPE_STREAM,
180
+ .free = block_job_free,
181
},
182
.start = stream_run,
183
};
184
diff --git a/blockjob.c b/blockjob.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/blockjob.c
187
+++ b/blockjob.c
188
@@ -XXX,XX +XXX,XX @@ static void block_job_resume(BlockJob *job)
189
block_job_enter_cond(job, block_job_timer_not_pending);
190
}
191
192
-void block_job_ref(BlockJob *job)
193
-{
194
- ++job->refcnt;
195
-}
196
-
197
static void block_job_attached_aio_context(AioContext *new_context,
198
void *opaque);
199
static void block_job_detach_aio_context(void *opaque);
200
201
-void block_job_unref(BlockJob *job)
202
+void block_job_free(Job *job)
203
{
204
- if (--job->refcnt == 0) {
205
- assert(job->job.status == JOB_STATUS_NULL);
206
- assert(!job->txn);
207
- BlockDriverState *bs = blk_bs(job->blk);
208
- bs->job = NULL;
209
- block_job_remove_all_bdrv(job);
210
- blk_remove_aio_context_notifier(job->blk,
211
- block_job_attached_aio_context,
212
- block_job_detach_aio_context, job);
213
- blk_unref(job->blk);
214
- error_free(job->blocker);
215
- assert(!timer_pending(&job->sleep_timer));
216
- job_delete(&job->job);
217
- }
218
+ BlockJob *bjob = container_of(job, BlockJob, job);
219
+ BlockDriverState *bs = blk_bs(bjob->blk);
220
+
221
+ assert(!bjob->txn);
222
+
223
+ bs->job = NULL;
224
+ block_job_remove_all_bdrv(bjob);
225
+ blk_remove_aio_context_notifier(bjob->blk,
226
+ block_job_attached_aio_context,
227
+ block_job_detach_aio_context, bjob);
228
+ blk_unref(bjob->blk);
229
+ error_free(bjob->blocker);
230
+ assert(!timer_pending(&bjob->sleep_timer));
231
}
232
233
static void block_job_attached_aio_context(AioContext *new_context,
234
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
235
BlockJob *job = opaque;
236
237
/* In case the job terminates during aio_poll()... */
238
- block_job_ref(job);
239
+ job_ref(&job->job);
240
241
block_job_pause(job);
242
243
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
244
block_job_drain(job);
245
}
246
247
- block_job_unref(job);
248
+ job_unref(&job->job);
249
}
250
251
static char *child_job_get_parent_desc(BdrvChild *c)
252
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
253
job->deferred_to_main_loop = true;
254
block_job_txn_del_job(job);
255
job_state_transition(&job->job, JOB_STATUS_NULL);
256
- block_job_unref(job);
257
+ job_unref(&job->job);
258
}
259
260
static void block_job_do_dismiss(BlockJob *job)
261
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
262
263
assert(blk_bs(job->blk)->job == job);
264
265
- block_job_ref(job);
266
+ job_ref(&job->job);
267
268
if (finish) {
269
finish(job, &local_err);
270
}
271
if (local_err) {
272
error_propagate(errp, local_err);
273
- block_job_unref(job);
274
+ job_unref(&job->job);
275
return -EBUSY;
276
}
277
/* block_job_drain calls block_job_enter, and it should be enough to
278
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
279
aio_poll(qemu_get_aio_context(), true);
280
}
281
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
282
- block_job_unref(job);
283
+ job_unref(&job->job);
284
return ret;
285
}
286
287
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
288
}
289
290
assert(is_block_job(&job->job));
291
+ assert(job->job.driver->free == &block_job_free);
292
293
job->driver = driver;
294
job->blk = blk;
295
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
296
job->busy = false;
297
job->paused = true;
298
job->pause_count = 1;
299
- job->refcnt = 1;
300
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
301
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
302
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
303
diff --git a/job.c b/job.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/job.c
306
+++ b/job.c
307
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
308
job = g_malloc0(driver->instance_size);
309
job->driver = driver;
310
job->id = g_strdup(job_id);
311
+ job->refcnt = 1;
312
313
job_state_transition(job, JOB_STATUS_CREATED);
314
315
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
316
return job;
317
}
318
319
-void job_delete(Job *job)
320
+void job_ref(Job *job)
321
{
322
- QLIST_REMOVE(job, job_list);
323
+ ++job->refcnt;
324
+}
325
+
326
+void job_unref(Job *job)
327
+{
328
+ if (--job->refcnt == 0) {
329
+ assert(job->status == JOB_STATUS_NULL);
330
331
- g_free(job->id);
332
- g_free(job);
333
+ if (job->driver->free) {
334
+ job->driver->free(job);
335
+ }
336
+
337
+ QLIST_REMOVE(job, job_list);
338
+
339
+ g_free(job->id);
340
+ g_free(job);
341
+ }
342
}
343
diff --git a/qemu-img.c b/qemu-img.c
43
diff --git a/qemu-img.c b/qemu-img.c
344
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
345
--- a/qemu-img.c
45
--- a/qemu-img.c
346
+++ b/qemu-img.c
46
+++ b/qemu-img.c
347
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
47
@@ -XXX,XX +XXX,XX @@ static int print_block_option_help(const char *filename, const char *fmt)
348
int ret = 0;
349
350
aio_context_acquire(aio_context);
351
- block_job_ref(job);
352
+ job_ref(&job->job);
353
do {
354
aio_poll(aio_context, true);
355
qemu_progress_print(job->len ?
356
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
357
} else {
358
ret = job->ret;
359
}
48
}
360
- block_job_unref(job);
49
361
+ job_unref(&job->job);
50
printf("Supported options:\n");
362
aio_context_release(aio_context);
51
- qemu_opts_print_help(create_opts);
363
52
+ qemu_opts_print_help(create_opts, false);
364
/* publish completion progress only when success */
53
qemu_opts_free(create_opts);
365
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
54
return 0;
55
}
56
@@ -XXX,XX +XXX,XX @@ static int print_amend_option_help(const char *format)
57
assert(drv->create_opts);
58
59
printf("Creation options for '%s':\n", format);
60
- qemu_opts_print_help(drv->create_opts);
61
+ qemu_opts_print_help(drv->create_opts, false);
62
printf("\nNote that not all of these options may be amendable.\n");
63
return 0;
64
}
65
diff --git a/util/qemu-option.c b/util/qemu-option.c
366
index XXXXXXX..XXXXXXX 100644
66
index XXXXXXX..XXXXXXX 100644
367
--- a/tests/test-bdrv-drain.c
67
--- a/util/qemu-option.c
368
+++ b/tests/test-bdrv-drain.c
68
+++ b/util/qemu-option.c
369
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
69
@@ -XXX,XX +XXX,XX @@ static const char *opt_type_to_string(enum QemuOptType type)
370
BlockJobDriver test_job_driver = {
70
g_assert_not_reached();
371
.job_driver = {
71
}
372
.instance_size = sizeof(TestBlockJob),
72
373
+ .free = block_job_free,
73
-void qemu_opts_print_help(QemuOptsList *list)
374
},
74
+/**
375
.start = test_job_start,
75
+ * Print the list of options available in the given list. If
376
.complete = test_job_complete,
76
+ * @print_caption is true, a caption (including the list name, if it
377
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
77
+ * exists) is printed. The options itself will be indented, so
78
+ * @print_caption should only be set to false if the caller prints its
79
+ * own custom caption (so that the indentation makes sense).
80
+ */
81
+void qemu_opts_print_help(QemuOptsList *list, bool print_caption)
82
{
83
QemuOptDesc *desc;
84
int i;
85
@@ -XXX,XX +XXX,XX @@ void qemu_opts_print_help(QemuOptsList *list)
86
desc = list->desc;
87
while (desc && desc->name) {
88
GString *str = g_string_new(NULL);
89
- if (list->name) {
90
- g_string_append_printf(str, "%s.", list->name);
91
- }
92
- g_string_append_printf(str, "%s=%s", desc->name,
93
+ g_string_append_printf(str, " %s=<%s>", desc->name,
94
opt_type_to_string(desc->type));
95
if (desc->help) {
96
+ if (str->len < 24) {
97
+ g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
98
+ }
99
g_string_append_printf(str, " - %s", desc->help);
100
}
101
g_ptr_array_add(array, g_string_free(str, false));
102
@@ -XXX,XX +XXX,XX @@ void qemu_opts_print_help(QemuOptsList *list)
103
}
104
105
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
106
+ if (print_caption && array->len > 0) {
107
+ if (list->name) {
108
+ printf("%s options:\n", list->name);
109
+ } else {
110
+ printf("Options:\n");
111
+ }
112
+ } else if (array->len == 0) {
113
+ if (list->name) {
114
+ printf("There are no options for %s.\n", list->name);
115
+ } else {
116
+ printf("No options available.\n");
117
+ }
118
+ }
119
for (i = 0; i < array->len; i++) {
120
printf("%s\n", (char *)array->pdata[i]);
121
}
122
@@ -XXX,XX +XXX,XX @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params,
123
opts = opts_parse(list, params, permit_abbrev, false, &invalidp, &err);
124
if (err) {
125
if (invalidp && has_help_option(params)) {
126
- qemu_opts_print_help(list);
127
+ qemu_opts_print_help(list, true);
128
error_free(err);
129
} else {
130
error_report_err(err);
131
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
378
index XXXXXXX..XXXXXXX 100644
132
index XXXXXXX..XXXXXXX 100644
379
--- a/tests/test-blockjob-txn.c
133
--- a/tests/qemu-iotests/082.out
380
+++ b/tests/test-blockjob-txn.c
134
+++ b/tests/qemu-iotests/082.out
381
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
135
@@ -XXX,XX +XXX,XX @@ cluster_size: 8192
382
static const BlockJobDriver test_block_job_driver = {
136
383
.job_driver = {
137
Testing: create -f qcow2 -o help TEST_DIR/t.qcow2 128M
384
.instance_size = sizeof(TestBlockJob),
138
Supported options:
385
+ .free = block_job_free,
139
-size Virtual disk size
386
},
140
-compat Compatibility level (0.10 or 1.1)
387
.start = test_block_job_run,
141
-backing_file File name of a base image
388
};
142
-backing_fmt Image format of the base image
389
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
143
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
390
index XXXXXXX..XXXXXXX 100644
144
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
391
--- a/tests/test-blockjob.c
145
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
392
+++ b/tests/test-blockjob.c
146
-encrypt.cipher-alg Name of encryption cipher algorithm
393
@@ -XXX,XX +XXX,XX @@
147
-encrypt.cipher-mode Name of encryption cipher mode
394
static const BlockJobDriver test_block_job_driver = {
148
-encrypt.ivgen-alg Name of IV generator algorithm
395
.job_driver = {
149
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
396
.instance_size = sizeof(BlockJob),
150
-encrypt.hash-alg Name of encryption hash algorithm
397
+ .free = block_job_free,
151
-encrypt.iter-time Time to spend in PBKDF in milliseconds
398
},
152
-cluster_size qcow2 cluster size
399
};
153
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
400
154
-lazy_refcounts Postpone refcount updates
401
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
155
-refcount_bits Width of a reference count entry in bits
402
static const BlockJobDriver test_cancel_driver = {
156
-nocow Turn off copy-on-write (valid only on btrfs)
403
.job_driver = {
157
+ backing_file=<str> - File name of a base image
404
.instance_size = sizeof(CancelJob),
158
+ backing_fmt=<str> - Image format of the base image
405
+ .free = block_job_free,
159
+ cluster_size=<size> - qcow2 cluster size
406
},
160
+ compat=<str> - Compatibility level (0.10 or 1.1)
407
.start = cancel_job_start,
161
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
408
.complete = cancel_job_complete,
162
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
409
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
163
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
410
blk = create_blk(NULL);
164
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
411
job = mk_job(blk, "Steve", &test_cancel_driver, true,
165
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
412
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
166
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
413
- block_job_ref(job);
167
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
414
+ job_ref(&job->job);
168
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
415
assert(job->job.status == JOB_STATUS_CREATED);
169
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
416
s = container_of(job, CancelJob, common);
170
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
417
s->blk = blk;
171
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
418
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
172
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
419
block_job_dismiss(&dummy, &error_abort);
173
+ refcount_bits=<num> - Width of a reference count entry in bits
420
}
174
+ size=<size> - Virtual disk size
421
assert(job->job.status == JOB_STATUS_NULL);
175
422
- block_job_unref(job);
176
Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M
423
+ job_unref(&job->job);
177
Supported options:
424
destroy_blk(blk);
178
-size Virtual disk size
425
}
179
-compat Compatibility level (0.10 or 1.1)
426
180
-backing_file File name of a base image
181
-backing_fmt Image format of the base image
182
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
183
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
184
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
185
-encrypt.cipher-alg Name of encryption cipher algorithm
186
-encrypt.cipher-mode Name of encryption cipher mode
187
-encrypt.ivgen-alg Name of IV generator algorithm
188
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
189
-encrypt.hash-alg Name of encryption hash algorithm
190
-encrypt.iter-time Time to spend in PBKDF in milliseconds
191
-cluster_size qcow2 cluster size
192
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
193
-lazy_refcounts Postpone refcount updates
194
-refcount_bits Width of a reference count entry in bits
195
-nocow Turn off copy-on-write (valid only on btrfs)
196
+ backing_file=<str> - File name of a base image
197
+ backing_fmt=<str> - Image format of the base image
198
+ cluster_size=<size> - qcow2 cluster size
199
+ compat=<str> - Compatibility level (0.10 or 1.1)
200
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
201
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
202
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
203
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
204
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
205
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
206
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
207
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
208
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
209
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
210
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
211
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
212
+ refcount_bits=<num> - Width of a reference count entry in bits
213
+ size=<size> - Virtual disk size
214
215
Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M
216
Supported options:
217
-size Virtual disk size
218
-compat Compatibility level (0.10 or 1.1)
219
-backing_file File name of a base image
220
-backing_fmt Image format of the base image
221
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
222
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
223
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
224
-encrypt.cipher-alg Name of encryption cipher algorithm
225
-encrypt.cipher-mode Name of encryption cipher mode
226
-encrypt.ivgen-alg Name of IV generator algorithm
227
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
228
-encrypt.hash-alg Name of encryption hash algorithm
229
-encrypt.iter-time Time to spend in PBKDF in milliseconds
230
-cluster_size qcow2 cluster size
231
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
232
-lazy_refcounts Postpone refcount updates
233
-refcount_bits Width of a reference count entry in bits
234
-nocow Turn off copy-on-write (valid only on btrfs)
235
+ backing_file=<str> - File name of a base image
236
+ backing_fmt=<str> - Image format of the base image
237
+ cluster_size=<size> - qcow2 cluster size
238
+ compat=<str> - Compatibility level (0.10 or 1.1)
239
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
240
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
241
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
242
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
243
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
244
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
245
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
246
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
247
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
248
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
249
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
250
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
251
+ refcount_bits=<num> - Width of a reference count entry in bits
252
+ size=<size> - Virtual disk size
253
254
Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M
255
Supported options:
256
-size Virtual disk size
257
-compat Compatibility level (0.10 or 1.1)
258
-backing_file File name of a base image
259
-backing_fmt Image format of the base image
260
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
261
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
262
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
263
-encrypt.cipher-alg Name of encryption cipher algorithm
264
-encrypt.cipher-mode Name of encryption cipher mode
265
-encrypt.ivgen-alg Name of IV generator algorithm
266
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
267
-encrypt.hash-alg Name of encryption hash algorithm
268
-encrypt.iter-time Time to spend in PBKDF in milliseconds
269
-cluster_size qcow2 cluster size
270
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
271
-lazy_refcounts Postpone refcount updates
272
-refcount_bits Width of a reference count entry in bits
273
-nocow Turn off copy-on-write (valid only on btrfs)
274
+ backing_file=<str> - File name of a base image
275
+ backing_fmt=<str> - Image format of the base image
276
+ cluster_size=<size> - qcow2 cluster size
277
+ compat=<str> - Compatibility level (0.10 or 1.1)
278
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
279
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
280
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
281
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
282
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
283
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
284
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
285
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
286
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
287
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
288
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
289
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
290
+ refcount_bits=<num> - Width of a reference count entry in bits
291
+ size=<size> - Virtual disk size
292
293
Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M
294
Supported options:
295
-size Virtual disk size
296
-compat Compatibility level (0.10 or 1.1)
297
-backing_file File name of a base image
298
-backing_fmt Image format of the base image
299
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
300
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
301
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
302
-encrypt.cipher-alg Name of encryption cipher algorithm
303
-encrypt.cipher-mode Name of encryption cipher mode
304
-encrypt.ivgen-alg Name of IV generator algorithm
305
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
306
-encrypt.hash-alg Name of encryption hash algorithm
307
-encrypt.iter-time Time to spend in PBKDF in milliseconds
308
-cluster_size qcow2 cluster size
309
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
310
-lazy_refcounts Postpone refcount updates
311
-refcount_bits Width of a reference count entry in bits
312
-nocow Turn off copy-on-write (valid only on btrfs)
313
+ backing_file=<str> - File name of a base image
314
+ backing_fmt=<str> - Image format of the base image
315
+ cluster_size=<size> - qcow2 cluster size
316
+ compat=<str> - Compatibility level (0.10 or 1.1)
317
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
318
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
319
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
320
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
321
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
322
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
323
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
324
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
325
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
326
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
327
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
328
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
329
+ refcount_bits=<num> - Width of a reference count entry in bits
330
+ size=<size> - Virtual disk size
331
332
Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M
333
Supported options:
334
-size Virtual disk size
335
-compat Compatibility level (0.10 or 1.1)
336
-backing_file File name of a base image
337
-backing_fmt Image format of the base image
338
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
339
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
340
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
341
-encrypt.cipher-alg Name of encryption cipher algorithm
342
-encrypt.cipher-mode Name of encryption cipher mode
343
-encrypt.ivgen-alg Name of IV generator algorithm
344
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
345
-encrypt.hash-alg Name of encryption hash algorithm
346
-encrypt.iter-time Time to spend in PBKDF in milliseconds
347
-cluster_size qcow2 cluster size
348
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
349
-lazy_refcounts Postpone refcount updates
350
-refcount_bits Width of a reference count entry in bits
351
-nocow Turn off copy-on-write (valid only on btrfs)
352
+ backing_file=<str> - File name of a base image
353
+ backing_fmt=<str> - Image format of the base image
354
+ cluster_size=<size> - qcow2 cluster size
355
+ compat=<str> - Compatibility level (0.10 or 1.1)
356
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
357
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
358
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
359
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
360
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
361
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
362
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
363
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
364
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
365
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
366
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
367
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
368
+ refcount_bits=<num> - Width of a reference count entry in bits
369
+ size=<size> - Virtual disk size
370
371
Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M
372
Supported options:
373
-size Virtual disk size
374
-compat Compatibility level (0.10 or 1.1)
375
-backing_file File name of a base image
376
-backing_fmt Image format of the base image
377
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
378
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
379
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
380
-encrypt.cipher-alg Name of encryption cipher algorithm
381
-encrypt.cipher-mode Name of encryption cipher mode
382
-encrypt.ivgen-alg Name of IV generator algorithm
383
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
384
-encrypt.hash-alg Name of encryption hash algorithm
385
-encrypt.iter-time Time to spend in PBKDF in milliseconds
386
-cluster_size qcow2 cluster size
387
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
388
-lazy_refcounts Postpone refcount updates
389
-refcount_bits Width of a reference count entry in bits
390
-nocow Turn off copy-on-write (valid only on btrfs)
391
+ backing_file=<str> - File name of a base image
392
+ backing_fmt=<str> - Image format of the base image
393
+ cluster_size=<size> - qcow2 cluster size
394
+ compat=<str> - Compatibility level (0.10 or 1.1)
395
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
396
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
397
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
398
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
399
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
400
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
401
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
402
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
403
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
404
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
405
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
406
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
407
+ refcount_bits=<num> - Width of a reference count entry in bits
408
+ size=<size> - Virtual disk size
409
410
Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M
411
Supported options:
412
-size Virtual disk size
413
-compat Compatibility level (0.10 or 1.1)
414
-backing_file File name of a base image
415
-backing_fmt Image format of the base image
416
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
417
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
418
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
419
-encrypt.cipher-alg Name of encryption cipher algorithm
420
-encrypt.cipher-mode Name of encryption cipher mode
421
-encrypt.ivgen-alg Name of IV generator algorithm
422
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
423
-encrypt.hash-alg Name of encryption hash algorithm
424
-encrypt.iter-time Time to spend in PBKDF in milliseconds
425
-cluster_size qcow2 cluster size
426
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
427
-lazy_refcounts Postpone refcount updates
428
-refcount_bits Width of a reference count entry in bits
429
-nocow Turn off copy-on-write (valid only on btrfs)
430
+ backing_file=<str> - File name of a base image
431
+ backing_fmt=<str> - Image format of the base image
432
+ cluster_size=<size> - qcow2 cluster size
433
+ compat=<str> - Compatibility level (0.10 or 1.1)
434
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
435
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
436
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
437
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
438
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
439
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
440
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
441
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
442
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
443
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
444
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
445
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
446
+ refcount_bits=<num> - Width of a reference count entry in bits
447
+ size=<size> - Virtual disk size
448
449
Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M
450
Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,help cluster_size=65536 lazy_refcounts=off refcount_bits=16
451
@@ -XXX,XX +XXX,XX @@ qemu-img: Invalid option list: ,,
452
453
Testing: create -f qcow2 -o help
454
Supported options:
455
-size Virtual disk size
456
-compat Compatibility level (0.10 or 1.1)
457
-backing_file File name of a base image
458
-backing_fmt Image format of the base image
459
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
460
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
461
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
462
-encrypt.cipher-alg Name of encryption cipher algorithm
463
-encrypt.cipher-mode Name of encryption cipher mode
464
-encrypt.ivgen-alg Name of IV generator algorithm
465
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
466
-encrypt.hash-alg Name of encryption hash algorithm
467
-encrypt.iter-time Time to spend in PBKDF in milliseconds
468
-cluster_size qcow2 cluster size
469
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
470
-lazy_refcounts Postpone refcount updates
471
-refcount_bits Width of a reference count entry in bits
472
+ backing_file=<str> - File name of a base image
473
+ backing_fmt=<str> - Image format of the base image
474
+ cluster_size=<size> - qcow2 cluster size
475
+ compat=<str> - Compatibility level (0.10 or 1.1)
476
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
477
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
478
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
479
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
480
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
481
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
482
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
483
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
484
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
485
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
486
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
487
+ refcount_bits=<num> - Width of a reference count entry in bits
488
+ size=<size> - Virtual disk size
489
490
Testing: create -o help
491
Supported options:
492
-size Virtual disk size
493
+ size=<size> - Virtual disk size
494
495
Testing: create -f bochs -o help
496
qemu-img: Format driver 'bochs' does not support image creation
497
@@ -XXX,XX +XXX,XX @@ cluster_size: 8192
498
499
Testing: convert -O qcow2 -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
500
Supported options:
501
-size Virtual disk size
502
-compat Compatibility level (0.10 or 1.1)
503
-backing_file File name of a base image
504
-backing_fmt Image format of the base image
505
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
506
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
507
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
508
-encrypt.cipher-alg Name of encryption cipher algorithm
509
-encrypt.cipher-mode Name of encryption cipher mode
510
-encrypt.ivgen-alg Name of IV generator algorithm
511
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
512
-encrypt.hash-alg Name of encryption hash algorithm
513
-encrypt.iter-time Time to spend in PBKDF in milliseconds
514
-cluster_size qcow2 cluster size
515
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
516
-lazy_refcounts Postpone refcount updates
517
-refcount_bits Width of a reference count entry in bits
518
-nocow Turn off copy-on-write (valid only on btrfs)
519
+ backing_file=<str> - File name of a base image
520
+ backing_fmt=<str> - Image format of the base image
521
+ cluster_size=<size> - qcow2 cluster size
522
+ compat=<str> - Compatibility level (0.10 or 1.1)
523
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
524
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
525
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
526
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
527
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
528
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
529
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
530
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
531
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
532
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
533
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
534
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
535
+ refcount_bits=<num> - Width of a reference count entry in bits
536
+ size=<size> - Virtual disk size
537
538
Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
539
Supported options:
540
-size Virtual disk size
541
-compat Compatibility level (0.10 or 1.1)
542
-backing_file File name of a base image
543
-backing_fmt Image format of the base image
544
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
545
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
546
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
547
-encrypt.cipher-alg Name of encryption cipher algorithm
548
-encrypt.cipher-mode Name of encryption cipher mode
549
-encrypt.ivgen-alg Name of IV generator algorithm
550
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
551
-encrypt.hash-alg Name of encryption hash algorithm
552
-encrypt.iter-time Time to spend in PBKDF in milliseconds
553
-cluster_size qcow2 cluster size
554
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
555
-lazy_refcounts Postpone refcount updates
556
-refcount_bits Width of a reference count entry in bits
557
-nocow Turn off copy-on-write (valid only on btrfs)
558
+ backing_file=<str> - File name of a base image
559
+ backing_fmt=<str> - Image format of the base image
560
+ cluster_size=<size> - qcow2 cluster size
561
+ compat=<str> - Compatibility level (0.10 or 1.1)
562
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
563
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
564
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
565
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
566
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
567
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
568
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
569
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
570
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
571
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
572
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
573
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
574
+ refcount_bits=<num> - Width of a reference count entry in bits
575
+ size=<size> - Virtual disk size
576
577
Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
578
Supported options:
579
-size Virtual disk size
580
-compat Compatibility level (0.10 or 1.1)
581
-backing_file File name of a base image
582
-backing_fmt Image format of the base image
583
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
584
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
585
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
586
-encrypt.cipher-alg Name of encryption cipher algorithm
587
-encrypt.cipher-mode Name of encryption cipher mode
588
-encrypt.ivgen-alg Name of IV generator algorithm
589
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
590
-encrypt.hash-alg Name of encryption hash algorithm
591
-encrypt.iter-time Time to spend in PBKDF in milliseconds
592
-cluster_size qcow2 cluster size
593
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
594
-lazy_refcounts Postpone refcount updates
595
-refcount_bits Width of a reference count entry in bits
596
-nocow Turn off copy-on-write (valid only on btrfs)
597
+ backing_file=<str> - File name of a base image
598
+ backing_fmt=<str> - Image format of the base image
599
+ cluster_size=<size> - qcow2 cluster size
600
+ compat=<str> - Compatibility level (0.10 or 1.1)
601
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
602
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
603
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
604
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
605
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
606
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
607
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
608
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
609
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
610
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
611
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
612
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
613
+ refcount_bits=<num> - Width of a reference count entry in bits
614
+ size=<size> - Virtual disk size
615
616
Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
617
Supported options:
618
-size Virtual disk size
619
-compat Compatibility level (0.10 or 1.1)
620
-backing_file File name of a base image
621
-backing_fmt Image format of the base image
622
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
623
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
624
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
625
-encrypt.cipher-alg Name of encryption cipher algorithm
626
-encrypt.cipher-mode Name of encryption cipher mode
627
-encrypt.ivgen-alg Name of IV generator algorithm
628
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
629
-encrypt.hash-alg Name of encryption hash algorithm
630
-encrypt.iter-time Time to spend in PBKDF in milliseconds
631
-cluster_size qcow2 cluster size
632
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
633
-lazy_refcounts Postpone refcount updates
634
-refcount_bits Width of a reference count entry in bits
635
-nocow Turn off copy-on-write (valid only on btrfs)
636
+ backing_file=<str> - File name of a base image
637
+ backing_fmt=<str> - Image format of the base image
638
+ cluster_size=<size> - qcow2 cluster size
639
+ compat=<str> - Compatibility level (0.10 or 1.1)
640
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
641
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
642
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
643
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
644
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
645
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
646
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
647
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
648
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
649
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
650
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
651
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
652
+ refcount_bits=<num> - Width of a reference count entry in bits
653
+ size=<size> - Virtual disk size
654
655
Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
656
Supported options:
657
-size Virtual disk size
658
-compat Compatibility level (0.10 or 1.1)
659
-backing_file File name of a base image
660
-backing_fmt Image format of the base image
661
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
662
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
663
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
664
-encrypt.cipher-alg Name of encryption cipher algorithm
665
-encrypt.cipher-mode Name of encryption cipher mode
666
-encrypt.ivgen-alg Name of IV generator algorithm
667
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
668
-encrypt.hash-alg Name of encryption hash algorithm
669
-encrypt.iter-time Time to spend in PBKDF in milliseconds
670
-cluster_size qcow2 cluster size
671
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
672
-lazy_refcounts Postpone refcount updates
673
-refcount_bits Width of a reference count entry in bits
674
-nocow Turn off copy-on-write (valid only on btrfs)
675
+ backing_file=<str> - File name of a base image
676
+ backing_fmt=<str> - Image format of the base image
677
+ cluster_size=<size> - qcow2 cluster size
678
+ compat=<str> - Compatibility level (0.10 or 1.1)
679
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
680
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
681
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
682
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
683
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
684
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
685
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
686
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
687
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
688
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
689
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
690
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
691
+ refcount_bits=<num> - Width of a reference count entry in bits
692
+ size=<size> - Virtual disk size
693
694
Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
695
Supported options:
696
-size Virtual disk size
697
-compat Compatibility level (0.10 or 1.1)
698
-backing_file File name of a base image
699
-backing_fmt Image format of the base image
700
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
701
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
702
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
703
-encrypt.cipher-alg Name of encryption cipher algorithm
704
-encrypt.cipher-mode Name of encryption cipher mode
705
-encrypt.ivgen-alg Name of IV generator algorithm
706
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
707
-encrypt.hash-alg Name of encryption hash algorithm
708
-encrypt.iter-time Time to spend in PBKDF in milliseconds
709
-cluster_size qcow2 cluster size
710
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
711
-lazy_refcounts Postpone refcount updates
712
-refcount_bits Width of a reference count entry in bits
713
-nocow Turn off copy-on-write (valid only on btrfs)
714
+ backing_file=<str> - File name of a base image
715
+ backing_fmt=<str> - Image format of the base image
716
+ cluster_size=<size> - qcow2 cluster size
717
+ compat=<str> - Compatibility level (0.10 or 1.1)
718
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
719
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
720
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
721
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
722
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
723
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
724
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
725
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
726
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
727
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
728
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
729
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
730
+ refcount_bits=<num> - Width of a reference count entry in bits
731
+ size=<size> - Virtual disk size
732
733
Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
734
Supported options:
735
-size Virtual disk size
736
-compat Compatibility level (0.10 or 1.1)
737
-backing_file File name of a base image
738
-backing_fmt Image format of the base image
739
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
740
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
741
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
742
-encrypt.cipher-alg Name of encryption cipher algorithm
743
-encrypt.cipher-mode Name of encryption cipher mode
744
-encrypt.ivgen-alg Name of IV generator algorithm
745
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
746
-encrypt.hash-alg Name of encryption hash algorithm
747
-encrypt.iter-time Time to spend in PBKDF in milliseconds
748
-cluster_size qcow2 cluster size
749
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
750
-lazy_refcounts Postpone refcount updates
751
-refcount_bits Width of a reference count entry in bits
752
-nocow Turn off copy-on-write (valid only on btrfs)
753
+ backing_file=<str> - File name of a base image
754
+ backing_fmt=<str> - Image format of the base image
755
+ cluster_size=<size> - qcow2 cluster size
756
+ compat=<str> - Compatibility level (0.10 or 1.1)
757
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
758
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
759
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
760
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
761
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
762
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
763
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
764
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
765
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
766
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
767
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
768
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
769
+ refcount_bits=<num> - Width of a reference count entry in bits
770
+ size=<size> - Virtual disk size
771
772
Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
773
Supported options:
774
-size Virtual disk size
775
-compat Compatibility level (0.10 or 1.1)
776
-backing_file File name of a base image
777
-backing_fmt Image format of the base image
778
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
779
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
780
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
781
-encrypt.cipher-alg Name of encryption cipher algorithm
782
-encrypt.cipher-mode Name of encryption cipher mode
783
-encrypt.ivgen-alg Name of IV generator algorithm
784
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
785
-encrypt.hash-alg Name of encryption hash algorithm
786
-encrypt.iter-time Time to spend in PBKDF in milliseconds
787
-cluster_size qcow2 cluster size
788
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
789
-lazy_refcounts Postpone refcount updates
790
-refcount_bits Width of a reference count entry in bits
791
-nocow Turn off copy-on-write (valid only on btrfs)
792
+ backing_file=<str> - File name of a base image
793
+ backing_fmt=<str> - Image format of the base image
794
+ cluster_size=<size> - qcow2 cluster size
795
+ compat=<str> - Compatibility level (0.10 or 1.1)
796
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
797
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
798
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
799
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
800
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
801
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
802
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
803
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
804
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
805
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
806
+ nocow=<bool (on/off)> - Turn off copy-on-write (valid only on btrfs)
807
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
808
+ refcount_bits=<num> - Width of a reference count entry in bits
809
+ size=<size> - Virtual disk size
810
811
Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base
812
qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory
813
@@ -XXX,XX +XXX,XX @@ qemu-img: Invalid option list: ,,
814
815
Testing: convert -O qcow2 -o help
816
Supported options:
817
-size Virtual disk size
818
-compat Compatibility level (0.10 or 1.1)
819
-backing_file File name of a base image
820
-backing_fmt Image format of the base image
821
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
822
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
823
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
824
-encrypt.cipher-alg Name of encryption cipher algorithm
825
-encrypt.cipher-mode Name of encryption cipher mode
826
-encrypt.ivgen-alg Name of IV generator algorithm
827
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
828
-encrypt.hash-alg Name of encryption hash algorithm
829
-encrypt.iter-time Time to spend in PBKDF in milliseconds
830
-cluster_size qcow2 cluster size
831
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
832
-lazy_refcounts Postpone refcount updates
833
-refcount_bits Width of a reference count entry in bits
834
+ backing_file=<str> - File name of a base image
835
+ backing_fmt=<str> - Image format of the base image
836
+ cluster_size=<size> - qcow2 cluster size
837
+ compat=<str> - Compatibility level (0.10 or 1.1)
838
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
839
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
840
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
841
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
842
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
843
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
844
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
845
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
846
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
847
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
848
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
849
+ refcount_bits=<num> - Width of a reference count entry in bits
850
+ size=<size> - Virtual disk size
851
852
Testing: convert -o help
853
Supported options:
854
-size Virtual disk size
855
+ size=<size> - Virtual disk size
856
857
Testing: convert -O bochs -o help
858
qemu-img: Format driver 'bochs' does not support image creation
859
@@ -XXX,XX +XXX,XX @@ cluster_size: 65536
860
861
Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2
862
Creation options for 'qcow2':
863
-size Virtual disk size
864
-compat Compatibility level (0.10 or 1.1)
865
-backing_file File name of a base image
866
-backing_fmt Image format of the base image
867
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
868
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
869
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
870
-encrypt.cipher-alg Name of encryption cipher algorithm
871
-encrypt.cipher-mode Name of encryption cipher mode
872
-encrypt.ivgen-alg Name of IV generator algorithm
873
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
874
-encrypt.hash-alg Name of encryption hash algorithm
875
-encrypt.iter-time Time to spend in PBKDF in milliseconds
876
-cluster_size qcow2 cluster size
877
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
878
-lazy_refcounts Postpone refcount updates
879
-refcount_bits Width of a reference count entry in bits
880
+ backing_file=<str> - File name of a base image
881
+ backing_fmt=<str> - Image format of the base image
882
+ cluster_size=<size> - qcow2 cluster size
883
+ compat=<str> - Compatibility level (0.10 or 1.1)
884
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
885
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
886
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
887
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
888
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
889
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
890
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
891
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
892
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
893
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
894
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
895
+ refcount_bits=<num> - Width of a reference count entry in bits
896
+ size=<size> - Virtual disk size
897
898
Note that not all of these options may be amendable.
899
900
Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2
901
Creation options for 'qcow2':
902
-size Virtual disk size
903
-compat Compatibility level (0.10 or 1.1)
904
-backing_file File name of a base image
905
-backing_fmt Image format of the base image
906
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
907
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
908
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
909
-encrypt.cipher-alg Name of encryption cipher algorithm
910
-encrypt.cipher-mode Name of encryption cipher mode
911
-encrypt.ivgen-alg Name of IV generator algorithm
912
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
913
-encrypt.hash-alg Name of encryption hash algorithm
914
-encrypt.iter-time Time to spend in PBKDF in milliseconds
915
-cluster_size qcow2 cluster size
916
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
917
-lazy_refcounts Postpone refcount updates
918
-refcount_bits Width of a reference count entry in bits
919
+ backing_file=<str> - File name of a base image
920
+ backing_fmt=<str> - Image format of the base image
921
+ cluster_size=<size> - qcow2 cluster size
922
+ compat=<str> - Compatibility level (0.10 or 1.1)
923
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
924
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
925
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
926
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
927
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
928
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
929
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
930
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
931
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
932
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
933
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
934
+ refcount_bits=<num> - Width of a reference count entry in bits
935
+ size=<size> - Virtual disk size
936
937
Note that not all of these options may be amendable.
938
939
Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2
940
Creation options for 'qcow2':
941
-size Virtual disk size
942
-compat Compatibility level (0.10 or 1.1)
943
-backing_file File name of a base image
944
-backing_fmt Image format of the base image
945
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
946
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
947
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
948
-encrypt.cipher-alg Name of encryption cipher algorithm
949
-encrypt.cipher-mode Name of encryption cipher mode
950
-encrypt.ivgen-alg Name of IV generator algorithm
951
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
952
-encrypt.hash-alg Name of encryption hash algorithm
953
-encrypt.iter-time Time to spend in PBKDF in milliseconds
954
-cluster_size qcow2 cluster size
955
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
956
-lazy_refcounts Postpone refcount updates
957
-refcount_bits Width of a reference count entry in bits
958
+ backing_file=<str> - File name of a base image
959
+ backing_fmt=<str> - Image format of the base image
960
+ cluster_size=<size> - qcow2 cluster size
961
+ compat=<str> - Compatibility level (0.10 or 1.1)
962
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
963
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
964
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
965
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
966
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
967
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
968
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
969
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
970
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
971
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
972
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
973
+ refcount_bits=<num> - Width of a reference count entry in bits
974
+ size=<size> - Virtual disk size
975
976
Note that not all of these options may be amendable.
977
978
Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2
979
Creation options for 'qcow2':
980
-size Virtual disk size
981
-compat Compatibility level (0.10 or 1.1)
982
-backing_file File name of a base image
983
-backing_fmt Image format of the base image
984
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
985
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
986
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
987
-encrypt.cipher-alg Name of encryption cipher algorithm
988
-encrypt.cipher-mode Name of encryption cipher mode
989
-encrypt.ivgen-alg Name of IV generator algorithm
990
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
991
-encrypt.hash-alg Name of encryption hash algorithm
992
-encrypt.iter-time Time to spend in PBKDF in milliseconds
993
-cluster_size qcow2 cluster size
994
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
995
-lazy_refcounts Postpone refcount updates
996
-refcount_bits Width of a reference count entry in bits
997
+ backing_file=<str> - File name of a base image
998
+ backing_fmt=<str> - Image format of the base image
999
+ cluster_size=<size> - qcow2 cluster size
1000
+ compat=<str> - Compatibility level (0.10 or 1.1)
1001
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1002
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1003
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1004
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1005
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1006
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1007
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1008
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1009
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1010
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1011
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1012
+ refcount_bits=<num> - Width of a reference count entry in bits
1013
+ size=<size> - Virtual disk size
1014
1015
Note that not all of these options may be amendable.
1016
1017
Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2
1018
Creation options for 'qcow2':
1019
-size Virtual disk size
1020
-compat Compatibility level (0.10 or 1.1)
1021
-backing_file File name of a base image
1022
-backing_fmt Image format of the base image
1023
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1024
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
1025
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
1026
-encrypt.cipher-alg Name of encryption cipher algorithm
1027
-encrypt.cipher-mode Name of encryption cipher mode
1028
-encrypt.ivgen-alg Name of IV generator algorithm
1029
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
1030
-encrypt.hash-alg Name of encryption hash algorithm
1031
-encrypt.iter-time Time to spend in PBKDF in milliseconds
1032
-cluster_size qcow2 cluster size
1033
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
1034
-lazy_refcounts Postpone refcount updates
1035
-refcount_bits Width of a reference count entry in bits
1036
+ backing_file=<str> - File name of a base image
1037
+ backing_fmt=<str> - Image format of the base image
1038
+ cluster_size=<size> - qcow2 cluster size
1039
+ compat=<str> - Compatibility level (0.10 or 1.1)
1040
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1041
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1042
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1043
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1044
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1045
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1046
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1047
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1048
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1049
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1050
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1051
+ refcount_bits=<num> - Width of a reference count entry in bits
1052
+ size=<size> - Virtual disk size
1053
1054
Note that not all of these options may be amendable.
1055
1056
Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2
1057
Creation options for 'qcow2':
1058
-size Virtual disk size
1059
-compat Compatibility level (0.10 or 1.1)
1060
-backing_file File name of a base image
1061
-backing_fmt Image format of the base image
1062
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1063
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
1064
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
1065
-encrypt.cipher-alg Name of encryption cipher algorithm
1066
-encrypt.cipher-mode Name of encryption cipher mode
1067
-encrypt.ivgen-alg Name of IV generator algorithm
1068
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
1069
-encrypt.hash-alg Name of encryption hash algorithm
1070
-encrypt.iter-time Time to spend in PBKDF in milliseconds
1071
-cluster_size qcow2 cluster size
1072
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
1073
-lazy_refcounts Postpone refcount updates
1074
-refcount_bits Width of a reference count entry in bits
1075
+ backing_file=<str> - File name of a base image
1076
+ backing_fmt=<str> - Image format of the base image
1077
+ cluster_size=<size> - qcow2 cluster size
1078
+ compat=<str> - Compatibility level (0.10 or 1.1)
1079
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1080
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1081
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1082
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1083
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1084
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1085
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1086
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1087
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1088
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1089
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1090
+ refcount_bits=<num> - Width of a reference count entry in bits
1091
+ size=<size> - Virtual disk size
1092
1093
Note that not all of these options may be amendable.
1094
1095
Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2
1096
Creation options for 'qcow2':
1097
-size Virtual disk size
1098
-compat Compatibility level (0.10 or 1.1)
1099
-backing_file File name of a base image
1100
-backing_fmt Image format of the base image
1101
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1102
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
1103
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
1104
-encrypt.cipher-alg Name of encryption cipher algorithm
1105
-encrypt.cipher-mode Name of encryption cipher mode
1106
-encrypt.ivgen-alg Name of IV generator algorithm
1107
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
1108
-encrypt.hash-alg Name of encryption hash algorithm
1109
-encrypt.iter-time Time to spend in PBKDF in milliseconds
1110
-cluster_size qcow2 cluster size
1111
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
1112
-lazy_refcounts Postpone refcount updates
1113
-refcount_bits Width of a reference count entry in bits
1114
+ backing_file=<str> - File name of a base image
1115
+ backing_fmt=<str> - Image format of the base image
1116
+ cluster_size=<size> - qcow2 cluster size
1117
+ compat=<str> - Compatibility level (0.10 or 1.1)
1118
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1119
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1120
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1121
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1122
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1123
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1124
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1125
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1126
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1127
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1128
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1129
+ refcount_bits=<num> - Width of a reference count entry in bits
1130
+ size=<size> - Virtual disk size
1131
1132
Note that not all of these options may be amendable.
1133
1134
Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2
1135
Creation options for 'qcow2':
1136
-size Virtual disk size
1137
-compat Compatibility level (0.10 or 1.1)
1138
-backing_file File name of a base image
1139
-backing_fmt Image format of the base image
1140
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1141
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
1142
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
1143
-encrypt.cipher-alg Name of encryption cipher algorithm
1144
-encrypt.cipher-mode Name of encryption cipher mode
1145
-encrypt.ivgen-alg Name of IV generator algorithm
1146
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
1147
-encrypt.hash-alg Name of encryption hash algorithm
1148
-encrypt.iter-time Time to spend in PBKDF in milliseconds
1149
-cluster_size qcow2 cluster size
1150
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
1151
-lazy_refcounts Postpone refcount updates
1152
-refcount_bits Width of a reference count entry in bits
1153
+ backing_file=<str> - File name of a base image
1154
+ backing_fmt=<str> - Image format of the base image
1155
+ cluster_size=<size> - qcow2 cluster size
1156
+ compat=<str> - Compatibility level (0.10 or 1.1)
1157
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1158
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1159
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1160
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1161
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1162
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1163
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1164
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1165
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1166
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1167
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1168
+ refcount_bits=<num> - Width of a reference count entry in bits
1169
+ size=<size> - Virtual disk size
1170
1171
Note that not all of these options may be amendable.
1172
1173
@@ -XXX,XX +XXX,XX @@ qemu-img: Invalid option list: ,,
1174
1175
Testing: amend -f qcow2 -o help
1176
Creation options for 'qcow2':
1177
-size Virtual disk size
1178
-compat Compatibility level (0.10 or 1.1)
1179
-backing_file File name of a base image
1180
-backing_fmt Image format of the base image
1181
-encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1182
-encrypt.format Encrypt the image, format choices: 'aes', 'luks'
1183
-encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase
1184
-encrypt.cipher-alg Name of encryption cipher algorithm
1185
-encrypt.cipher-mode Name of encryption cipher mode
1186
-encrypt.ivgen-alg Name of IV generator algorithm
1187
-encrypt.ivgen-hash-alg Name of IV generator hash algorithm
1188
-encrypt.hash-alg Name of encryption hash algorithm
1189
-encrypt.iter-time Time to spend in PBKDF in milliseconds
1190
-cluster_size qcow2 cluster size
1191
-preallocation Preallocation mode (allowed values: off, metadata, falloc, full)
1192
-lazy_refcounts Postpone refcount updates
1193
-refcount_bits Width of a reference count entry in bits
1194
+ backing_file=<str> - File name of a base image
1195
+ backing_fmt=<str> - Image format of the base image
1196
+ cluster_size=<size> - qcow2 cluster size
1197
+ compat=<str> - Compatibility level (0.10 or 1.1)
1198
+ encrypt.cipher-alg=<str> - Name of encryption cipher algorithm
1199
+ encrypt.cipher-mode=<str> - Name of encryption cipher mode
1200
+ encrypt.format=<str> - Encrypt the image, format choices: 'aes', 'luks'
1201
+ encrypt.hash-alg=<str> - Name of encryption hash algorithm
1202
+ encrypt.iter-time=<num> - Time to spend in PBKDF in milliseconds
1203
+ encrypt.ivgen-alg=<str> - Name of IV generator algorithm
1204
+ encrypt.ivgen-hash-alg=<str> - Name of IV generator hash algorithm
1205
+ encrypt.key-secret=<str> - ID of secret providing qcow AES key or LUKS passphrase
1206
+ encryption=<bool (on/off)> - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes)
1207
+ lazy_refcounts=<bool (on/off)> - Postpone refcount updates
1208
+ preallocation=<str> - Preallocation mode (allowed values: off, metadata, falloc, full)
1209
+ refcount_bits=<num> - Width of a reference count entry in bits
1210
+ size=<size> - Virtual disk size
1211
1212
Note that not all of these options may be amendable.
1213
1214
Testing: convert -o help
1215
Supported options:
1216
-size Virtual disk size
1217
+ size=<size> - Virtual disk size
1218
1219
Testing: amend -f bochs -o help
1220
qemu-img: Format driver 'bochs' does not support option amendment
427
--
1221
--
428
2.13.6
1222
2.19.1
429
1223
430
1224
diff view generated by jsdifflib
1
block_job_cancel_async() did two things that were still block job
1
From: Max Reitz <mreitz@redhat.com>
2
specific:
3
2
4
* Setting job->force. This field makes sense on the Job level, so we can
3
Following the example of qemu_opts_print_help(), indent all entries in
5
just move it. While at it, rename it to job->force_cancel to make its
4
the list of character devices.
6
purpose more obvious.
7
5
8
* Resetting the I/O status. This can't be moved because generic Jobs
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
don't have an I/O status. What the function really implements is a
7
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
10
user resume, except without entering the coroutine. Consequently, it
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
makes sense to call the .user_resume driver callback here which
9
---
12
already resets the I/O status.
10
chardev/char.c | 2 +-
11
1 file changed, 1 insertion(+), 1 deletion(-)
13
12
14
The old block_job_cancel_async() has two separate if statements that
13
diff --git a/chardev/char.c b/chardev/char.c
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
14
index XXXXXXX..XXXXXXX 100644
33
--- a/include/block/blockjob.h
15
--- a/chardev/char.c
34
+++ b/include/block/blockjob.h
16
+++ b/chardev/char.c
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
17
@@ -XXX,XX +XXX,XX @@ help_string_append(const char *name, void *opaque)
36
BlockBackend *blk;
18
{
37
19
GString *str = opaque;
38
/**
20
39
- * Set to true if the job should abort immediately without waiting
21
- g_string_append_printf(str, "\n%s", name);
40
- * for data to be in sync.
22
+ g_string_append_printf(str, "\n %s", name);
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
}
23
}
94
24
95
-static void block_job_cancel_async(BlockJob *job, bool force)
25
static const char *chardev_alias_translate(const char *name)
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
--
26
--
141
2.13.6
27
2.19.1
142
28
143
29
diff view generated by jsdifflib
1
BlockJob.driver is redundant with Job.driver and only used in very few
1
From: Max Reitz <mreitz@redhat.com>
2
places any more. Remove it.
3
2
3
Just like in qemu_opts_print_help(), print the device name as a caption
4
instead of on every single line, indent all options, add angle brackets
5
around types, and align the descriptions after 24 characters. Also,
6
separate the descriptions with " - " instead of putting them in
7
parentheses, because that is what we do everywhere else. This does look
8
a bit funny here because basically all bits have the description
9
"on/off", but funny does not mean it is less readable.
10
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
Reviewed-by: Marc-André Lureau <marcandre.lureau@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
qdev-monitor.c | 13 +++++++++++--
7
blockjob.c | 17 ++++++++++-------
16
1 file changed, 11 insertions(+), 2 deletions(-)
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/qdev-monitor.c b/qdev-monitor.c
11
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/blockjob.h
20
--- a/qdev-monitor.c
13
+++ b/include/block/blockjob.h
21
+++ b/qdev-monitor.c
14
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
22
@@ -XXX,XX +XXX,XX @@ int qdev_device_help(QemuOpts *opts)
15
/** Data belonging to the generic Job infrastructure */
23
goto error;
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
}
24
}
41
25
42
job_resume(&job->job);
26
+ if (prop_list) {
43
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
27
+ out_printf("%s options:\n", driver);
44
void block_job_drain(Job *job)
28
+ } else {
45
{
29
+ out_printf("There are no options for %s.\n", driver);
46
BlockJob *bjob = container_of(job, BlockJob, job);
30
+ }
47
+ const JobDriver *drv = job->driver;
31
for (prop = prop_list; prop; prop = prop->next) {
48
+ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver);
32
- out_printf("%s.%s=%s", driver, prop->value->name, prop->value->type);
49
33
+ int len;
50
blk_drain(bjob->blk);
34
+ out_printf(" %s=<%s>%n", prop->value->name, prop->value->type, &len);
51
- if (bjob->driver->drain) {
35
if (prop->value->has_description) {
52
- bjob->driver->drain(bjob);
36
- out_printf(" (%s)\n", prop->value->description);
53
+ if (bjdrv->drain) {
37
+ if (len < 24) {
54
+ bjdrv->drain(bjob);
38
+ out_printf("%*s", 24 - len, "");
55
}
39
+ }
56
}
40
+ out_printf(" - %s\n", prop->value->description);
57
41
} else {
58
@@ -XXX,XX +XXX,XX @@ bool block_job_is_internal(BlockJob *job)
42
out_printf("\n");
59
43
}
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
--
44
--
78
2.13.6
45
2.19.1
79
46
80
47
diff view generated by jsdifflib
1
The transition to the READY state was still performed in the BlockJob
1
From: Max Reitz <mreitz@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
Just like in qemu_opts_print_help(), print the object name as a caption
5
the QMP event using a notifier called from the Job layer, like we
4
instead of on every single line, indent all options, add angle brackets
6
already do for other events related to state transitions.
5
around types, and align the descriptions after 24 characters.
7
6
7
Also, indent every object name in the list of available objects.
8
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
12
---
11
include/block/blockjob.h | 3 +++
13
vl.c | 13 ++++++++++---
12
include/block/blockjob_int.h | 8 --------
14
1 file changed, 10 insertions(+), 3 deletions(-)
13
include/qemu/job.h | 9 ++++++---
14
block/mirror.c | 6 +++---
15
blockjob.c | 33 ++++++++++++++++++---------------
16
job.c | 16 +++++++++++++---
17
tests/test-bdrv-drain.c | 2 +-
18
tests/test-blockjob.c | 2 +-
19
8 files changed, 45 insertions(+), 34 deletions(-)
20
15
21
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
16
diff --git a/vl.c b/vl.c
22
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob.h
18
--- a/vl.c
24
+++ b/include/block/blockjob.h
19
+++ b/vl.c
25
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
20
@@ -XXX,XX +XXX,XX @@ static bool object_create_initial(const char *type, QemuOpts *opts)
26
/** Called when the job transitions to PENDING */
21
list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false);
27
Notifier pending_notifier;
22
for (l = list; l != NULL; l = l->next) {
28
23
ObjectClass *oc = OBJECT_CLASS(l->data);
29
+ /** Called when the job transitions to READY */
24
- printf("%s\n", object_class_get_name(oc));
30
+ Notifier ready_notifier;
25
+ printf(" %s\n", object_class_get_name(oc));
31
+
26
}
32
/** BlockDriverStates that are involved in this block job */
27
g_slist_free(list);
33
GSList *nodes;
28
exit(0);
34
} BlockJob;
29
@@ -XXX,XX +XXX,XX @@ static bool object_create_initial(const char *type, QemuOpts *opts)
35
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
36
index XXXXXXX..XXXXXXX 100644
37
--- a/include/block/blockjob_int.h
38
+++ b/include/block/blockjob_int.h
39
@@ -XXX,XX +XXX,XX @@ void block_job_drain(Job *job);
40
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
41
42
/**
43
- * block_job_event_ready:
44
- * @job: The job which is now ready to be completed.
45
- *
46
- * Send a BLOCK_JOB_READY event for the specified job.
47
- */
48
-void block_job_event_ready(BlockJob *job);
49
-
50
-/**
51
* block_job_error_action:
52
* @job: The job to signal an error for.
53
* @on_err: The error action setting.
54
diff --git a/include/qemu/job.h b/include/qemu/job.h
55
index XXXXXXX..XXXXXXX 100644
56
--- a/include/qemu/job.h
57
+++ b/include/qemu/job.h
58
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
59
/** Notifiers called when the job transitions to PENDING */
60
NotifierList on_pending;
61
62
+ /** Notifiers called when the job transitions to READY */
63
+ NotifierList on_ready;
64
+
65
/** Element of the list of jobs */
66
QLIST_ENTRY(Job) job_list;
67
68
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
69
/** The @job could not be started, free it. */
70
void job_early_fail(Job *job);
71
72
+/** Moves the @job from RUNNING to READY */
73
+void job_transition_to_ready(Job *job);
74
+
75
/**
76
* @job: The job being completed.
77
* @ret: The status code.
78
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
79
*/
80
int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
81
82
-/* TODO To be removed from the public interface */
83
-void job_state_transition(Job *job, JobStatus s1);
84
-
85
#endif
86
diff --git a/block/mirror.c b/block/mirror.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/mirror.c
89
+++ b/block/mirror.c
90
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
91
}
92
93
if (s->bdev_length == 0) {
94
- /* Report BLOCK_JOB_READY and wait for complete. */
95
- block_job_event_ready(&s->common);
96
+ /* Transition to the READY state and wait for complete. */
97
+ job_transition_to_ready(&s->common.job);
98
s->synced = true;
99
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
100
job_yield(&s->common.job);
101
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
102
* report completion. This way, block-job-cancel will leave
103
* the target in a consistent state.
104
*/
105
- block_job_event_ready(&s->common);
106
+ job_transition_to_ready(&s->common.job);
107
s->synced = true;
108
}
30
}
109
31
110
diff --git a/blockjob.c b/blockjob.c
32
str = g_string_new(NULL);
111
index XXXXXXX..XXXXXXX 100644
33
- g_string_append_printf(str, "%s.%s=%s", type,
112
--- a/blockjob.c
34
- prop->name, prop->type);
113
+++ b/blockjob.c
35
+ g_string_append_printf(str, " %s=<%s>", prop->name, prop->type);
114
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
36
if (prop->description) {
115
&error_abort);
37
+ if (str->len < 24) {
116
}
38
+ g_string_append_printf(str, "%*s", 24 - (int)str->len, "");
117
39
+ }
118
+static void block_job_event_ready(Notifier *n, void *opaque)
40
g_string_append_printf(str, " - %s", prop->description);
119
+{
41
}
120
+ BlockJob *job = opaque;
42
g_ptr_array_add(array, g_string_free(str, false));
121
+
122
+ if (block_job_is_internal(job)) {
123
+ return;
124
+ }
125
+
126
+ qapi_event_send_block_job_ready(job_type(&job->job),
127
+ job->job.id,
128
+ job->len,
129
+ job->offset,
130
+ job->speed, &error_abort);
131
+}
132
+
133
+
134
/*
135
* API for block job drivers and the block layer. These functions are
136
* declared in blockjob_int.h.
137
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
138
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
139
job->finalize_completed_notifier.notify = block_job_event_completed;
140
job->pending_notifier.notify = block_job_event_pending;
141
+ job->ready_notifier.notify = block_job_event_ready;
142
143
notifier_list_add(&job->job.on_finalize_cancelled,
144
&job->finalize_cancelled_notifier);
145
notifier_list_add(&job->job.on_finalize_completed,
146
&job->finalize_completed_notifier);
147
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
148
+ notifier_list_add(&job->job.on_ready, &job->ready_notifier);
149
150
error_setg(&job->blocker, "block device is in use by block job: %s",
151
job_type_str(&job->job));
152
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job)
153
block_job_iostatus_reset(bjob);
154
}
155
156
-void block_job_event_ready(BlockJob *job)
157
-{
158
- job_state_transition(&job->job, JOB_STATUS_READY);
159
-
160
- if (block_job_is_internal(job)) {
161
- return;
162
- }
163
-
164
- qapi_event_send_block_job_ready(job_type(&job->job),
165
- job->job.id,
166
- job->len,
167
- job->offset,
168
- job->speed, &error_abort);
169
-}
170
-
171
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
172
int is_read, int error)
173
{
174
diff --git a/job.c b/job.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/job.c
177
+++ b/job.c
178
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
179
return rc;
180
}
181
182
-
183
-/* TODO Make static once the whole state machine is in job.c */
184
-void job_state_transition(Job *job, JobStatus s1)
185
+static void job_state_transition(Job *job, JobStatus s1)
186
{
187
JobStatus s0 = job->status;
188
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
189
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
190
notifier_list_init(&job->on_finalize_cancelled);
191
notifier_list_init(&job->on_finalize_completed);
192
notifier_list_init(&job->on_pending);
193
+ notifier_list_init(&job->on_ready);
194
195
job_state_transition(job, JOB_STATUS_CREATED);
196
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
197
@@ -XXX,XX +XXX,XX @@ static void job_event_pending(Job *job)
198
notifier_list_notify(&job->on_pending, job);
199
}
200
201
+static void job_event_ready(Job *job)
202
+{
203
+ notifier_list_notify(&job->on_ready, job);
204
+}
205
+
206
void job_enter_cond(Job *job, bool(*fn)(Job *job))
207
{
208
if (!job_started(job)) {
209
@@ -XXX,XX +XXX,XX @@ static int job_transition_to_pending(Job *job)
210
return 0;
211
}
212
213
+void job_transition_to_ready(Job *job)
214
+{
215
+ job_state_transition(job, JOB_STATUS_READY);
216
+ job_event_ready(job);
217
+}
218
+
219
static void job_completed_txn_success(Job *job)
220
{
221
JobTxn *txn = job->txn;
222
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
223
index XXXXXXX..XXXXXXX 100644
224
--- a/tests/test-bdrv-drain.c
225
+++ b/tests/test-bdrv-drain.c
226
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
227
{
228
TestBlockJob *s = opaque;
229
230
- block_job_event_ready(&s->common);
231
+ job_transition_to_ready(&s->common.job);
232
while (!s->should_complete) {
233
job_sleep_ns(&s->common.job, 100000);
234
}
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 coroutine_fn cancel_job_start(void *opaque)
240
}
43
}
241
44
g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0);
242
if (!job_is_ready(&s->common.job) && s->should_converge) {
45
+ if (array->len > 0) {
243
- block_job_event_ready(&s->common);
46
+ printf("%s options:\n", type);
244
+ job_transition_to_ready(&s->common.job);
47
+ } else {
48
+ printf("There are no options for %s.\n", type);
49
+ }
50
for (i = 0; i < array->len; i++) {
51
printf("%s\n", (char *)array->pdata[i]);
245
}
52
}
246
247
job_sleep_ns(&s->common.job, 100000);
248
--
53
--
249
2.13.6
54
2.19.1
250
55
251
56
diff view generated by jsdifflib
1
Now that we cancel all jobs and not only block jobs on shutdown, doing
1
From: Max Reitz <mreitz@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
There is no good reason why there should be a newline in this
4
description, so remove it.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
8
Reviewed-by: Marc-André Lureau <marcandre.lureau@redhat.com>
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
10
---
8
block.c | 4 +---
11
vl.c | 2 +-
9
qemu-nbd.c | 8 +++++++-
12
1 file changed, 1 insertion(+), 1 deletion(-)
10
vl.c | 1 +
11
3 files changed, 9 insertions(+), 4 deletions(-)
12
13
13
diff --git a/block.c b/block.c
14
index XXXXXXX..XXXXXXX 100644
15
--- a/block.c
16
+++ b/block.c
17
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
18
19
void bdrv_close_all(void)
20
{
21
- /* TODO We do want to cancel all jobs instead of just block jobs on
22
- * shutdown, but bdrv_close_all() isn't the right place any more. */
23
- job_cancel_sync_all();
24
+ assert(job_next(NULL) == NULL);
25
nbd_export_close_all();
26
27
/* Drop references from requests still in flight, such as canceled block
28
diff --git a/qemu-nbd.c b/qemu-nbd.c
29
index XXXXXXX..XXXXXXX 100644
30
--- a/qemu-nbd.c
31
+++ b/qemu-nbd.c
32
@@ -XXX,XX +XXX,XX @@ static const char *socket_activation_validate_opts(const char *device,
33
return NULL;
34
}
35
36
+static void qemu_nbd_shutdown(void)
37
+{
38
+ job_cancel_sync_all();
39
+ bdrv_close_all();
40
+}
41
+
42
int main(int argc, char **argv)
43
{
44
BlockBackend *blk;
45
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
46
exit(EXIT_FAILURE);
47
}
48
bdrv_init();
49
- atexit(bdrv_close_all);
50
+ atexit(qemu_nbd_shutdown);
51
52
srcpath = argv[optind];
53
if (imageOpts) {
54
diff --git a/vl.c b/vl.c
14
diff --git a/vl.c b/vl.c
55
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
56
--- a/vl.c
16
--- a/vl.c
57
+++ b/vl.c
17
+++ b/vl.c
58
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
18
@@ -XXX,XX +XXX,XX @@ static QemuOptsList qemu_fw_cfg_opts = {
59
/* No more vcpu or device emulation activity beyond this point */
19
}, {
60
vm_shutdown();
20
.name = "file",
61
21
.type = QEMU_OPT_STRING,
62
+ job_cancel_sync_all();
22
- .help = "Sets the name of the file from which\n"
63
bdrv_close_all();
23
+ .help = "Sets the name of the file from which "
64
24
"the fw_cfg blob will be loaded",
65
res_free();
25
}, {
26
.name = "string",
66
--
27
--
67
2.13.6
28
2.19.1
68
29
69
30
diff view generated by jsdifflib
1
Test cases were trying to use nfs:// URLs as local filenames, which made
1
From: Leonid Bloch <lbloch@janustech.com>
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
2
3
If an expression is used to define DEFAULT_CLUSTER_SIZE, when compiled,
4
it will be embedded as a literal expression in the binary (as the
5
default value) because it is stringified to mark the size of the default
6
value. Now this is fixed by using a defined number to define this value.
7
8
Signed-off-by: Leonid Bloch <lbloch@janustech.com>
9
Reviewed-by: Stefan Weil <sw@weilnetz.de>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
11
---
8
tests/qemu-iotests/common.rc | 4 ++--
12
block/vdi.c | 4 ++--
9
1 file changed, 2 insertions(+), 2 deletions(-)
13
1 file changed, 2 insertions(+), 2 deletions(-)
10
14
11
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
15
diff --git a/block/vdi.c b/block/vdi.c
12
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/common.rc
17
--- a/block/vdi.c
14
+++ b/tests/qemu-iotests/common.rc
18
+++ b/block/vdi.c
15
@@ -XXX,XX +XXX,XX @@ else
19
@@ -XXX,XX +XXX,XX @@
16
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
20
#define BLOCK_OPT_STATIC "static"
17
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
21
18
elif [ "$IMGPROTO" = "nfs" ]; then
22
#define SECTOR_SIZE 512
19
- TEST_DIR="nfs://127.0.0.1/$TEST_DIR"
23
-#define DEFAULT_CLUSTER_SIZE (1 * MiB)
20
- TEST_IMG=$TEST_DIR/t.$IMGFMT
24
+#define DEFAULT_CLUSTER_SIZE S_1MiB
21
+ TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
25
22
+ TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
26
#if defined(CONFIG_VDI_DEBUG)
23
elif [ "$IMGPROTO" = "vxhs" ]; then
27
#define VDI_DEBUG 1
24
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
28
@@ -XXX,XX +XXX,XX @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags,
25
TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT"
29
goto fail;
30
} else if (header.block_size != DEFAULT_CLUSTER_SIZE) {
31
error_setg(errp, "unsupported VDI image (block size %" PRIu32
32
- " is not %" PRIu64 ")",
33
+ " is not %" PRIu32 ")",
34
header.block_size, DEFAULT_CLUSTER_SIZE);
35
ret = -ENOTSUP;
36
goto fail;
26
--
37
--
27
2.13.6
38
2.19.1
28
39
29
40
diff view generated by jsdifflib
Deleted patch
1
NFS paths were only partially filtered in _filter_img_create, _img_info
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
1
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Fam Zheng <famz@redhat.com>
9
---
10
tests/qemu-iotests/126.out | 2 +-
11
tests/qemu-iotests/common.filter | 6 ++++--
12
tests/qemu-iotests/common.rc | 8 +++++++-
13
3 files changed, 12 insertions(+), 4 deletions(-)
14
15
diff --git a/tests/qemu-iotests/126.out b/tests/qemu-iotests/126.out
16
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/126.out
18
+++ b/tests/qemu-iotests/126.out
19
@@ -XXX,XX +XXX,XX @@ QA output created by 126
20
=== Testing plain files ===
21
22
Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
23
-Formatting 'TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
24
+Formatting 'file:TEST_DIR/a:b.IMGFMT', fmt=IMGFMT size=67108864
25
26
=== Testing relative backing filename resolution ===
27
28
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
29
index XXXXXXX..XXXXXXX 100644
30
--- a/tests/qemu-iotests/common.filter
31
+++ 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()
43
44
discard=0
45
regex_json_spec_start='^ *"format-specific": \{'
46
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
47
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
48
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
49
-e "s#$TEST_DIR#TEST_DIR#g" \
50
-e "s#$IMGFMT#IMGFMT#g" \
51
-e 's#nbd://127.0.0.1:10810$#TEST_DIR/t.IMGFMT#g' \
52
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
53
index XXXXXXX..XXXXXXX 100644
54
--- a/tests/qemu-iotests/common.rc
55
+++ b/tests/qemu-iotests/common.rc
56
@@ -XXX,XX +XXX,XX @@ else
57
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
58
elif [ "$IMGPROTO" = "nfs" ]; then
59
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
60
+ REMOTE_TEST_DIR="nfs://127.0.0.1$TEST_DIR"
61
TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
62
elif [ "$IMGPROTO" = "vxhs" ]; then
63
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
64
@@ -XXX,XX +XXX,XX @@ if [ ! -d "$TEST_DIR" ]; then
65
exit 1
66
fi
67
68
+if [ -z "$REMOTE_TEST_DIR" ]; then
69
+ REMOTE_TEST_DIR="$TEST_DIR"
70
+fi
71
+
72
if [ ! -d "$SAMPLE_IMG_DIR" ]; then
73
echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory"
74
exit 1
75
@@ -XXX,XX +XXX,XX @@ _img_info()
76
discard=0
77
regex_json_spec_start='^ *"format-specific": \{'
78
$QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
79
- sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
80
+ sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \
81
+ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
82
-e "s#$TEST_DIR#TEST_DIR#g" \
83
-e "s#$IMGFMT#IMGFMT#g" \
84
-e "/^disk size:/ D" \
85
--
86
2.13.6
87
88
diff view generated by jsdifflib
Deleted patch
1
grep for "migrate" turns up a few test cases which use migration, but
2
haven't been in the "migration" group so far. Add them to the group.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Juan Quintela <quintela@redhat.com>
6
---
7
tests/qemu-iotests/group | 10 +++++-----
8
1 file changed, 5 insertions(+), 5 deletions(-)
9
10
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
11
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qemu-iotests/group
13
+++ b/tests/qemu-iotests/group
14
@@ -XXX,XX +XXX,XX @@
15
088 rw auto quick
16
089 rw auto quick
17
090 rw auto quick
18
-091 rw auto
19
+091 rw auto migration
20
092 rw auto quick
21
093 auto
22
094 rw auto quick
23
@@ -XXX,XX +XXX,XX @@
24
162 auto quick
25
163 rw auto
26
165 rw auto quick
27
-169 rw auto quick
28
+169 rw auto quick migration
29
170 rw auto quick
30
171 rw auto quick
31
172 auto
32
@@ -XXX,XX +XXX,XX @@
33
192 rw auto quick
34
194 rw auto migration quick
35
195 rw auto quick
36
-196 rw auto quick
37
+196 rw auto quick migration
38
197 rw auto quick
39
198 rw auto
40
-199 rw auto
41
+199 rw auto migration
42
200 rw auto
43
201 rw auto migration
44
202 rw auto quick
45
-203 rw auto
46
+203 rw auto migration
47
204 rw auto quick
48
205 rw auto quick
49
206 rw auto
50
--
51
2.13.6
52
53
diff view generated by jsdifflib
Deleted patch
1
185 and 191 define a MIG_SOCKET even though they don't do anything with
2
migration. Remove the useless variable.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Juan Quintela <quintela@redhat.com>
6
---
7
tests/qemu-iotests/185 | 2 --
8
tests/qemu-iotests/191 | 2 --
9
2 files changed, 4 deletions(-)
10
11
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/185
14
+++ b/tests/qemu-iotests/185
15
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
16
here=`pwd`
17
status=1 # failure is the default!
18
19
-MIG_SOCKET="${TEST_DIR}/migrate"
20
-
21
_cleanup()
22
{
23
rm -f "${TEST_IMG}.mid"
24
diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191
25
index XXXXXXX..XXXXXXX 100755
26
--- a/tests/qemu-iotests/191
27
+++ b/tests/qemu-iotests/191
28
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
29
here=`pwd`
30
status=1 # failure is the default!
31
32
-MIG_SOCKET="${TEST_DIR}/migrate"
33
-
34
_cleanup()
35
{
36
rm -f "${TEST_IMG}.mid"
37
--
38
2.13.6
39
40
diff view generated by jsdifflib
Deleted patch
1
Clarify that len is just an estimation of the end value of offset, and
2
that offset increases monotonically while len can change arbitrarily.
3
1
4
While touching the documentation of offset, move it directly after len
5
to match the order of the declaration below.
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: John Snow <jsnow@redhat.com>
10
---
11
qapi/block-core.json | 9 ++++++---
12
1 file changed, 6 insertions(+), 3 deletions(-)
13
14
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/block-core.json
17
+++ b/qapi/block-core.json
18
@@ -XXX,XX +XXX,XX @@
19
# @device: The job identifier. Originally the device name but other
20
# values are allowed since QEMU 2.7
21
#
22
-# @len: the maximum progress value
23
+# @len: Estimated @offset value at the completion of the job. This value can
24
+# arbitrarily change while the job is running, in both directions.
25
+#
26
+# @offset: Progress made until now. The unit is arbitrary and the value can
27
+# only meaningfully be used for the ratio of @offset to @len. The
28
+# value is monotonically increasing.
29
#
30
# @busy: false if the job is known to be in a quiescent state, with
31
# no pending I/O. Since 1.3.
32
@@ -XXX,XX +XXX,XX @@
33
# @paused: whether the job is paused or, if @busy is true, will
34
# pause itself as soon as possible. Since 1.3.
35
#
36
-# @offset: the current progress value
37
-#
38
# @speed: the rate limit, bytes per second
39
#
40
# @io-status: the status of the job (since 1.3)
41
--
42
2.13.6
43
44
diff view generated by jsdifflib
Deleted patch
1
QAPI types aren't externally visible, so we can rename them without
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
1
5
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
qapi/block-core.json | 14 +++++++-------
11
include/block/blockjob_int.h | 2 +-
12
block/backup.c | 2 +-
13
block/commit.c | 2 +-
14
block/mirror.c | 4 ++--
15
block/stream.c | 2 +-
16
blockjob.c | 6 +++---
17
7 files changed, 16 insertions(+), 16 deletions(-)
18
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
index XXXXXXX..XXXXXXX 100644
21
--- a/qapi/block-core.json
22
+++ b/qapi/block-core.json
23
@@ -XXX,XX +XXX,XX @@
24
'data': ['top', 'full', 'none', 'incremental'] }
25
26
##
27
-# @BlockJobType:
28
+# @JobType:
29
#
30
-# Type of a block job.
31
+# Type of a background job.
32
#
33
# @commit: block commit job type, see "block-commit"
34
#
35
@@ -XXX,XX +XXX,XX @@
36
#
37
# Since: 1.7
38
##
39
-{ 'enum': 'BlockJobType',
40
+{ 'enum': 'JobType',
41
'data': ['commit', 'stream', 'mirror', 'backup'] }
42
43
##
44
@@ -XXX,XX +XXX,XX @@
45
#
46
##
47
{ 'event': 'BLOCK_JOB_COMPLETED',
48
- 'data': { 'type' : 'BlockJobType',
49
+ 'data': { 'type' : 'JobType',
50
'device': 'str',
51
'len' : 'int',
52
'offset': 'int',
53
@@ -XXX,XX +XXX,XX @@
54
#
55
##
56
{ 'event': 'BLOCK_JOB_CANCELLED',
57
- 'data': { 'type' : 'BlockJobType',
58
+ 'data': { 'type' : 'JobType',
59
'device': 'str',
60
'len' : 'int',
61
'offset': 'int',
62
@@ -XXX,XX +XXX,XX @@
63
#
64
##
65
{ 'event': 'BLOCK_JOB_READY',
66
- 'data': { 'type' : 'BlockJobType',
67
+ 'data': { 'type' : 'JobType',
68
'device': 'str',
69
'len' : 'int',
70
'offset': 'int',
71
@@ -XXX,XX +XXX,XX @@
72
#
73
##
74
{ 'event': 'BLOCK_JOB_PENDING',
75
- 'data': { 'type' : 'BlockJobType',
76
+ 'data': { 'type' : 'JobType',
77
'id' : 'str' } }
78
79
##
80
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
81
index XXXXXXX..XXXXXXX 100644
82
--- a/include/block/blockjob_int.h
83
+++ b/include/block/blockjob_int.h
84
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
85
JobDriver job_driver;
86
87
/** String describing the operation, part of query-block-jobs QMP API */
88
- BlockJobType job_type;
89
+ JobType job_type;
90
91
/** Mandatory: Entrypoint for the Coroutine. */
92
CoroutineEntry *start;
93
diff --git a/block/backup.c b/block/backup.c
94
index XXXXXXX..XXXXXXX 100644
95
--- a/block/backup.c
96
+++ b/block/backup.c
97
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
98
.job_driver = {
99
.instance_size = sizeof(BackupBlockJob),
100
},
101
- .job_type = BLOCK_JOB_TYPE_BACKUP,
102
+ .job_type = JOB_TYPE_BACKUP,
103
.start = backup_run,
104
.commit = backup_commit,
105
.abort = backup_abort,
106
diff --git a/block/commit.c b/block/commit.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/block/commit.c
109
+++ b/block/commit.c
110
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
111
.job_driver = {
112
.instance_size = sizeof(CommitBlockJob),
113
},
114
- .job_type = BLOCK_JOB_TYPE_COMMIT,
115
+ .job_type = JOB_TYPE_COMMIT,
116
.start = commit_run,
117
};
118
119
diff --git a/block/mirror.c b/block/mirror.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/block/mirror.c
122
+++ b/block/mirror.c
123
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
124
.job_driver = {
125
.instance_size = sizeof(MirrorBlockJob),
126
},
127
- .job_type = BLOCK_JOB_TYPE_MIRROR,
128
+ .job_type = JOB_TYPE_MIRROR,
129
.start = mirror_run,
130
.complete = mirror_complete,
131
.pause = mirror_pause,
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
133
.job_driver = {
134
.instance_size = sizeof(MirrorBlockJob),
135
},
136
- .job_type = BLOCK_JOB_TYPE_COMMIT,
137
+ .job_type = JOB_TYPE_COMMIT,
138
.start = mirror_run,
139
.complete = mirror_complete,
140
.pause = mirror_pause,
141
diff --git a/block/stream.c b/block/stream.c
142
index XXXXXXX..XXXXXXX 100644
143
--- a/block/stream.c
144
+++ b/block/stream.c
145
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
146
.job_driver = {
147
.instance_size = sizeof(StreamBlockJob),
148
},
149
- .job_type = BLOCK_JOB_TYPE_STREAM,
150
+ .job_type = JOB_TYPE_STREAM,
151
.start = stream_run,
152
};
153
154
diff --git a/blockjob.c b/blockjob.c
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
{
160
BlockJob *job = c->opaque;
161
return g_strdup_printf("%s job '%s'",
162
- BlockJobType_str(job->driver->job_type),
163
+ JobType_str(job->driver->job_type),
164
job->job.id);
165
}
166
167
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
168
return NULL;
169
}
170
info = g_new0(BlockJobInfo, 1);
171
- info->type = g_strdup(BlockJobType_str(job->driver->job_type));
172
+ info->type = g_strdup(JobType_str(job->driver->job_type));
173
info->device = g_strdup(job->job.id);
174
info->len = job->len;
175
info->busy = atomic_read(&job->busy);
176
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
177
block_job_sleep_timer_cb, job);
178
179
error_setg(&job->blocker, "block device is in use by block job: %s",
180
- BlockJobType_str(driver->job_type));
181
+ JobType_str(driver->job_type));
182
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
183
bs->job = job;
184
185
--
186
2.13.6
187
188
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
We cannot yet move the whole logic around job cancelling to Job because
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
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
9
include/block/blockjob.h | 8 --------
10
include/block/blockjob_int.h | 8 --------
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
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
25
+++ b/include/block/blockjob.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
27
Coroutine *co;
28
29
/**
30
- * Set to true if the job should cancel itself. The flag must
31
- * always be tested just before toggling the busy flag from false
32
- * to true. After a job has been cancelled, it should only yield
33
- * if #aio_poll will ("sooner or later") reenter the coroutine.
34
- */
35
- bool cancelled;
36
-
37
- /**
38
* Set to true if the job should abort immediately without waiting
39
* for data to be in sync.
40
*/
41
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/block/blockjob_int.h
44
+++ b/include/block/blockjob_int.h
45
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job);
46
void block_job_completed(BlockJob *job, int ret);
47
48
/**
49
- * block_job_is_cancelled:
50
- * @job: The job being queried.
51
- *
52
- * Returns whether the job is scheduled for cancellation.
53
- */
54
-bool block_job_is_cancelled(BlockJob *job);
55
-
56
-/**
57
* block_job_pause_point:
58
* @job: The job that is ready to pause.
59
*
60
diff --git a/include/qemu/job.h b/include/qemu/job.h
61
index XXXXXXX..XXXXXXX 100644
62
--- a/include/qemu/job.h
63
+++ b/include/qemu/job.h
64
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
65
/** Current state; See @JobStatus for details. */
66
JobStatus status;
67
68
+ /**
69
+ * Set to true if the job should cancel itself. The flag must
70
+ * always be tested just before toggling the busy flag from false
71
+ * to true. After a job has been cancelled, it should only yield
72
+ * if #aio_poll will ("sooner or later") reenter the coroutine.
73
+ */
74
+ bool cancelled;
75
+
76
/** Element of the list of jobs */
77
QLIST_ENTRY(Job) job_list;
78
} Job;
79
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
80
/** Returns the enum string for the JobType of a given Job. */
81
const char *job_type_str(const Job *job);
82
83
+/** Returns whether the job is scheduled for cancellation. */
84
+bool job_is_cancelled(Job *job);
85
+
86
/**
87
* Get the next element from the list of block jobs after @job, or the
88
* first one if @job is %NULL.
89
diff --git a/block/backup.c b/block/backup.c
90
index XXXXXXX..XXXXXXX 100644
91
--- a/block/backup.c
92
+++ b/block/backup.c
93
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
94
{
95
uint64_t delay_ns;
96
97
- if (block_job_is_cancelled(&job->common)) {
98
+ if (job_is_cancelled(&job->common.job)) {
99
return true;
100
}
101
102
@@ -XXX,XX +XXX,XX @@ static bool coroutine_fn yield_and_check(BackupBlockJob *job)
103
job->bytes_read = 0;
104
block_job_sleep_ns(&job->common, delay_ns);
105
106
- if (block_job_is_cancelled(&job->common)) {
107
+ if (job_is_cancelled(&job->common.job)) {
108
return true;
109
}
110
111
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
112
if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
113
/* All bits are set in copy_bitmap to allow any cluster to be copied.
114
* This does not actually require them to be copied. */
115
- while (!block_job_is_cancelled(&job->common)) {
116
+ while (!job_is_cancelled(&job->common.job)) {
117
/* Yield until the job is cancelled. We just let our before_write
118
* notify callback service CoW requests. */
119
block_job_yield(&job->common);
120
diff --git a/block/commit.c b/block/commit.c
121
index XXXXXXX..XXXXXXX 100644
122
--- a/block/commit.c
123
+++ b/block/commit.c
124
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
125
* the normal backing chain can be restored. */
126
blk_unref(s->base);
127
128
- if (!block_job_is_cancelled(&s->common) && ret == 0) {
129
+ if (!job_is_cancelled(&s->common.job) && ret == 0) {
130
/* success */
131
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
132
s->backing_file_str);
133
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
134
* with no pending I/O here so that bdrv_drain_all() returns.
135
*/
136
block_job_sleep_ns(&s->common, delay_ns);
137
- if (block_job_is_cancelled(&s->common)) {
138
+ if (job_is_cancelled(&s->common.job)) {
139
break;
140
}
141
/* Copy if allocated above the base */
142
diff --git a/block/mirror.c b/block/mirror.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/mirror.c
145
+++ b/block/mirror.c
146
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
147
148
mirror_throttle(s);
149
150
- if (block_job_is_cancelled(&s->common)) {
151
+ if (job_is_cancelled(&s->common.job)) {
152
s->initial_zeroing_ongoing = false;
153
return 0;
154
}
155
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn mirror_dirty_init(MirrorBlockJob *s)
156
157
mirror_throttle(s);
158
159
- if (block_job_is_cancelled(&s->common)) {
160
+ if (job_is_cancelled(&s->common.job)) {
161
return 0;
162
}
163
164
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
165
checking for a NULL string */
166
int ret = 0;
167
168
- if (block_job_is_cancelled(&s->common)) {
169
+ if (job_is_cancelled(&s->common.job)) {
170
goto immediate_exit;
171
}
172
173
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
174
/* Report BLOCK_JOB_READY and wait for complete. */
175
block_job_event_ready(&s->common);
176
s->synced = true;
177
- while (!block_job_is_cancelled(&s->common) && !s->should_complete) {
178
+ while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
179
block_job_yield(&s->common);
180
}
181
- s->common.cancelled = false;
182
+ s->common.job.cancelled = false;
183
goto immediate_exit;
184
}
185
186
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
187
s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME);
188
if (!s->is_none_mode) {
189
ret = mirror_dirty_init(s);
190
- if (ret < 0 || block_job_is_cancelled(&s->common)) {
191
+ if (ret < 0 || job_is_cancelled(&s->common.job)) {
192
goto immediate_exit;
193
}
194
}
195
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
196
}
197
198
should_complete = s->should_complete ||
199
- block_job_is_cancelled(&s->common);
200
+ job_is_cancelled(&s->common.job);
201
cnt = bdrv_get_dirty_count(s->dirty_bitmap);
202
}
203
204
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
205
* completion.
206
*/
207
assert(QLIST_EMPTY(&bs->tracked_requests));
208
- s->common.cancelled = false;
209
+ s->common.job.cancelled = false;
210
need_drain = false;
211
break;
212
}
213
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
214
}
215
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
216
block_job_sleep_ns(&s->common, delay_ns);
217
- if (block_job_is_cancelled(&s->common) &&
218
+ if (job_is_cancelled(&s->common.job) &&
219
(!s->synced || s->common.force))
220
{
221
break;
222
@@ -XXX,XX +XXX,XX @@ immediate_exit:
223
* the target is a copy of the source.
224
*/
225
assert(ret < 0 || ((s->common.force || !s->synced) &&
226
- block_job_is_cancelled(&s->common)));
227
+ job_is_cancelled(&s->common.job)));
228
assert(need_drain);
229
mirror_wait_for_all_io(s);
230
}
231
diff --git a/block/stream.c b/block/stream.c
232
index XXXXXXX..XXXXXXX 100644
233
--- a/block/stream.c
234
+++ b/block/stream.c
235
@@ -XXX,XX +XXX,XX @@ static void stream_complete(BlockJob *job, void *opaque)
236
BlockDriverState *base = s->base;
237
Error *local_err = NULL;
238
239
- if (!block_job_is_cancelled(&s->common) && bs->backing &&
240
+ if (!job_is_cancelled(&s->common.job) && bs->backing &&
241
data->ret == 0) {
242
const char *base_id = NULL, *base_fmt = NULL;
243
if (base) {
244
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
245
* with no pending I/O here so that bdrv_drain_all() returns.
246
*/
247
block_job_sleep_ns(&s->common, delay_ns);
248
- if (block_job_is_cancelled(&s->common)) {
249
+ if (job_is_cancelled(&s->common.job)) {
250
break;
251
}
252
253
diff --git a/blockjob.c b/blockjob.c
254
index XXXXXXX..XXXXXXX 100644
255
--- a/blockjob.c
256
+++ b/blockjob.c
257
@@ -XXX,XX +XXX,XX @@ static void block_job_conclude(BlockJob *job)
258
259
static void block_job_update_rc(BlockJob *job)
260
{
261
- if (!job->ret && block_job_is_cancelled(job)) {
262
+ if (!job->ret && job_is_cancelled(&job->job)) {
263
job->ret = -ECANCELED;
264
}
265
if (job->ret) {
266
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
267
268
/* Emit events only if we actually started */
269
if (block_job_started(job)) {
270
- if (block_job_is_cancelled(job)) {
271
+ if (job_is_cancelled(&job->job)) {
272
block_job_event_cancelled(job);
273
} else {
274
const char *msg = NULL;
275
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job, bool force)
276
job->user_paused = false;
277
job->pause_count--;
278
}
279
- job->cancelled = true;
280
+ job->job.cancelled = true;
281
/* To prevent 'force == false' overriding a previous 'force == true' */
282
job->force |= force;
283
}
284
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
285
while (!job->completed) {
286
aio_poll(qemu_get_aio_context(), true);
287
}
288
- ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
289
+ ret = (job_is_cancelled(&job->job) && job->ret == 0)
290
+ ? -ECANCELED : job->ret;
291
job_unref(&job->job);
292
return ret;
293
}
294
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
295
other_job = QLIST_FIRST(&txn->jobs);
296
ctx = blk_get_aio_context(other_job->blk);
297
if (!other_job->completed) {
298
- assert(other_job->cancelled);
299
+ assert(job_is_cancelled(&other_job->job));
300
block_job_finish_sync(other_job, NULL, NULL);
301
}
302
block_job_finalize_single(other_job);
303
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
304
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
305
return;
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
420
--
421
2.13.6
422
423
diff view generated by jsdifflib
Deleted patch
1
There is nothing block layer specific about block_job_sleep_ns(), so
2
move the function to Job.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: John Snow <jsnow@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
8
include/block/blockjob_int.h | 11 -----------
9
include/qemu/job.h | 19 ++++++++++++++++++-
10
block/backup.c | 2 +-
11
block/commit.c | 2 +-
12
block/mirror.c | 4 ++--
13
block/stream.c | 2 +-
14
blockjob.c | 27 ---------------------------
15
job.c | 32 ++++++++++++++++++++++++++++++++
16
tests/test-bdrv-drain.c | 8 ++++----
17
tests/test-blockjob-txn.c | 2 +-
18
tests/test-blockjob.c | 2 +-
19
11 files changed, 61 insertions(+), 50 deletions(-)
20
21
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob_int.h
24
+++ b/include/block/blockjob_int.h
25
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
26
void block_job_free(Job *job);
27
28
/**
29
- * block_job_sleep_ns:
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
*/
72
void coroutine_fn job_pause_point(Job *job);
73
74
+/**
75
+ * @job: The job that calls the function.
76
+ * @ns: How many nanoseconds to stop for.
77
+ *
78
+ * Put the job to sleep (assuming that it wasn't canceled) for @ns
79
+ * %QEMU_CLOCK_REALTIME nanoseconds. Canceling the job will immediately
80
+ * interrupt the wait.
81
+ */
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
}
126
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
127
cnt == 0 ? BLOCK_JOB_SLICE_TIME : 0);
128
}
129
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
130
- block_job_sleep_ns(&s->common, delay_ns);
131
+ job_sleep_ns(&s->common.job, delay_ns);
132
if (job_is_cancelled(&s->common.job) &&
133
(!s->synced || s->common.force))
134
{
135
diff --git a/block/stream.c b/block/stream.c
136
index XXXXXXX..XXXXXXX 100644
137
--- a/block/stream.c
138
+++ b/block/stream.c
139
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
140
/* Note that even when no rate limit is applied we need to yield
141
* with no pending I/O here so that bdrv_drain_all() returns.
142
*/
143
- block_job_sleep_ns(&s->common, delay_ns);
144
+ job_sleep_ns(&s->common.job, delay_ns);
145
if (job_is_cancelled(&s->common.job)) {
146
break;
147
}
148
diff --git a/blockjob.c b/blockjob.c
149
index XXXXXXX..XXXXXXX 100644
150
--- a/blockjob.c
151
+++ b/blockjob.c
152
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
153
block_job_detach_aio_context, bjob);
154
blk_unref(bjob->blk);
155
error_free(bjob->blocker);
156
- assert(!timer_pending(&bjob->job.sleep_timer));
157
}
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
}
257
}
258
259
+void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
260
+{
261
+ assert(job->busy);
262
+
263
+ /* Check cancellation *before* setting busy = false, too! */
264
+ if (job_is_cancelled(job)) {
265
+ return;
266
+ }
267
+
268
+ if (!job_should_pause(job)) {
269
+ job_do_yield(job, qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + ns);
270
+ }
271
+
272
+ job_pause_point(job);
273
+}
274
+
275
/**
276
* All jobs must allow a pause point before entering their job proper. This
277
* ensures that jobs can be paused prior to being started, then resumed later.
278
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/tests/test-bdrv-drain.c
281
+++ b/tests/test-bdrv-drain.c
282
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
283
284
block_job_event_ready(&s->common);
285
while (!s->should_complete) {
286
- block_job_sleep_ns(&s->common, 100000);
287
+ job_sleep_ns(&s->common.job, 100000);
288
}
289
290
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
291
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
292
293
g_assert_cmpint(job->job.pause_count, ==, 0);
294
g_assert_false(job->job.paused);
295
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
296
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
297
298
do_drain_begin(drain_type, src);
299
300
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
301
302
g_assert_cmpint(job->job.pause_count, ==, 0);
303
g_assert_false(job->job.paused);
304
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
305
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
306
307
do_drain_begin(drain_type, target);
308
309
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
310
311
g_assert_cmpint(job->job.pause_count, ==, 0);
312
g_assert_false(job->job.paused);
313
- g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
314
+ g_assert_false(job->job.busy); /* We're in job_sleep_ns() */
315
316
ret = block_job_complete_sync(job, &error_abort);
317
g_assert_cmpint(ret, ==, 0);
318
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
319
index XXXXXXX..XXXXXXX 100644
320
--- a/tests/test-blockjob-txn.c
321
+++ b/tests/test-blockjob-txn.c
322
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
323
324
while (s->iterations--) {
325
if (s->use_timer) {
326
- block_job_sleep_ns(job, 0);
327
+ job_sleep_ns(&job->job, 0);
328
} else {
329
block_job_yield(job);
330
}
331
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
332
index XXXXXXX..XXXXXXX 100644
333
--- a/tests/test-blockjob.c
334
+++ b/tests/test-blockjob.c
335
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
336
block_job_event_ready(&s->common);
337
}
338
339
- block_job_sleep_ns(&s->common, 100000);
340
+ job_sleep_ns(&s->common.job, 100000);
341
}
342
343
defer:
344
--
345
2.13.6
346
347
diff view generated by jsdifflib
Deleted patch
1
block_job_event_pending() doesn't only send a QMP event, but it also
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
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
9
blockjob.c | 27 ++++++++++++++++++---------
10
1 file changed, 18 insertions(+), 9 deletions(-)
11
12
diff --git a/blockjob.c b/blockjob.c
13
index XXXXXXX..XXXXXXX 100644
14
--- a/blockjob.c
15
+++ b/blockjob.c
16
@@ -XXX,XX +XXX,XX @@
17
18
static void block_job_event_cancelled(BlockJob *job);
19
static void block_job_event_completed(BlockJob *job, const char *msg);
20
-static int block_job_event_pending(BlockJob *job);
21
+static void block_job_event_pending(BlockJob *job);
22
23
/* Transactional group of block jobs */
24
struct BlockJobTxn {
25
@@ -XXX,XX +XXX,XX @@ static void block_job_do_finalize(BlockJob *job)
26
}
27
}
28
29
+static int block_job_transition_to_pending(BlockJob *job)
30
+{
31
+ job_state_transition(&job->job, JOB_STATUS_PENDING);
32
+ if (!job->job.auto_finalize) {
33
+ block_job_event_pending(job);
34
+ }
35
+ return 0;
36
+}
37
+
38
static void block_job_completed_txn_success(BlockJob *job)
39
{
40
BlockJobTxn *txn = job->txn;
41
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
42
assert(other_job->ret == 0);
43
}
44
45
- block_job_txn_apply(txn, block_job_event_pending, false);
46
+ block_job_txn_apply(txn, block_job_transition_to_pending, false);
47
48
/* If no jobs need manual finalization, automatically do so */
49
if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
50
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
51
&error_abort);
52
}
53
54
-static int block_job_event_pending(BlockJob *job)
55
+static void block_job_event_pending(BlockJob *job)
56
{
57
- job_state_transition(&job->job, JOB_STATUS_PENDING);
58
- if (!job->job.auto_finalize && !block_job_is_internal(job)) {
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
}
65
- return 0;
66
+
67
+ qapi_event_send_block_job_pending(job_type(&job->job),
68
+ job->job.id,
69
+ &error_abort);
70
}
71
72
/*
73
--
74
2.13.6
75
76
diff view generated by jsdifflib
1
This adds QMP commands that control the transition between states of the
1
From: Leonid Bloch <lbloch@janustech.com>
2
job lifecycle.
3
2
3
The lookup table for power-of-two sizes was added in commit 540b8492618eb
4
for the purpose of having convenient shortcuts for these sizes in cases
5
when the literal number has to be present at compile time, and
6
expressions as '(1 * KiB)' can not be used. One such case is the
7
stringification of sizes. Beyond that, it is convenient to use these
8
shortcuts for all power-of-two sizes, even if they don't have to be
9
literal numbers.
10
11
Despite its convenience, this table introduced 55 lines of "dumb" code,
12
the purpose and origin of which are obscure without reading the message
13
of the commit which introduced it. This patch fixes that by adding a
14
comment to the code itself with a brief explanation for the reasoning
15
behind this table. This comment includes the short AWK script that
16
generated the table, so that anyone who's interested could make sure
17
that the values in it are correct (otherwise these values look as if
18
they were typed manually).
19
20
Signed-off-by: Leonid Bloch <lbloch@janustech.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
22
---
6
qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++++++++
23
include/qemu/units.h | 18 ++++++++++++++++++
7
job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
24
1 file changed, 18 insertions(+)
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
25
14
diff --git a/qapi/job.json b/qapi/job.json
26
diff --git a/include/qemu/units.h b/include/qemu/units.h
15
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/job.json
28
--- a/include/qemu/units.h
17
+++ b/qapi/job.json
29
+++ b/include/qemu/units.h
18
@@ -XXX,XX +XXX,XX @@
30
@@ -XXX,XX +XXX,XX @@
19
{ 'event': 'JOB_STATUS_CHANGE',
31
#define PiB (INT64_C(1) << 50)
20
'data': { 'id': 'str',
32
#define EiB (INT64_C(1) << 60)
21
'status': 'JobStatus' } }
33
22
+
23
+##
24
+# @job-pause:
25
+#
26
+# Pause an active job.
27
+#
28
+# This command returns immediately after marking the active job for pausing.
29
+# Pausing an already paused job is an error.
30
+#
31
+# The job will pause as soon as possible, which means transitioning into the
32
+# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The
33
+# corresponding JOB_STATUS_CHANGE event will be emitted.
34
+#
35
+# Cancelling a paused job automatically resumes it.
36
+#
37
+# @id: The job identifier.
38
+#
39
+# Since: 2.13
40
+##
41
+{ 'command': 'job-pause', 'data': { 'id': 'str' } }
42
+
43
+##
44
+# @job-resume:
45
+#
46
+# Resume a paused job.
47
+#
48
+# This command returns immediately after resuming a paused job. Resuming an
49
+# already running job is an error.
50
+#
51
+# @id : The job identifier.
52
+#
53
+# Since: 2.13
54
+##
55
+{ 'command': 'job-resume', 'data': { 'id': 'str' } }
56
+
57
+##
58
+# @job-cancel:
59
+#
60
+# Instruct an active background job to cancel at the next opportunity.
61
+# This command returns immediately after marking the active job for
62
+# cancellation.
63
+#
64
+# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE
65
+# event. Usually, the status will change to ABORTING, but it is possible that
66
+# a job successfully completes (e.g. because it was almost done and there was
67
+# no opportunity to cancel earlier than completing the job) and transitions to
68
+# PENDING instead.
69
+#
70
+# @id: The job identifier.
71
+#
72
+# Since: 2.13
73
+##
74
+{ 'command': 'job-cancel', 'data': { 'id': 'str' } }
75
+
76
+
77
+##
78
+# @job-complete:
79
+#
80
+# Manually trigger completion of an active job in the READY state.
81
+#
82
+# @id: The job identifier.
83
+#
84
+# Since: 2.13
85
+##
86
+{ 'command': 'job-complete', 'data': { 'id': 'str' } }
87
+
88
+##
89
+# @job-dismiss:
90
+#
91
+# Deletes a job that is in the CONCLUDED state. This command only needs to be
92
+# run explicitly for jobs that don't have automatic dismiss enabled.
93
+#
94
+# This command will refuse to operate on any job that has not yet reached its
95
+# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY
96
+# event, job-cancel or job-complete will still need to be used as appropriate.
97
+#
98
+# @id: The job identifier.
99
+#
100
+# Since: 2.13
101
+##
102
+{ 'command': 'job-dismiss', 'data': { 'id': 'str' } }
103
+
104
+##
105
+# @job-finalize:
106
+#
107
+# Instructs all jobs in a transaction (or a single job if it is not part of any
108
+# transaction) to finalize any graph changes and do any necessary cleanup. This
109
+# command requires that all involved jobs are in the PENDING state.
110
+#
111
+# For jobs in a transaction, instructing one job to finalize will force
112
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
113
+# a single member job to finalize.
114
+#
115
+# @id: The identifier of any job in the transaction, or of a job that is not
116
+# part of any transaction.
117
+#
118
+# Since: 2.13
119
+##
120
+{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
121
diff --git a/job-qmp.c b/job-qmp.c
122
new file mode 100644
123
index XXXXXXX..XXXXXXX
124
--- /dev/null
125
+++ b/job-qmp.c
126
@@ -XXX,XX +XXX,XX @@
127
+/*
34
+/*
128
+ * QMP interface for background jobs
35
+ * The following lookup table is intended to be used when a literal string of
36
+ * the number of bytes is required (for example if it needs to be stringified).
37
+ * It can also be used for generic shortcuts of power-of-two sizes.
38
+ * This table is generated using the AWK script below:
129
+ *
39
+ *
130
+ * Copyright (c) 2011 IBM Corp.
40
+ * BEGIN {
131
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
41
+ * suffix="KMGTPE";
132
+ *
42
+ * for(i=10; i<64; i++) {
133
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
43
+ * val=2**i;
134
+ * of this software and associated documentation files (the "Software"), to deal
44
+ * s=substr(suffix, int(i/10), 1);
135
+ * in the Software without restriction, including without limitation the rights
45
+ * n=2**(i%10);
136
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
46
+ * pad=21-int(log(n)/log(10));
137
+ * copies of the Software, and to permit persons to whom the Software is
47
+ * printf("#define S_%d%siB %*d\n", n, s, pad, val);
138
+ * furnished to do so, subject to the following conditions:
48
+ * }
139
+ *
49
+ * }
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
+ */
50
+ */
151
+
51
+
152
+#include "qemu/osdep.h"
52
#define S_1KiB 1024
153
+#include "qemu-common.h"
53
#define S_2KiB 2048
154
+#include "qemu/job.h"
54
#define S_4KiB 4096
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
305
--
55
--
306
2.13.6
56
2.19.1
307
57
308
58
diff view generated by jsdifflib