1
The following changes since commit 4f50c1673a89b07f376ce5c42d22d79a79cd466d:
1
The following changes since commit 9436e082de18b2fb2ceed2e9d1beef641ae64f23:
2
2
3
Merge remote-tracking branch 'remotes/ehabkost/tags/x86-next-pull-request' into staging (2018-05-22 09:43:58 +0100)
3
MAINTAINERS: clarify some of the tags (2018-11-19 11:19:23 +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 6d0a4a0fb5c8f10c8eb68b52cfda0082b00ae963:
10
10
11
qemu-iotests: Test job-* with block jobs (2018-05-23 14:30:52 +0200)
11
iotests: Test file-posix locking and reopen (2018-11-19 14:32:04 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches:
14
Block layer patches:
15
15
16
- Generic background jobs
16
- file-posix: Fix shared permission locks after reopen
17
- qemu-iotests fixes for NFS and the 'migration' group
17
- block: Fix error path for failed .bdrv_reopen_prepare
18
- sheepdog: Minor code simplification
18
- qcow2: Catch invalid allocations when the image becomes too large
19
- vvfat/fdc/nvme: Fix segfaults and leaks
19
20
20
----------------------------------------------------------------
21
----------------------------------------------------------------
21
Kevin Wolf (45):
22
Eric Blake (3):
22
qemu-iotests: Fix paths for NFS
23
qcow2: Document some maximum size constraints
23
qemu-iotests: Filter NFS paths
24
qcow2: Don't allow overflow during cluster allocation
24
qemu-iotests: 086 doesn't work with NFS
25
iotests: Add new test 220 for max compressed cluster offset
25
qemu-iotests: Add more tests to "migration" group
26
qemu-iotests: Remove MIG_SOCKET from non-migration tests
27
blockjob: Update block-job-pause/resume documentation
28
blockjob: Improve BlockJobInfo.offset/len documentation
29
job: Create Job, JobDriver and job_create()
30
job: Rename BlockJobType into JobType
31
job: Add JobDriver.job_type
32
job: Add job_delete()
33
job: Maintain a list of all jobs
34
job: Move state transitions to Job
35
job: Add reference counting
36
job: Move cancelled to Job
37
job: Add Job.aio_context
38
job: Move defer_to_main_loop to Job
39
job: Move coroutine and related code to Job
40
job: Add job_sleep_ns()
41
job: Move pause/resume functions to Job
42
job: Replace BlockJob.completed with job_is_completed()
43
job: Move BlockJobCreateFlags to Job
44
blockjob: Split block_job_event_pending()
45
job: Add job_event_*()
46
job: Move single job finalisation to Job
47
job: Convert block_job_cancel_async() to Job
48
job: Add job_drain()
49
job: Move .complete callback to Job
50
job: Move job_finish_sync() to Job
51
job: Switch transactions to JobTxn
52
job: Move transactions to Job
53
job: Move completion and cancellation to Job
54
block: Cancel job in bdrv_close_all() callers
55
job: Add job_yield()
56
job: Add job_dismiss()
57
job: Add job_is_ready()
58
job: Add job_transition_to_ready()
59
job: Move progress fields to Job
60
job: Introduce qapi/job.json
61
job: Add JOB_STATUS_CHANGE QMP event
62
job: Add lifecycle QMP commands
63
job: Add query-jobs QMP command
64
blockjob: Remove BlockJob.driver
65
iotests: Move qmp_to_opts() to VM
66
qemu-iotests: Test job-* with block jobs
67
26
68
Peter Maydell (1):
27
Kevin Wolf (1):
69
sheepdog: Remove unnecessary NULL check in sd_prealloc()
28
vvfat: Fix memory leak
70
29
71
qapi/block-core.json | 116 +---
30
Li Qiang (1):
72
qapi/job.json | 253 +++++++++
31
nvme: fix oob access issue(CVE-2018-16847)
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
32
33
Mark Cave-Ayland (1):
34
fdc: fix segfault in fdctrl_stop_transfer() when DMA is disabled
35
36
Max Reitz (3):
37
block: Always abort reopen after prepare succeeded
38
file-posix: Fix shared locks on reopen commit
39
iotests: Test file-posix locking and reopen
40
41
docs/interop/qcow2.txt | 38 +++++++++++++++++-
42
block/qcow2.h | 6 +++
43
block.c | 12 ++++++
44
block/file-posix.c | 2 +-
45
block/qcow2-refcount.c | 20 ++++++----
46
block/vvfat.c | 6 +--
47
hw/block/fdc.c | 2 +-
48
hw/block/nvme.c | 7 ++++
49
tests/qemu-iotests/182 | 71 ++++++++++++++++++++++++++++++++++
50
tests/qemu-iotests/182.out | 9 +++++
51
tests/qemu-iotests/220 | 96 ++++++++++++++++++++++++++++++++++++++++++++++
52
tests/qemu-iotests/220.out | 54 ++++++++++++++++++++++++++
53
tests/qemu-iotests/group | 1 +
54
13 files changed, 310 insertions(+), 14 deletions(-)
55
create mode 100755 tests/qemu-iotests/220
56
create mode 100644 tests/qemu-iotests/220.out
57
diff view generated by jsdifflib
Deleted patch
1
Test cases were trying to use nfs:// URLs as local filenames, which made
2
every test fail for NFS. With TEST_IMG and TEST_IMG_FILE set like for
3
the other protocols, NFS tests can pass again.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
7
---
8
tests/qemu-iotests/common.rc | 4 ++--
9
1 file changed, 2 insertions(+), 2 deletions(-)
10
11
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
12
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/common.rc
14
+++ b/tests/qemu-iotests/common.rc
15
@@ -XXX,XX +XXX,XX @@ else
16
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
17
TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE"
18
elif [ "$IMGPROTO" = "nfs" ]; then
19
- TEST_DIR="nfs://127.0.0.1/$TEST_DIR"
20
- TEST_IMG=$TEST_DIR/t.$IMGFMT
21
+ TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
22
+ TEST_IMG="nfs://127.0.0.1$TEST_IMG_FILE"
23
elif [ "$IMGPROTO" = "vxhs" ]; then
24
TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT
25
TEST_IMG="vxhs://127.0.0.1:9999/t.$IMGFMT"
26
--
27
2.13.6
28
29
diff view generated by jsdifflib
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
1
This adds a minimal query-jobs implementation that shouldn't pose many
1
From: Li Qiang <liq3ea@gmail.com>
2
design questions. It can later be extended to expose more information,
3
and especially job-specific information.
4
2
3
Currently, the nvme_cmb_ops mr doesn't check the addr and size.
4
This can lead an oob access issue. This is triggerable in the guest.
5
Add check to avoid this issue.
6
7
Fixes CVE-2018-16847.
8
9
Reported-by: Li Qiang <liq3ea@gmail.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
11
Signed-off-by: Li Qiang <liq3ea@gmail.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
13
---
7
qapi/job.json | 46 ++++++++++++++++++++++++++++++++++++++++++++++
14
hw/block/nvme.c | 7 +++++++
8
include/qemu/job.h | 3 +++
15
1 file changed, 7 insertions(+)
9
job-qmp.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
10
job.c | 2 +-
11
4 files changed, 104 insertions(+), 1 deletion(-)
12
16
13
diff --git a/qapi/job.json b/qapi/job.json
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
14
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
15
--- a/qapi/job.json
19
--- a/hw/block/nvme.c
16
+++ b/qapi/job.json
20
+++ b/hw/block/nvme.c
17
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data,
18
# Since: 2.13
22
unsigned size)
19
##
23
{
20
{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
24
NvmeCtrl *n = (NvmeCtrl *)opaque;
21
+
25
+
22
+##
26
+ if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
23
+# @JobInfo:
27
+ return;
24
+#
28
+ }
25
+# Information about a job.
29
memcpy(&n->cmbuf[addr], &data, size);
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
}
30
}
89
+
31
90
+static JobInfo *job_query_single(Job *job, Error **errp)
32
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size)
91
+{
33
uint64_t val;
92
+ JobInfo *info;
34
NvmeCtrl *n = (NvmeCtrl *)opaque;
93
+ const char *errmsg = NULL;
35
94
+
36
+ if (addr + size > NVME_CMBSZ_GETSIZE(n->bar.cmbsz)) {
95
+ assert(!job_is_internal(job));
37
+ return 0;
96
+
97
+ if (job->ret < 0) {
98
+ errmsg = strerror(-job->ret);
99
+ }
38
+ }
100
+
39
memcpy(&val, &n->cmbuf[addr], size);
101
+ info = g_new(JobInfo, 1);
40
return val;
102
+ *info = (JobInfo) {
103
+ .id = g_strdup(job->id),
104
+ .type = job_type(job),
105
+ .status = job->status,
106
+ .current_progress = job->progress_current,
107
+ .total_progress = job->progress_total,
108
+ .has_error = !!errmsg,
109
+ .error = g_strdup(errmsg),
110
+ };
111
+
112
+ return info;
113
+}
114
+
115
+JobInfoList *qmp_query_jobs(Error **errp)
116
+{
117
+ JobInfoList *head = NULL, **p_next = &head;
118
+ Job *job;
119
+
120
+ for (job = job_next(NULL); job; job = job_next(job)) {
121
+ JobInfoList *elem;
122
+ AioContext *aio_context;
123
+
124
+ if (job_is_internal(job)) {
125
+ continue;
126
+ }
127
+ elem = g_new0(JobInfoList, 1);
128
+ aio_context = job->aio_context;
129
+ aio_context_acquire(aio_context);
130
+ elem->value = job_query_single(job, errp);
131
+ aio_context_release(aio_context);
132
+ if (!elem->value) {
133
+ g_free(elem);
134
+ qapi_free_JobInfoList(head);
135
+ return NULL;
136
+ }
137
+ *p_next = elem;
138
+ p_next = &elem->next;
139
+ }
140
+
141
+ return head;
142
+}
143
diff --git a/job.c b/job.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/job.c
146
+++ b/job.c
147
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
148
return rc;
149
}
150
151
-static bool job_is_internal(Job *job)
152
+bool job_is_internal(Job *job)
153
{
154
return (job->id == NULL);
155
}
41
}
156
--
42
--
157
2.13.6
43
2.19.1
158
44
159
45
diff view generated by jsdifflib
1
This adds QMP commands that control the transition between states of the
1
From: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
2
job lifecycle.
3
2
3
Commit c8a35f1cf0f "fdc: use IsaDma interface instead of global DMA_*
4
functions" accidentally introduced a segfault in fdctrl_stop_transfer() for
5
non-DMA transfers.
6
7
If fdctrl->dma_chann has not been configured then the fdctrl->dma interface
8
reference isn't initialised during isabus_fdc_realize(). Unfortunately
9
fdctrl_stop_transfer() unconditionally references the DMA interface when
10
finishing the transfer causing a NULL pointer dereference.
11
12
Fix the issue by adding a check in fdctrl_stop_transfer() so that the DMA
13
interface reference and release method is only invoked if fdctrl->dma_chann
14
has been set.
15
16
(This issue was discovered by Martin testing a recent change in the NetBSD
17
installer under qemu-system-sparc)
18
19
Cc: qemu-stable@nongnu.org
20
Reported-by: Martin Husemann <martin@duskware.de>
21
Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk>
22
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
23
Reviewed-by: Hervé Poussineau <hpoussin@reactos.org>
24
Reviewed-by: John Snow <jsnow@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
25
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
26
---
6
qapi/job.json | 99 +++++++++++++++++++++++++++++++++++++++++++
27
hw/block/fdc.c | 2 +-
7
job-qmp.c | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
28
1 file changed, 1 insertion(+), 1 deletion(-)
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
29
14
diff --git a/qapi/job.json b/qapi/job.json
30
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
15
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
16
--- a/qapi/job.json
32
--- a/hw/block/fdc.c
17
+++ b/qapi/job.json
33
+++ b/hw/block/fdc.c
18
@@ -XXX,XX +XXX,XX @@
34
@@ -XXX,XX +XXX,XX @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0,
19
{ 'event': 'JOB_STATUS_CHANGE',
35
fdctrl->fifo[5] = cur_drv->sect;
20
'data': { 'id': 'str',
36
fdctrl->fifo[6] = FD_SECTOR_SC;
21
'status': 'JobStatus' } }
37
fdctrl->data_dir = FD_DIR_READ;
22
+
38
- if (!(fdctrl->msr & FD_MSR_NONDMA)) {
23
+##
39
+ if (fdctrl->dma_chann != -1 && !(fdctrl->msr & FD_MSR_NONDMA)) {
24
+# @job-pause:
40
IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma);
25
+#
41
k->release_DREQ(fdctrl->dma, fdctrl->dma_chann);
26
+# Pause an active job.
42
}
27
+#
28
+# This command returns immediately after marking the active job for pausing.
29
+# Pausing an already paused job is an error.
30
+#
31
+# The job will pause as soon as possible, which means transitioning into the
32
+# PAUSED state if it was RUNNING, or into STANDBY if it was READY. The
33
+# corresponding JOB_STATUS_CHANGE event will be emitted.
34
+#
35
+# Cancelling a paused job automatically resumes it.
36
+#
37
+# @id: The job identifier.
38
+#
39
+# Since: 2.13
40
+##
41
+{ 'command': 'job-pause', 'data': { 'id': 'str' } }
42
+
43
+##
44
+# @job-resume:
45
+#
46
+# Resume a paused job.
47
+#
48
+# This command returns immediately after resuming a paused job. Resuming an
49
+# already running job is an error.
50
+#
51
+# @id : The job identifier.
52
+#
53
+# Since: 2.13
54
+##
55
+{ 'command': 'job-resume', 'data': { 'id': 'str' } }
56
+
57
+##
58
+# @job-cancel:
59
+#
60
+# Instruct an active background job to cancel at the next opportunity.
61
+# This command returns immediately after marking the active job for
62
+# cancellation.
63
+#
64
+# The job will cancel as soon as possible and then emit a JOB_STATUS_CHANGE
65
+# event. Usually, the status will change to ABORTING, but it is possible that
66
+# a job successfully completes (e.g. because it was almost done and there was
67
+# no opportunity to cancel earlier than completing the job) and transitions to
68
+# PENDING instead.
69
+#
70
+# @id: The job identifier.
71
+#
72
+# Since: 2.13
73
+##
74
+{ 'command': 'job-cancel', 'data': { 'id': 'str' } }
75
+
76
+
77
+##
78
+# @job-complete:
79
+#
80
+# Manually trigger completion of an active job in the READY state.
81
+#
82
+# @id: The job identifier.
83
+#
84
+# Since: 2.13
85
+##
86
+{ 'command': 'job-complete', 'data': { 'id': 'str' } }
87
+
88
+##
89
+# @job-dismiss:
90
+#
91
+# Deletes a job that is in the CONCLUDED state. This command only needs to be
92
+# run explicitly for jobs that don't have automatic dismiss enabled.
93
+#
94
+# This command will refuse to operate on any job that has not yet reached its
95
+# terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of JOB_READY
96
+# event, job-cancel or job-complete will still need to be used as appropriate.
97
+#
98
+# @id: The job identifier.
99
+#
100
+# Since: 2.13
101
+##
102
+{ 'command': 'job-dismiss', 'data': { 'id': 'str' } }
103
+
104
+##
105
+# @job-finalize:
106
+#
107
+# Instructs all jobs in a transaction (or a single job if it is not part of any
108
+# transaction) to finalize any graph changes and do any necessary cleanup. This
109
+# command requires that all involved jobs are in the PENDING state.
110
+#
111
+# For jobs in a transaction, instructing one job to finalize will force
112
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
113
+# a single member job to finalize.
114
+#
115
+# @id: The identifier of any job in the transaction, or of a job that is not
116
+# part of any transaction.
117
+#
118
+# Since: 2.13
119
+##
120
+{ 'command': 'job-finalize', 'data': { 'id': 'str' } }
121
diff --git a/job-qmp.c b/job-qmp.c
122
new file mode 100644
123
index XXXXXXX..XXXXXXX
124
--- /dev/null
125
+++ b/job-qmp.c
126
@@ -XXX,XX +XXX,XX @@
127
+/*
128
+ * QMP interface for background jobs
129
+ *
130
+ * Copyright (c) 2011 IBM Corp.
131
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
132
+ *
133
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
134
+ * of this software and associated documentation files (the "Software"), to deal
135
+ * in the Software without restriction, including without limitation the rights
136
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
137
+ * copies of the Software, and to permit persons to whom the Software is
138
+ * furnished to do so, subject to the following conditions:
139
+ *
140
+ * The above copyright notice and this permission notice shall be included in
141
+ * all copies or substantial portions of the Software.
142
+ *
143
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
144
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
145
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
146
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
147
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
148
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
149
+ * THE SOFTWARE.
150
+ */
151
+
152
+#include "qemu/osdep.h"
153
+#include "qemu-common.h"
154
+#include "qemu/job.h"
155
+#include "qapi/qapi-commands-job.h"
156
+#include "qapi/error.h"
157
+#include "trace-root.h"
158
+
159
+/* Get a job using its ID and acquire its AioContext */
160
+static Job *find_job(const char *id, AioContext **aio_context, Error **errp)
161
+{
162
+ Job *job;
163
+
164
+ *aio_context = NULL;
165
+
166
+ job = job_get(id);
167
+ if (!job) {
168
+ error_setg(errp, "Job not found");
169
+ return NULL;
170
+ }
171
+
172
+ *aio_context = job->aio_context;
173
+ aio_context_acquire(*aio_context);
174
+
175
+ return job;
176
+}
177
+
178
+void qmp_job_cancel(const char *id, Error **errp)
179
+{
180
+ AioContext *aio_context;
181
+ Job *job = find_job(id, &aio_context, errp);
182
+
183
+ if (!job) {
184
+ return;
185
+ }
186
+
187
+ trace_qmp_job_cancel(job);
188
+ job_user_cancel(job, true, errp);
189
+ aio_context_release(aio_context);
190
+}
191
+
192
+void qmp_job_pause(const char *id, Error **errp)
193
+{
194
+ AioContext *aio_context;
195
+ Job *job = find_job(id, &aio_context, errp);
196
+
197
+ if (!job) {
198
+ return;
199
+ }
200
+
201
+ trace_qmp_job_pause(job);
202
+ job_user_pause(job, errp);
203
+ aio_context_release(aio_context);
204
+}
205
+
206
+void qmp_job_resume(const char *id, Error **errp)
207
+{
208
+ AioContext *aio_context;
209
+ Job *job = find_job(id, &aio_context, errp);
210
+
211
+ if (!job) {
212
+ return;
213
+ }
214
+
215
+ trace_qmp_job_resume(job);
216
+ job_user_resume(job, errp);
217
+ aio_context_release(aio_context);
218
+}
219
+
220
+void qmp_job_complete(const char *id, Error **errp)
221
+{
222
+ AioContext *aio_context;
223
+ Job *job = find_job(id, &aio_context, errp);
224
+
225
+ if (!job) {
226
+ return;
227
+ }
228
+
229
+ trace_qmp_job_complete(job);
230
+ job_complete(job, errp);
231
+ aio_context_release(aio_context);
232
+}
233
+
234
+void qmp_job_finalize(const char *id, Error **errp)
235
+{
236
+ AioContext *aio_context;
237
+ Job *job = find_job(id, &aio_context, errp);
238
+
239
+ if (!job) {
240
+ return;
241
+ }
242
+
243
+ trace_qmp_job_finalize(job);
244
+ job_finalize(job, errp);
245
+ aio_context_release(aio_context);
246
+}
247
+
248
+void qmp_job_dismiss(const char *id, Error **errp)
249
+{
250
+ AioContext *aio_context;
251
+ Job *job = find_job(id, &aio_context, errp);
252
+
253
+ if (!job) {
254
+ return;
255
+ }
256
+
257
+ trace_qmp_job_dismiss(job);
258
+ job_dismiss(&job, errp);
259
+ aio_context_release(aio_context);
260
+}
261
diff --git a/MAINTAINERS b/MAINTAINERS
262
index XXXXXXX..XXXXXXX 100644
263
--- a/MAINTAINERS
264
+++ b/MAINTAINERS
265
@@ -XXX,XX +XXX,XX @@ S: Supported
266
F: blockjob.c
267
F: include/block/blockjob.h
268
F: job.c
269
+F: job-qmp.c
270
F: include/block/job.h
271
F: block/backup.c
272
F: block/commit.c
273
diff --git a/Makefile.objs b/Makefile.objs
274
index XXXXXXX..XXXXXXX 100644
275
--- a/Makefile.objs
276
+++ b/Makefile.objs
277
@@ -XXX,XX +XXX,XX @@ io-obj-y = io/
278
ifeq ($(CONFIG_SOFTMMU),y)
279
common-obj-y = blockdev.o blockdev-nbd.o block/
280
common-obj-y += bootdevice.o iothread.o
281
+common-obj-y += job-qmp.o
282
common-obj-y += net/
283
common-obj-y += qdev-monitor.o device-hotplug.o
284
common-obj-$(CONFIG_WIN32) += os-win32.o
285
diff --git a/trace-events b/trace-events
286
index XXXXXXX..XXXXXXX 100644
287
--- a/trace-events
288
+++ b/trace-events
289
@@ -XXX,XX +XXX,XX @@ job_state_transition(void *job, int ret, const char *legal, const char *s0, con
290
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
291
job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
292
293
+# job-qmp.c
294
+qmp_job_cancel(void *job) "job %p"
295
+qmp_job_pause(void *job) "job %p"
296
+qmp_job_resume(void *job) "job %p"
297
+qmp_job_complete(void *job) "job %p"
298
+qmp_job_finalize(void *job) "job %p"
299
+qmp_job_dismiss(void *job) "job %p"
300
+
301
+
302
### Guest events, keep at bottom
303
304
305
--
43
--
306
2.13.6
44
2.19.1
307
45
308
46
diff view generated by jsdifflib
1
Move the defer_to_main_loop functionality from BlockJob to Job.
1
Don't leak 'cluster' in the mapping == NULL case. Found by Coverity
2
(CID 1055918).
2
3
3
The code can be simplified because we can use job->aio_context in
4
Fixes: 8d9401c2791ee2d2805b741b1ee3006041edcd3e
4
job_defer_to_main_loop_bh() now, instead of having to access the
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
BlockDriverState.
6
Reviewed-by: Philippe Mathieu-Daudé <philmd@redhat.com>
7
Reviewed-by: Liam Merwick <liam.merwick@oracle.com>
8
Tested-by: Philippe Mathieu-Daudé <philmd@redhat.com>
9
---
10
block/vvfat.c | 6 +++---
11
1 file changed, 3 insertions(+), 3 deletions(-)
6
12
7
Probably taking the data->aio_context lock in addition was already
13
diff --git a/block/vvfat.c b/block/vvfat.c
8
unnecessary in the old code because we didn't actually make use of
9
anything protected by the old AioContext except getting the new
10
AioContext, in case it changed between scheduling the BH and running it.
11
But it's certainly unnecessary now that the BDS isn't accessed at all
12
any more.
13
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Reviewed-by: John Snow <jsnow@redhat.com>
17
---
18
include/block/blockjob.h | 5 ----
19
include/block/blockjob_int.h | 19 ---------------
20
include/qemu/job.h | 20 ++++++++++++++++
21
block/backup.c | 7 +++---
22
block/commit.c | 11 +++++----
23
block/mirror.c | 15 ++++++------
24
block/stream.c | 14 +++++------
25
blockjob.c | 57 ++++----------------------------------------
26
job.c | 32 +++++++++++++++++++++++++
27
tests/test-bdrv-drain.c | 7 +++---
28
tests/test-blockjob-txn.c | 13 +++++-----
29
tests/test-blockjob.c | 7 +++---
30
12 files changed, 97 insertions(+), 110 deletions(-)
31
32
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
33
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX 100644
34
--- a/include/block/blockjob.h
15
--- a/block/vvfat.c
35
+++ b/include/block/blockjob.h
16
+++ b/block/vvfat.c
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
17
@@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s,
37
*/
18
uint32_t first_cluster = c;
38
bool ready;
19
mapping_t* mapping = find_mapping_for_cluster(s, c);
39
20
uint32_t size = filesize_of_direntry(direntry);
40
- /**
21
- char* cluster = g_malloc(s->cluster_size);
41
- * Set to true when the job has deferred work to the main loop.
22
+ char *cluster;
42
- */
23
uint32_t i;
43
- bool deferred_to_main_loop;
24
int fd = 0;
44
-
25
45
/** Status that is published by the query-block-jobs QMP API */
26
@@ -XXX,XX +XXX,XX @@ static int commit_one_file(BDRVVVFATState* s,
46
BlockDeviceIoStatus iostatus;
27
if (fd < 0) {
47
28
fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path,
48
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
29
strerror(errno), errno);
49
index XXXXXXX..XXXXXXX 100644
30
- g_free(cluster);
50
--- a/include/block/blockjob_int.h
31
return fd;
51
+++ b/include/block/blockjob_int.h
52
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job);
53
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
54
int is_read, int error);
55
56
-typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
57
-
58
-/**
59
- * block_job_defer_to_main_loop:
60
- * @job: The job
61
- * @fn: The function to run in the main loop
62
- * @opaque: The opaque value that is passed to @fn
63
- *
64
- * This function must be called by the main job coroutine just before it
65
- * returns. @fn is executed in the main loop with the BlockDriverState
66
- * AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
67
- * anything that uses bdrv_drain_all() in the main loop.
68
- *
69
- * The @job AioContext is held while @fn executes.
70
- */
71
-void block_job_defer_to_main_loop(BlockJob *job,
72
- BlockJobDeferToMainLoopFn *fn,
73
- void *opaque);
74
-
75
#endif
76
diff --git a/include/qemu/job.h b/include/qemu/job.h
77
index XXXXXXX..XXXXXXX 100644
78
--- a/include/qemu/job.h
79
+++ b/include/qemu/job.h
80
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
81
*/
82
bool cancelled;
83
84
+ /** Set to true when the job has deferred work to the main loop. */
85
+ bool deferred_to_main_loop;
86
+
87
/** Element of the list of jobs */
88
QLIST_ENTRY(Job) job_list;
89
} Job;
90
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id);
91
*/
92
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
93
94
+typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
95
+
96
+/**
97
+ * @job: The job
98
+ * @fn: The function to run in the main loop
99
+ * @opaque: The opaque value that is passed to @fn
100
+ *
101
+ * This function must be called by the main job coroutine just before it
102
+ * returns. @fn is executed in the main loop with the job AioContext acquired.
103
+ *
104
+ * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses
105
+ * bdrv_drain_all() in the main loop.
106
+ *
107
+ * The @job AioContext is held while @fn executes.
108
+ */
109
+void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
110
+
111
/* TODO To be removed from the public interface */
112
void job_state_transition(Job *job, JobStatus s1);
113
114
diff --git a/block/backup.c b/block/backup.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/block/backup.c
117
+++ b/block/backup.c
118
@@ -XXX,XX +XXX,XX @@ typedef struct {
119
int ret;
120
} BackupCompleteData;
121
122
-static void backup_complete(BlockJob *job, void *opaque)
123
+static void backup_complete(Job *job, void *opaque)
124
{
125
+ BlockJob *bjob = container_of(job, BlockJob, job);
126
BackupCompleteData *data = opaque;
127
128
- block_job_completed(job, data->ret);
129
+ block_job_completed(bjob, data->ret);
130
g_free(data);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
134
135
data = g_malloc(sizeof(*data));
136
data->ret = ret;
137
- block_job_defer_to_main_loop(&job->common, backup_complete, data);
138
+ job_defer_to_main_loop(&job->common.job, backup_complete, data);
139
}
140
141
static const BlockJobDriver backup_job_driver = {
142
diff --git a/block/commit.c b/block/commit.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/commit.c
145
+++ b/block/commit.c
146
@@ -XXX,XX +XXX,XX @@ typedef struct {
147
int ret;
148
} CommitCompleteData;
149
150
-static void commit_complete(BlockJob *job, void *opaque)
151
+static void commit_complete(Job *job, void *opaque)
152
{
153
- CommitBlockJob *s = container_of(job, CommitBlockJob, common);
154
+ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
155
+ BlockJob *bjob = &s->common;
156
CommitCompleteData *data = opaque;
157
BlockDriverState *top = blk_bs(s->top);
158
BlockDriverState *base = blk_bs(s->base);
159
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
160
* the normal backing chain can be restored. */
161
blk_unref(s->base);
162
163
- if (!job_is_cancelled(&s->common.job) && ret == 0) {
164
+ if (!job_is_cancelled(job) && ret == 0) {
165
/* success */
166
ret = bdrv_drop_intermediate(s->commit_top_bs, base,
167
s->backing_file_str);
168
@@ -XXX,XX +XXX,XX @@ static void commit_complete(BlockJob *job, void *opaque)
169
* block_job_finish_sync()), block_job_completed() won't free it and
170
* therefore the blockers on the intermediate nodes remain. This would
171
* cause bdrv_set_backing_hd() to fail. */
172
- block_job_remove_all_bdrv(job);
173
+ block_job_remove_all_bdrv(bjob);
174
175
block_job_completed(&s->common, ret);
176
g_free(data);
177
@@ -XXX,XX +XXX,XX @@ out:
178
179
data = g_malloc(sizeof(*data));
180
data->ret = ret;
181
- block_job_defer_to_main_loop(&s->common, commit_complete, data);
182
+ job_defer_to_main_loop(&s->common.job, commit_complete, data);
183
}
184
185
static const BlockJobDriver commit_job_driver = {
186
diff --git a/block/mirror.c b/block/mirror.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/block/mirror.c
189
+++ b/block/mirror.c
190
@@ -XXX,XX +XXX,XX @@ typedef struct {
191
int ret;
192
} MirrorExitData;
193
194
-static void mirror_exit(BlockJob *job, void *opaque)
195
+static void mirror_exit(Job *job, void *opaque)
196
{
197
- MirrorBlockJob *s = container_of(job, MirrorBlockJob, common);
198
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
199
+ BlockJob *bjob = &s->common;
200
MirrorExitData *data = opaque;
201
AioContext *replace_aio_context = NULL;
202
BlockDriverState *src = s->source;
203
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
204
* the blockers on the intermediate nodes so that the resulting state is
205
* valid. Also give up permissions on mirror_top_bs->backing, which might
206
* block the removal. */
207
- block_job_remove_all_bdrv(job);
208
+ block_job_remove_all_bdrv(bjob);
209
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
210
&error_abort);
211
bdrv_replace_node(mirror_top_bs, backing_bs(mirror_top_bs), &error_abort);
212
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(BlockJob *job, void *opaque)
213
/* We just changed the BDS the job BB refers to (with either or both of the
214
* bdrv_replace_node() calls), so switch the BB back so the cleanup does
215
* the right thing. We don't need any permissions any more now. */
216
- blk_remove_bs(job->blk);
217
- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
218
- blk_insert_bs(job->blk, mirror_top_bs, &error_abort);
219
+ blk_remove_bs(bjob->blk);
220
+ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
221
+ blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
222
223
block_job_completed(&s->common, data->ret);
224
225
@@ -XXX,XX +XXX,XX @@ immediate_exit:
226
if (need_drain) {
227
bdrv_drained_begin(bs);
228
}
32
}
229
- block_job_defer_to_main_loop(&s->common, mirror_exit, data);
33
if (offset > 0) {
230
+ job_defer_to_main_loop(&s->common.job, mirror_exit, data);
34
if (lseek(fd, offset, SEEK_SET) != offset) {
231
}
35
qemu_close(fd);
232
36
- g_free(cluster);
233
static void mirror_complete(BlockJob *job, Error **errp)
37
return -3;
234
diff --git a/block/stream.c b/block/stream.c
235
index XXXXXXX..XXXXXXX 100644
236
--- a/block/stream.c
237
+++ b/block/stream.c
238
@@ -XXX,XX +XXX,XX @@ typedef struct {
239
int ret;
240
} StreamCompleteData;
241
242
-static void stream_complete(BlockJob *job, void *opaque)
243
+static void stream_complete(Job *job, void *opaque)
244
{
245
- StreamBlockJob *s = container_of(job, StreamBlockJob, common);
246
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
247
+ BlockJob *bjob = &s->common;
248
StreamCompleteData *data = opaque;
249
- BlockDriverState *bs = blk_bs(job->blk);
250
+ BlockDriverState *bs = blk_bs(bjob->blk);
251
BlockDriverState *base = s->base;
252
Error *local_err = NULL;
253
254
- if (!job_is_cancelled(&s->common.job) && bs->backing &&
255
- data->ret == 0) {
256
+ if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
257
const char *base_id = NULL, *base_fmt = NULL;
258
if (base) {
259
base_id = s->backing_file_str;
260
@@ -XXX,XX +XXX,XX @@ out:
261
/* Reopen the image back in read-only mode if necessary */
262
if (s->bs_flags != bdrv_get_flags(bs)) {
263
/* Give up write permissions before making it read-only */
264
- blk_set_perm(job->blk, 0, BLK_PERM_ALL, &error_abort);
265
+ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort);
266
bdrv_reopen(bs, s->bs_flags, NULL);
267
}
268
269
@@ -XXX,XX +XXX,XX @@ out:
270
/* Modify backing chain and close BDSes in main loop */
271
data = g_malloc(sizeof(*data));
272
data->ret = ret;
273
- block_job_defer_to_main_loop(&s->common, stream_complete, data);
274
+ job_defer_to_main_loop(&s->common.job, stream_complete, data);
275
}
276
277
static const BlockJobDriver stream_job_driver = {
278
diff --git a/blockjob.c b/blockjob.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/blockjob.c
281
+++ b/blockjob.c
282
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
283
job->completed = true;
284
job->busy = false;
285
job->paused = false;
286
- job->deferred_to_main_loop = true;
287
+ job->job.deferred_to_main_loop = true;
288
block_job_txn_del_job(job);
289
job_state_transition(&job->job, JOB_STATUS_NULL);
290
job_unref(&job->job);
291
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
292
/* block_job_drain calls block_job_enter, and it should be enough to
293
* induce progress until the job completes or moves to the main thread.
294
*/
295
- while (!job->deferred_to_main_loop && !job->completed) {
296
+ while (!job->job.deferred_to_main_loop && !job->completed) {
297
block_job_drain(job);
298
}
299
while (!job->completed) {
300
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
301
block_job_cancel_async(job, force);
302
if (!block_job_started(job)) {
303
block_job_completed(job, -ECANCELED);
304
- } else if (job->deferred_to_main_loop) {
305
+ } else if (job->job.deferred_to_main_loop) {
306
block_job_completed_txn_abort(job);
307
} else {
308
block_job_enter(job);
309
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
310
if (!block_job_started(job)) {
311
return;
312
}
313
- if (job->deferred_to_main_loop) {
314
+ if (job->job.deferred_to_main_loop) {
315
return;
316
}
317
318
@@ -XXX,XX +XXX,XX @@ static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job))
319
return;
320
}
321
322
- assert(!job->deferred_to_main_loop);
323
+ assert(!job->job.deferred_to_main_loop);
324
timer_del(&job->sleep_timer);
325
job->busy = true;
326
block_job_unlock();
327
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
328
}
329
return action;
330
}
331
-
332
-typedef struct {
333
- BlockJob *job;
334
- AioContext *aio_context;
335
- BlockJobDeferToMainLoopFn *fn;
336
- void *opaque;
337
-} BlockJobDeferToMainLoopData;
338
-
339
-static void block_job_defer_to_main_loop_bh(void *opaque)
340
-{
341
- BlockJobDeferToMainLoopData *data = opaque;
342
- AioContext *aio_context;
343
-
344
- /* Prevent race with block_job_defer_to_main_loop() */
345
- aio_context_acquire(data->aio_context);
346
-
347
- /* Fetch BDS AioContext again, in case it has changed */
348
- aio_context = blk_get_aio_context(data->job->blk);
349
- if (aio_context != data->aio_context) {
350
- aio_context_acquire(aio_context);
351
- }
352
-
353
- data->fn(data->job, data->opaque);
354
-
355
- if (aio_context != data->aio_context) {
356
- aio_context_release(aio_context);
357
- }
358
-
359
- aio_context_release(data->aio_context);
360
-
361
- g_free(data);
362
-}
363
-
364
-void block_job_defer_to_main_loop(BlockJob *job,
365
- BlockJobDeferToMainLoopFn *fn,
366
- void *opaque)
367
-{
368
- BlockJobDeferToMainLoopData *data = g_malloc(sizeof(*data));
369
- data->job = job;
370
- data->aio_context = blk_get_aio_context(job->blk);
371
- data->fn = fn;
372
- data->opaque = opaque;
373
- job->deferred_to_main_loop = true;
374
-
375
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
376
- block_job_defer_to_main_loop_bh, data);
377
-}
378
diff --git a/job.c b/job.c
379
index XXXXXXX..XXXXXXX 100644
380
--- a/job.c
381
+++ b/job.c
382
@@ -XXX,XX +XXX,XX @@
383
#include "qapi/error.h"
384
#include "qemu/job.h"
385
#include "qemu/id.h"
386
+#include "qemu/main-loop.h"
387
#include "trace-root.h"
388
389
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
390
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
391
g_free(job);
392
}
393
}
394
+
395
+typedef struct {
396
+ Job *job;
397
+ JobDeferToMainLoopFn *fn;
398
+ void *opaque;
399
+} JobDeferToMainLoopData;
400
+
401
+static void job_defer_to_main_loop_bh(void *opaque)
402
+{
403
+ JobDeferToMainLoopData *data = opaque;
404
+ Job *job = data->job;
405
+ AioContext *aio_context = job->aio_context;
406
+
407
+ aio_context_acquire(aio_context);
408
+ data->fn(data->job, data->opaque);
409
+ aio_context_release(aio_context);
410
+
411
+ g_free(data);
412
+}
413
+
414
+void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque)
415
+{
416
+ JobDeferToMainLoopData *data = g_malloc(sizeof(*data));
417
+ data->job = job;
418
+ data->fn = fn;
419
+ data->opaque = opaque;
420
+ job->deferred_to_main_loop = true;
421
+
422
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
423
+ job_defer_to_main_loop_bh, data);
424
+}
425
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
426
index XXXXXXX..XXXXXXX 100644
427
--- a/tests/test-bdrv-drain.c
428
+++ b/tests/test-bdrv-drain.c
429
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
430
bool should_complete;
431
} TestBlockJob;
432
433
-static void test_job_completed(BlockJob *job, void *opaque)
434
+static void test_job_completed(Job *job, void *opaque)
435
{
436
- block_job_completed(job, 0);
437
+ BlockJob *bjob = container_of(job, BlockJob, job);
438
+ block_job_completed(bjob, 0);
439
}
440
441
static void coroutine_fn test_job_start(void *opaque)
442
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
443
block_job_sleep_ns(&s->common, 100000);
444
}
445
446
- block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
447
+ job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
448
}
449
450
static void test_job_complete(BlockJob *job, Error **errp)
451
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
452
index XXXXXXX..XXXXXXX 100644
453
--- a/tests/test-blockjob-txn.c
454
+++ b/tests/test-blockjob-txn.c
455
@@ -XXX,XX +XXX,XX @@ typedef struct {
456
int *result;
457
} TestBlockJob;
458
459
-static void test_block_job_complete(BlockJob *job, void *opaque)
460
+static void test_block_job_complete(Job *job, void *opaque)
461
{
462
- BlockDriverState *bs = blk_bs(job->blk);
463
+ BlockJob *bjob = container_of(job, BlockJob, job);
464
+ BlockDriverState *bs = blk_bs(bjob->blk);
465
int rc = (intptr_t)opaque;
466
467
- if (job_is_cancelled(&job->job)) {
468
+ if (job_is_cancelled(job)) {
469
rc = -ECANCELED;
470
}
471
472
- block_job_completed(job, rc);
473
+ block_job_completed(bjob, rc);
474
bdrv_unref(bs);
475
}
476
477
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
478
}
38
}
479
}
39
}
480
40
481
- block_job_defer_to_main_loop(job, test_block_job_complete,
41
+ cluster = g_malloc(s->cluster_size);
482
- (void *)(intptr_t)s->rc);
42
+
483
+ job_defer_to_main_loop(&job->job, test_block_job_complete,
43
while (offset < size) {
484
+ (void *)(intptr_t)s->rc);
44
uint32_t c1;
485
}
45
int rest_size = (size - offset > s->cluster_size ?
486
487
typedef struct {
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
489
index XXXXXXX..XXXXXXX 100644
490
--- a/tests/test-blockjob.c
491
+++ b/tests/test-blockjob.c
492
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
493
bool completed;
494
} CancelJob;
495
496
-static void cancel_job_completed(BlockJob *job, void *opaque)
497
+static void cancel_job_completed(Job *job, void *opaque)
498
{
499
+ BlockJob *bjob = container_of(job, BlockJob, job);
500
CancelJob *s = opaque;
501
s->completed = true;
502
- block_job_completed(job, 0);
503
+ block_job_completed(bjob, 0);
504
}
505
506
static void cancel_job_complete(BlockJob *job, Error **errp)
507
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
508
}
509
510
defer:
511
- block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
512
+ job_defer_to_main_loop(&s->common.job, cancel_job_completed, s);
513
}
514
515
static const BlockJobDriver test_cancel_driver = {
516
--
46
--
517
2.13.6
47
2.19.1
518
48
519
49
diff view generated by jsdifflib
1
From: Peter Maydell <peter.maydell@linaro.org>
1
From: Eric Blake <eblake@redhat.com>
2
2
3
In commit 8b9ad56e9cbfd852a, we removed the code that could result
3
Although off_t permits up to 63 bits (8EB) of file offsets, in
4
in our getting to sd_prealloc()'s out_with_err_set label with a
4
practice, we're going to hit other limits first. Document some
5
NULL blk pointer. That makes the NULL check in the error-handling
5
of those limits in the qcow2 spec (some are inherent, others are
6
path unnecessary, and Coverity gripes about it (CID 1390636).
6
implementation choices of qemu), and how choice of cluster size
7
Delete the redundant check.
7
can influence some of the limits.
8
8
9
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
9
While we cannot map any uncompressed virtual cluster to any
10
address higher than 64 PB (56 bits) (due to the current L1/L2
11
field encoding stopping at bit 55), qemu's cap of 8M for the
12
refcount table can still access larger host addresses for some
13
combinations of large clusters and small refcount_order. For
14
comparison, ext4 with 4k blocks caps files at 16PB.
15
16
Another interesting limit: for compressed clusters, the L2 layout
17
requires an ever-smaller maximum host offset as cluster size gets
18
larger, down to a 512 TB maximum with 2M clusters. In particular,
19
note that with a cluster size of 8k or smaller, the L2 entry for
20
a compressed cluster could technically point beyond the 64PB mark,
21
but when you consider that with 8k clusters and refcount_order = 0,
22
you cannot access beyond 512T without exceeding qemu's limit of an
23
8M cap on the refcount table, it is unlikely that any image in the
24
wild has attempted to do so. To be safe, let's document that bits
25
beyond 55 in a compressed cluster must be 0.
26
27
Signed-off-by: Eric Blake <eblake@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
28
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
---
29
---
12
block/sheepdog.c | 4 +---
30
docs/interop/qcow2.txt | 38 ++++++++++++++++++++++++++++++++++++--
13
1 file changed, 1 insertion(+), 3 deletions(-)
31
1 file changed, 36 insertions(+), 2 deletions(-)
14
32
15
diff --git a/block/sheepdog.c b/block/sheepdog.c
33
diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt
16
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
17
--- a/block/sheepdog.c
35
--- a/docs/interop/qcow2.txt
18
+++ b/block/sheepdog.c
36
+++ b/docs/interop/qcow2.txt
19
@@ -XXX,XX +XXX,XX @@ out:
37
@@ -XXX,XX +XXX,XX @@ The first cluster of a qcow2 image contains the file header:
20
error_setg_errno(errp, -ret, "Can't pre-allocate");
38
with larger cluster sizes.
21
}
39
22
out_with_err_set:
40
24 - 31: size
23
- if (blk) {
41
- Virtual disk size in bytes
24
- blk_unref(blk);
42
+ Virtual disk size in bytes.
25
- }
43
+
26
+ blk_unref(blk);
44
+ Note: qemu has an implementation limit of 32 MB as
27
g_free(buf);
45
+ the maximum L1 table size. With a 2 MB cluster
28
46
+ size, it is unable to populate a virtual cluster
29
return ret;
47
+ beyond 2 EB (61 bits); with a 512 byte cluster
48
+ size, it is unable to populate a virtual size
49
+ larger than 128 GB (37 bits). Meanwhile, L1/L2
50
+ table layouts limit an image to no more than 64 PB
51
+ (56 bits) of populated clusters, and an image may
52
+ hit other limits first (such as a file system's
53
+ maximum size).
54
55
32 - 35: crypt_method
56
0 for no encryption
57
@@ -XXX,XX +XXX,XX @@ in the image file.
58
It contains pointers to the second level structures which are called refcount
59
blocks and are exactly one cluster in size.
60
61
+Although a large enough refcount table can reserve clusters past 64 PB
62
+(56 bits) (assuming the underlying protocol can even be sized that
63
+large), note that some qcow2 metadata such as L1/L2 tables must point
64
+to clusters prior to that point.
65
+
66
+Note: qemu has an implementation limit of 8 MB as the maximum refcount
67
+table size. With a 2 MB cluster size and a default refcount_order of
68
+4, it is unable to reference host resources beyond 2 EB (61 bits); in
69
+the worst case, with a 512 cluster size and refcount_order of 6, it is
70
+unable to access beyond 32 GB (35 bits).
71
+
72
Given an offset into the image file, the refcount of its cluster can be
73
obtained as follows:
74
75
@@ -XXX,XX +XXX,XX @@ The L1 table has a variable size (stored in the header) and may use multiple
76
clusters, however it must be contiguous in the image file. L2 tables are
77
exactly one cluster in size.
78
79
+The L1 and L2 tables have implications on the maximum virtual file
80
+size; for a given L1 table size, a larger cluster size is required for
81
+the guest to have access to more space. Furthermore, a virtual
82
+cluster must currently map to a host offset below 64 PB (56 bits)
83
+(although this limit could be relaxed by putting reserved bits into
84
+use). Additionally, as cluster size increases, the maximum host
85
+offset for a compressed cluster is reduced (a 2M cluster size requires
86
+compressed clusters to reside below 512 TB (49 bits), and this limit
87
+cannot be relaxed without an incompatible layout change).
88
+
89
Given an offset into the virtual disk, the offset into the image file can be
90
obtained as follows:
91
92
@@ -XXX,XX +XXX,XX @@ Standard Cluster Descriptor:
93
Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)):
94
95
Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a
96
- cluster or sector boundary!
97
+ cluster or sector boundary! If cluster_bits is
98
+ small enough that this field includes bits beyond
99
+ 55, those upper bits must be set to 0.
100
101
x - 61: Number of additional 512-byte sectors used for the
102
compressed data, beyond the sector containing the offset
30
--
103
--
31
2.13.6
104
2.19.1
32
105
33
106
diff view generated by jsdifflib
1
BlockJob.driver is redundant with Job.driver and only used in very few
1
From: Eric Blake <eblake@redhat.com>
2
places any more. Remove it.
3
2
3
Our code was already checking that we did not attempt to
4
allocate more clusters than what would fit in an INT64 (the
5
physical maximimum if we can access a full off_t's worth of
6
data). But this does not catch smaller limits enforced by
7
various spots in the qcow2 image description: L1 and normal
8
clusters of L2 are documented as having bits 63-56 reserved
9
for other purposes, capping our maximum offset at 64PB (bit
10
55 is the maximum bit set). And for compressed images with
11
2M clusters, the cap drops the maximum offset to bit 48, or
12
a maximum offset of 512TB. If we overflow that offset, we
13
would write compressed data into one place, but try to
14
decompress from another, which won't work.
15
16
It's actually possible to prove that overflow can cause image
17
corruption without this patch; I'll add the iotests separately
18
in the next commit.
19
20
Signed-off-by: Eric Blake <eblake@redhat.com>
21
Reviewed-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
23
---
6
include/block/blockjob.h | 3 ---
24
block/qcow2.h | 6 ++++++
7
blockjob.c | 17 ++++++++++-------
25
block/qcow2-refcount.c | 20 +++++++++++++-------
8
2 files changed, 10 insertions(+), 10 deletions(-)
26
2 files changed, 19 insertions(+), 7 deletions(-)
9
27
10
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
28
diff --git a/block/qcow2.h b/block/qcow2.h
11
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
12
--- a/include/block/blockjob.h
30
--- a/block/qcow2.h
13
+++ b/include/block/blockjob.h
31
+++ b/block/qcow2.h
14
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
32
@@ -XXX,XX +XXX,XX @@
15
/** Data belonging to the generic Job infrastructure */
33
#define QCOW_MAX_CRYPT_CLUSTERS 32
16
Job job;
34
#define QCOW_MAX_SNAPSHOTS 65536
17
35
18
- /** The job type, including the job vtable. */
36
+/* Field widths in qcow2 mean normal cluster offsets cannot reach
19
- const BlockJobDriver *driver;
37
+ * 64PB; depending on cluster size, compressed clusters can have a
20
-
38
+ * smaller limit (64PB for up to 16k clusters, then ramps down to
21
/** The block device on which the job is operating. */
39
+ * 512TB for 2M clusters). */
22
BlockBackend *blk;
40
+#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1)
23
41
+
24
diff --git a/blockjob.c b/blockjob.c
42
/* 8 MB refcount table is enough for 2 PB images at 64k cluster size
43
* (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */
44
#define QCOW_MAX_REFTABLE_SIZE S_8MiB
45
diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c
25
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
26
--- a/blockjob.c
47
--- a/block/qcow2-refcount.c
27
+++ b/blockjob.c
48
+++ b/block/qcow2-refcount.c
28
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
49
@@ -XXX,XX +XXX,XX @@
29
void *opaque)
50
#include "qemu/bswap.h"
51
#include "qemu/cutils.h"
52
53
-static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size);
54
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
55
+ uint64_t max);
56
static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs,
57
int64_t offset, int64_t length, uint64_t addend,
58
bool decrease, enum qcow2_discard_type type);
59
@@ -XXX,XX +XXX,XX @@ static int alloc_refcount_block(BlockDriverState *bs,
60
}
61
62
/* Allocate the refcount block itself and mark it as used */
63
- int64_t new_block = alloc_clusters_noref(bs, s->cluster_size);
64
+ int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX);
65
if (new_block < 0) {
66
return new_block;
67
}
68
@@ -XXX,XX +XXX,XX @@ int qcow2_update_cluster_refcount(BlockDriverState *bs,
69
70
71
/* return < 0 if error */
72
-static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size)
73
+static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size,
74
+ uint64_t max)
30
{
75
{
31
BlockJob *job = opaque;
76
BDRVQcow2State *s = bs->opaque;
32
+ const JobDriver *drv = job->job.driver;
77
uint64_t i, nb_clusters, refcount;
33
+ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver);
78
@@ -XXX,XX +XXX,XX @@ retry:
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
}
79
}
41
80
42
job_resume(&job->job);
81
/* Make sure that all offsets in the "allocated" range are representable
43
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
82
- * in an int64_t */
44
void block_job_drain(Job *job)
83
+ * in the requested max */
45
{
84
if (s->free_cluster_index > 0 &&
46
BlockJob *bjob = container_of(job, BlockJob, job);
85
- s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits))
47
+ const JobDriver *drv = job->driver;
86
+ s->free_cluster_index - 1 > (max >> s->cluster_bits))
48
+ BlockJobDriver *bjdrv = container_of(drv, BlockJobDriver, job_driver);
87
{
49
88
return -EFBIG;
50
blk_drain(bjob->blk);
51
- if (bjob->driver->drain) {
52
- bjob->driver->drain(bjob);
53
+ if (bjdrv->drain) {
54
+ bjdrv->drain(bjob);
55
}
89
}
56
}
90
@@ -XXX,XX +XXX,XX @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size)
57
91
58
@@ -XXX,XX +XXX,XX @@ bool block_job_is_internal(BlockJob *job)
92
BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC);
59
93
do {
60
const BlockJobDriver *block_job_driver(BlockJob *job)
94
- offset = alloc_clusters_noref(bs, size);
61
{
95
+ offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET);
62
- return job->driver;
96
if (offset < 0) {
63
+ return container_of(job->job.driver, BlockJobDriver, job_driver);
97
return offset;
64
}
98
}
65
99
@@ -XXX,XX +XXX,XX @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size)
66
/* Assumes the job_mutex is held */
100
free_in_cluster = s->cluster_size - offset_into_cluster(s, offset);
67
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
101
do {
68
assert(job->job.driver->user_resume == &block_job_user_resume);
102
if (!offset || free_in_cluster < size) {
69
assert(job->job.driver->drain == &block_job_drain);
103
- int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size);
70
104
+ int64_t new_cluster;
71
- job->driver = driver;
105
+
72
- job->blk = blk;
106
+ new_cluster = alloc_clusters_noref(bs, s->cluster_size,
73
+ job->blk = blk;
107
+ MIN(s->cluster_offset_mask,
74
108
+ QCOW_MAX_CLUSTER_OFFSET));
75
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
109
if (new_cluster < 0) {
76
job->finalize_completed_notifier.notify = block_job_event_completed;
110
return new_cluster;
111
}
77
--
112
--
78
2.13.6
113
2.19.1
79
114
80
115
diff view generated by jsdifflib
1
This adds a test case that tests the new job-* QMP commands with
1
From: Eric Blake <eblake@redhat.com>
2
mirror and backup block jobs.
2
3
3
If you have a capable file system (tmpfs is good, ext4 not so much;
4
run ./check with TEST_DIR pointing to a good location so as not
5
to skip the test), it's actually possible to create a qcow2 file
6
that expands to a sparse 512T image with just over 38M of content.
7
The test is not the world's fastest (qemu crawling through 256M
8
bits of refcount table to find the next cluster to allocate takes
9
several seconds, as does qemu-img check reporting millions of
10
leaked clusters); but it DOES catch the problem that the previous
11
patch just fixed where writing a compressed cluster to a full
12
image ended up overwriting the wrong cluster.
13
14
Suggested-by: Max Reitz <mreitz@redhat.com>
15
Signed-off-by: Eric Blake <eblake@redhat.com>
16
Reviewed-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
18
---
6
tests/qemu-iotests/219 | 209 +++++++++++++++++++++++++++++
19
tests/qemu-iotests/220 | 96 ++++++++++++++++++++++++++++++++++++++
7
tests/qemu-iotests/219.out | 327 +++++++++++++++++++++++++++++++++++++++++++++
20
tests/qemu-iotests/220.out | 54 +++++++++++++++++++++
8
tests/qemu-iotests/group | 1 +
21
tests/qemu-iotests/group | 1 +
9
3 files changed, 537 insertions(+)
22
3 files changed, 151 insertions(+)
10
create mode 100755 tests/qemu-iotests/219
23
create mode 100755 tests/qemu-iotests/220
11
create mode 100644 tests/qemu-iotests/219.out
24
create mode 100644 tests/qemu-iotests/220.out
12
25
13
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
26
diff --git a/tests/qemu-iotests/220 b/tests/qemu-iotests/220
14
new file mode 100755
27
new file mode 100755
15
index XXXXXXX..XXXXXXX
28
index XXXXXXX..XXXXXXX
16
--- /dev/null
29
--- /dev/null
17
+++ b/tests/qemu-iotests/219
30
+++ b/tests/qemu-iotests/220
18
@@ -XXX,XX +XXX,XX @@
31
@@ -XXX,XX +XXX,XX @@
19
+#!/usr/bin/env python
32
+#!/bin/bash
33
+#
34
+# max limits on compression in huge qcow2 files
20
+#
35
+#
21
+# Copyright (C) 2018 Red Hat, Inc.
36
+# Copyright (C) 2018 Red Hat, Inc.
22
+#
37
+#
23
+# This program is free software; you can redistribute it and/or modify
38
+# 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
39
+# it under the terms of the GNU General Public License as published by
...
...
31
+# GNU General Public License for more details.
46
+# GNU General Public License for more details.
32
+#
47
+#
33
+# You should have received a copy of the GNU General Public License
48
+# 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/>.
49
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
35
+#
50
+#
36
+# Creator/Owner: Kevin Wolf <kwolf@redhat.com>
51
+
37
+#
52
+seq=$(basename $0)
38
+# Check using the job-* QMP commands with block jobs
53
+echo "QA output created by $seq"
39
+
54
+
40
+import iotests
55
+status=1 # failure is the default!
41
+
56
+
42
+iotests.verify_image_format(supported_fmts=['qcow2'])
57
+_cleanup()
43
+
58
+{
44
+def pause_wait(vm, job_id):
59
+ _cleanup_test_img
45
+ with iotests.Timeout(3, "Timeout waiting for job to pause"):
60
+}
46
+ while True:
61
+trap "_cleanup; exit \$status" 0 1 2 3 15
47
+ result = vm.qmp('query-jobs')
62
+
48
+ for job in result['return']:
63
+# get standard environment, filters and checks
49
+ if job['id'] == job_id and job['status'] in ['paused', 'standby']:
64
+. ./common.rc
50
+ return job
65
+. ./common.filter
51
+
66
+. ./common.pattern
52
+# Test that block-job-pause/resume and job-pause/resume can be mixed
67
+
53
+def test_pause_resume(vm):
68
+_supported_fmt qcow2
54
+ for pause_cmd, pause_arg in [('block-job-pause', 'device'),
69
+_supported_proto file
55
+ ('job-pause', 'id')]:
70
+_supported_os Linux
56
+ for resume_cmd, resume_arg in [('block-job-resume', 'device'),
71
+
57
+ ('job-resume', 'id')]:
72
+echo "== Creating huge file =="
58
+ iotests.log('=== Testing %s/%s ===' % (pause_cmd, resume_cmd))
73
+
59
+
74
+# Sanity check: We require a file system that permits the creation
60
+ iotests.log(vm.qmp(pause_cmd, **{pause_arg: 'job0'}))
75
+# of a HUGE (but very sparse) file. tmpfs works, ext4 does not.
61
+ pause_wait(vm, 'job0')
76
+if ! truncate --size=513T "$TEST_IMG"; then
62
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
77
+ _notrun "file system on $TEST_DIR does not support large enough files"
63
+ iotests.log(vm.qmp('query-jobs'))
78
+fi
64
+
79
+rm "$TEST_IMG"
65
+ iotests.log(vm.qmp(resume_cmd, **{resume_arg: 'job0'}))
80
+IMGOPTS='cluster_size=2M,refcount_bits=1' _make_test_img 513T
66
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
81
+
67
+ iotests.log(vm.qmp('query-jobs'))
82
+echo "== Populating refcounts =="
68
+
83
+# We want an image with 256M refcounts * 2M clusters = 512T referenced.
69
+def test_job_lifecycle(vm, job, job_args, has_ready=False):
84
+# Each 2M cluster holds 16M refcounts; the refcount table initially uses
70
+ iotests.log('')
85
+# 1 refblock, so we need to add 15 more. The refcount table lives at 2M,
71
+ iotests.log('')
86
+# first refblock at 4M, L2 at 6M, so our remaining additions start at 8M.
72
+ iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
87
+# Then, for each refblock, mark it as fully populated.
73
+ (job,
88
+to_hex() {
74
+ job_args.get('auto-finalize', True),
89
+ printf %016x\\n $1 | sed 's/\(..\)/\\x\1/g'
75
+ job_args.get('auto-dismiss', True)))
90
+}
76
+ iotests.log(vm.qmp(job, job_id='job0', **job_args))
91
+truncate --size=38m "$TEST_IMG"
77
+
92
+entry=$((0x200000))
78
+ # Depending on the storage, the first request may or may not have completed
93
+$QEMU_IO_PROG -f raw -c "w -P 0xff 4m 2m" "$TEST_IMG" | _filter_qemu_io
79
+ # yet, so filter out the progress. Later query-job calls don't need the
94
+for i in {1..15}; do
80
+ # filtering because the progress is made deterministic by the block job
95
+ offs=$((0x600000 + i*0x200000))
81
+ # speed
96
+ poke_file "$TEST_IMG" $((i*8 + entry)) $(to_hex $offs)
82
+ result = vm.qmp('query-jobs')
97
+ $QEMU_IO_PROG -f raw -c "w -P 0xff $offs 2m" "$TEST_IMG" | _filter_qemu_io
83
+ for j in result['return']:
98
+done
84
+ del j['current-progress']
99
+
85
+ iotests.log(result)
100
+echo "== Checking file before =="
86
+
101
+# FIXME: 'qemu-img check' doesn't diagnose refcounts beyond the end of
87
+ # undefined -> created -> running
102
+# the file as leaked clusters
88
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
103
+_check_test_img 2>&1 | sed '/^Leaked cluster/d'
89
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
104
+stat -c 'image size %s' "$TEST_IMG"
90
+
105
+
91
+ # RUNNING state:
106
+echo "== Trying to write compressed cluster =="
92
+ # pause/resume should work, complete/finalize/dismiss should error out
107
+# Given our file size, the next available cluster at 512T lies beyond the
93
+ iotests.log('')
108
+# maximum offset that a compressed 2M cluster can reside in
94
+ iotests.log('Pause/resume in RUNNING')
109
+$QEMU_IO_PROG -c 'w -c 0 2m' "$TEST_IMG" | _filter_qemu_io
95
+ test_pause_resume(vm)
110
+# The attempt failed, but ended up allocating a new refblock
96
+
111
+stat -c 'image size %s' "$TEST_IMG"
97
+ iotests.log(vm.qmp('job-complete', id='job0'))
112
+
98
+ iotests.log(vm.qmp('job-finalize', id='job0'))
113
+echo "== Writing normal cluster =="
99
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
114
+# The failed write should not corrupt the image, so a normal write succeeds
100
+
115
+$QEMU_IO_PROG -c 'w 0 2m' "$TEST_IMG" | _filter_qemu_io
101
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
116
+
102
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
117
+echo "== Checking file after =="
103
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
118
+# qemu-img now sees the millions of leaked clusters, thanks to the allocations
104
+
119
+# at 512T. Undo many of our faked references to speed up the check.
105
+ # Let the job complete (or transition to READY if it supports that)
120
+$QEMU_IO_PROG -f raw -c "w -z 5m 1m" -c "w -z 8m 30m" "$TEST_IMG" |
106
+ iotests.log(vm.qmp('block-job-set-speed', device='job0', speed=0))
121
+ _filter_qemu_io
107
+ if has_ready:
122
+_check_test_img 2>&1 | sed '/^Leaked cluster/d'
108
+ iotests.log('')
123
+
109
+ iotests.log('Waiting for READY state...')
124
+# success, all done
110
+ vm.event_wait('BLOCK_JOB_READY')
125
+echo "*** done"
111
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
126
+rm -f $seq.full
112
+ iotests.log(vm.qmp('query-jobs'))
127
+status=0
113
+
128
diff --git a/tests/qemu-iotests/220.out b/tests/qemu-iotests/220.out
114
+ # READY state:
115
+ # pause/resume/complete should work, finalize/dismiss should error out
116
+ iotests.log('')
117
+ iotests.log('Pause/resume in READY')
118
+ test_pause_resume(vm)
119
+
120
+ iotests.log(vm.qmp('job-finalize', id='job0'))
121
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
122
+
123
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
124
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
125
+
126
+ # Transition to WAITING
127
+ iotests.log(vm.qmp('job-complete', id='job0'))
128
+
129
+ # Move to WAITING and PENDING state
130
+ iotests.log('')
131
+ iotests.log('Waiting for PENDING state...')
132
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
133
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
134
+
135
+ if not job_args.get('auto-finalize', True):
136
+ # PENDING state:
137
+ # finalize should work, pause/complete/dismiss should error out
138
+ iotests.log(vm.qmp('query-jobs'))
139
+
140
+ iotests.log(vm.qmp('job-pause', id='job0'))
141
+ iotests.log(vm.qmp('job-complete', id='job0'))
142
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
143
+
144
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
145
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
146
+ iotests.log(vm.qmp('block-job-dismiss', id='job0'))
147
+
148
+ # Transition to CONCLUDED
149
+ iotests.log(vm.qmp('job-finalize', id='job0'))
150
+
151
+
152
+ # Move to CONCLUDED state
153
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
154
+
155
+ if not job_args.get('auto-dismiss', True):
156
+ # CONCLUDED state:
157
+ # dismiss should work, pause/complete/finalize should error out
158
+ iotests.log(vm.qmp('query-jobs'))
159
+
160
+ iotests.log(vm.qmp('job-pause', id='job0'))
161
+ iotests.log(vm.qmp('job-complete', id='job0'))
162
+ iotests.log(vm.qmp('job-finalize', id='job0'))
163
+
164
+ iotests.log(vm.qmp('block-job-pause', device='job0'))
165
+ iotests.log(vm.qmp('block-job-complete', device='job0'))
166
+ iotests.log(vm.qmp('block-job-finalize', id='job0'))
167
+
168
+ # Transition to NULL
169
+ iotests.log(vm.qmp('job-dismiss', id='job0'))
170
+
171
+ # Move to NULL state
172
+ iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
173
+ iotests.log(vm.qmp('query-jobs'))
174
+
175
+
176
+with iotests.FilePath('disk.img') as disk_path, \
177
+ iotests.FilePath('copy.img') as copy_path, \
178
+ iotests.VM() as vm:
179
+
180
+ img_size = '4M'
181
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
182
+ iotests.qemu_io('-c', 'write 0 %s' % (img_size),
183
+ '-f', iotests.imgfmt, disk_path)
184
+
185
+ iotests.log('Launching VM...')
186
+ vm.add_blockdev(vm.qmp_to_opts({
187
+ 'driver': iotests.imgfmt,
188
+ 'node-name': 'drive0-node',
189
+ 'file': {
190
+ 'driver': 'file',
191
+ 'filename': disk_path,
192
+ },
193
+ }))
194
+ vm.launch()
195
+
196
+ # In order to keep things deterministic (especially progress in query-job,
197
+ # but related to this also automatic state transitions like job
198
+ # completion), but still get pause points often enough to avoid making this
199
+ # test very slow, it's important to have the right ratio between speed and
200
+ # buf_size.
201
+ #
202
+ # For backup, buf_size is hard-coded to the source image cluster size (64k),
203
+ # so we'll pick the same for mirror. The slice time, i.e. the granularity
204
+ # of the rate limiting is 100ms. With a speed of 256k per second, we can
205
+ # get four pause points per second. This gives us 250ms per iteration,
206
+ # which should be enough to stay deterministic.
207
+
208
+ test_job_lifecycle(vm, 'drive-mirror', has_ready=True, job_args={
209
+ 'device': 'drive0-node',
210
+ 'target': copy_path,
211
+ 'sync': 'full',
212
+ 'speed': 262144,
213
+ 'buf_size': 65536,
214
+ })
215
+
216
+ for auto_finalize in [True, False]:
217
+ for auto_dismiss in [True, False]:
218
+ test_job_lifecycle(vm, 'drive-backup', job_args={
219
+ 'device': 'drive0-node',
220
+ 'target': copy_path,
221
+ 'sync': 'full',
222
+ 'speed': 262144,
223
+ 'auto-finalize': auto_finalize,
224
+ 'auto-dismiss': auto_dismiss,
225
+ })
226
+
227
+ vm.shutdown()
228
diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out
229
new file mode 100644
129
new file mode 100644
230
index XXXXXXX..XXXXXXX
130
index XXXXXXX..XXXXXXX
231
--- /dev/null
131
--- /dev/null
232
+++ b/tests/qemu-iotests/219.out
132
+++ b/tests/qemu-iotests/220.out
233
@@ -XXX,XX +XXX,XX @@
133
@@ -XXX,XX +XXX,XX @@
234
+Launching VM...
134
+QA output created by 220
235
+
135
+== Creating huge file ==
236
+
136
+Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=564049465049088
237
+Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True)
137
+== Populating refcounts ==
238
+{u'return': {}}
138
+wrote 2097152/2097152 bytes at offset 4194304
239
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
139
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
240
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
140
+wrote 2097152/2097152 bytes at offset 8388608
241
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
141
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
242
+
142
+wrote 2097152/2097152 bytes at offset 10485760
243
+Pause/resume in RUNNING
143
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
244
+=== Testing block-job-pause/block-job-resume ===
144
+wrote 2097152/2097152 bytes at offset 12582912
245
+{u'return': {}}
145
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
246
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
146
+wrote 2097152/2097152 bytes at offset 14680064
247
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
147
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
248
+{u'return': {}}
148
+wrote 2097152/2097152 bytes at offset 16777216
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'}
149
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
250
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
150
+wrote 2097152/2097152 bytes at offset 18874368
251
+=== Testing block-job-pause/job-resume ===
151
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
252
+{u'return': {}}
152
+wrote 2097152/2097152 bytes at offset 20971520
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'}
153
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
254
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
154
+wrote 2097152/2097152 bytes at offset 23068672
255
+{u'return': {}}
155
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
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'}
156
+wrote 2097152/2097152 bytes at offset 25165824
257
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
157
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
258
+=== Testing job-pause/block-job-resume ===
158
+wrote 2097152/2097152 bytes at offset 27262976
259
+{u'return': {}}
159
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
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'}
160
+wrote 2097152/2097152 bytes at offset 29360128
261
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
161
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
262
+{u'return': {}}
162
+wrote 2097152/2097152 bytes at offset 31457280
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'}
163
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
264
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
164
+wrote 2097152/2097152 bytes at offset 33554432
265
+=== Testing job-pause/job-resume ===
165
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
266
+{u'return': {}}
166
+wrote 2097152/2097152 bytes at offset 35651584
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'}
167
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
268
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
168
+wrote 2097152/2097152 bytes at offset 37748736
269
+{u'return': {}}
169
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
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'}
170
+== Checking file before ==
271
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
171
+No errors were found on the image.
272
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
172
+image size 39845888
273
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
173
+== Trying to write compressed cluster ==
274
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
174
+write failed: Input/output error
275
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
175
+image size 562949957615616
276
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
176
+== Writing normal cluster ==
277
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
177
+wrote 2097152/2097152 bytes at offset 0
278
+{u'return': {}}
178
+2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
279
+
179
+== Checking file after ==
280
+Waiting for READY state...
180
+wrote 1048576/1048576 bytes at offset 5242880
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'}
181
+1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
282
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
182
+wrote 31457280/31457280 bytes at offset 8388608
283
+
183
+30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
284
+Pause/resume in READY
184
+
285
+=== Testing block-job-pause/block-job-resume ===
185
+8388589 leaked clusters were found on the image.
286
+{u'return': {}}
186
+This means waste of disk space, but no harm to data.
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'}
187
+*** done
288
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
289
+{u'return': {}}
290
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
291
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
292
+=== Testing block-job-pause/job-resume ===
293
+{u'return': {}}
294
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
295
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
296
+{u'return': {}}
297
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
298
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
299
+=== Testing job-pause/block-job-resume ===
300
+{u'return': {}}
301
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
302
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
303
+{u'return': {}}
304
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
305
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
306
+=== Testing job-pause/job-resume ===
307
+{u'return': {}}
308
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
309
+{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
310
+{u'return': {}}
311
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
312
+{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]}
313
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
314
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
315
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}}
316
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}}
317
+{u'return': {}}
318
+
319
+Waiting for PENDING state...
320
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
321
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
322
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
323
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
324
+{u'return': []}
325
+
326
+
327
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True)
328
+{u'return': {}}
329
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
330
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
331
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
332
+
333
+Pause/resume in RUNNING
334
+=== Testing block-job-pause/block-job-resume ===
335
+{u'return': {}}
336
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
337
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
338
+{u'return': {}}
339
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
340
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
341
+=== Testing block-job-pause/job-resume ===
342
+{u'return': {}}
343
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
344
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
345
+{u'return': {}}
346
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
347
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
348
+=== Testing job-pause/block-job-resume ===
349
+{u'return': {}}
350
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
351
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
352
+{u'return': {}}
353
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
354
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
355
+=== Testing job-pause/job-resume ===
356
+{u'return': {}}
357
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
358
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
359
+{u'return': {}}
360
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
361
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
362
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
363
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
364
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
365
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
366
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
367
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
368
+{u'return': {}}
369
+
370
+Waiting for PENDING state...
371
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
372
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
373
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
374
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
375
+{u'return': []}
376
+
377
+
378
+Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False)
379
+{u'return': {}}
380
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
381
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
382
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
383
+
384
+Pause/resume in RUNNING
385
+=== Testing block-job-pause/block-job-resume ===
386
+{u'return': {}}
387
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
388
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
389
+{u'return': {}}
390
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
391
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
392
+=== Testing block-job-pause/job-resume ===
393
+{u'return': {}}
394
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
395
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
396
+{u'return': {}}
397
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
398
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
399
+=== Testing job-pause/block-job-resume ===
400
+{u'return': {}}
401
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
402
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
403
+{u'return': {}}
404
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
405
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
406
+=== Testing job-pause/job-resume ===
407
+{u'return': {}}
408
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
409
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
410
+{u'return': {}}
411
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
412
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
413
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
414
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
415
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
416
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
417
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
418
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
419
+{u'return': {}}
420
+
421
+Waiting for PENDING state...
422
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
423
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
424
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
425
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
426
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
427
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
428
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
429
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
430
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
431
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
432
+{u'return': {}}
433
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
434
+{u'return': []}
435
+
436
+
437
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True)
438
+{u'return': {}}
439
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
440
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
441
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
442
+
443
+Pause/resume in RUNNING
444
+=== Testing block-job-pause/block-job-resume ===
445
+{u'return': {}}
446
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
447
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
448
+{u'return': {}}
449
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
450
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
451
+=== Testing block-job-pause/job-resume ===
452
+{u'return': {}}
453
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
454
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
455
+{u'return': {}}
456
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
457
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
458
+=== Testing job-pause/block-job-resume ===
459
+{u'return': {}}
460
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
461
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
462
+{u'return': {}}
463
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
464
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
465
+=== Testing job-pause/job-resume ===
466
+{u'return': {}}
467
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
468
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
469
+{u'return': {}}
470
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
471
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
472
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
473
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
474
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
475
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
476
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
477
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
478
+{u'return': {}}
479
+
480
+Waiting for PENDING state...
481
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
482
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
483
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
484
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
485
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
486
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
487
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
488
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
489
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
490
+{u'return': {}}
491
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
492
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
493
+{u'return': []}
494
+
495
+
496
+Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False)
497
+{u'return': {}}
498
+{u'return': [{u'status': u'running', u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
499
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
500
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
501
+
502
+Pause/resume in RUNNING
503
+=== Testing block-job-pause/block-job-resume ===
504
+{u'return': {}}
505
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
506
+{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
507
+{u'return': {}}
508
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
509
+{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
510
+=== Testing block-job-pause/job-resume ===
511
+{u'return': {}}
512
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
513
+{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
514
+{u'return': {}}
515
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
516
+{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
517
+=== Testing job-pause/block-job-resume ===
518
+{u'return': {}}
519
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
520
+{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
521
+{u'return': {}}
522
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
523
+{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
524
+=== Testing job-pause/job-resume ===
525
+{u'return': {}}
526
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
527
+{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
528
+{u'return': {}}
529
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
530
+{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
531
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
532
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
533
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
534
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}}
535
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}}
536
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}}
537
+{u'return': {}}
538
+
539
+Waiting for PENDING state...
540
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
541
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
542
+{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
543
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
544
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
545
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
546
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}}
547
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}}
548
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}}
549
+{u'return': {}}
550
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
551
+{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]}
552
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
553
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
554
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
555
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}}
556
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}}
557
+{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}}
558
+{u'return': {}}
559
+{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'}
560
+{u'return': []}
561
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
188
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
562
index XXXXXXX..XXXXXXX 100644
189
index XXXXXXX..XXXXXXX 100644
563
--- a/tests/qemu-iotests/group
190
--- a/tests/qemu-iotests/group
564
+++ b/tests/qemu-iotests/group
191
+++ b/tests/qemu-iotests/group
565
@@ -XXX,XX +XXX,XX @@
192
@@ -XXX,XX +XXX,XX @@
566
215 rw auto quick
193
217 rw auto quick
567
216 rw auto quick
568
218 rw auto quick
194
218 rw auto quick
569
+219 rw auto
195
219 rw auto
196
+220 rw auto
197
221 rw auto quick
198
222 rw auto quick
199
223 rw auto quick
570
--
200
--
571
2.13.6
201
2.19.1
572
202
573
203
diff view generated by jsdifflib
1
This moves the top-level job completion and cancellation functions from
1
From: Max Reitz <mreitz@redhat.com>
2
BlockJob to Job.
3
2
3
bdrv_reopen_multiple() does not invoke bdrv_reopen_abort() for the
4
element of the reopen queue for which bdrv_reopen_prepare() failed,
5
because it assumes that the prepare function will have rolled back all
6
changes already.
7
8
However, bdrv_reopen_prepare() does not do this in every case: It may
9
notice an error after BlockDriver.bdrv_reopen_prepare() succeeded, and
10
it will not invoke BlockDriver.bdrv_reopen_abort() then; and neither
11
will bdrv_reopen_multiple(), as explained above.
12
13
This is wrong because we must always call .bdrv_reopen_commit() or
14
.bdrv_reopen_abort() after .bdrv_reopen_prepare() has succeeded.
15
Otherwise, the block driver has no chance to undo what it has done in
16
its implementation of .bdrv_reopen_prepare().
17
18
To fix this, bdrv_reopen_prepare() has to call .bdrv_reopen_abort() if
19
it wants to return an error after .bdrv_reopen_prepare() has succeeded.
20
21
Signed-off-by: Max Reitz <mreitz@redhat.com>
22
Reviewed-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
24
---
6
include/block/blockjob.h | 55 -----------------------------
25
block.c | 12 ++++++++++++
7
include/block/blockjob_int.h | 18 ----------
26
1 file changed, 12 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
27
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
28
diff --git a/block.c b/block.c
237
index XXXXXXX..XXXXXXX 100644
29
index XXXXXXX..XXXXXXX 100644
238
--- a/block.c
30
--- a/block.c
239
+++ b/block.c
31
+++ b/block.c
240
@@ -XXX,XX +XXX,XX @@ static void bdrv_close(BlockDriverState *bs)
32
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
241
33
QDict *orig_reopen_opts;
242
void bdrv_close_all(void)
34
char *discard = NULL;
243
{
35
bool read_only;
244
- block_job_cancel_sync_all();
36
+ bool drv_prepared = false;
245
+ /* TODO We do want to cancel all jobs instead of just block jobs on
37
246
+ * shutdown, but bdrv_close_all() isn't the right place any more. */
38
assert(reopen_state != NULL);
247
+ job_cancel_sync_all();
39
assert(reopen_state->bs->drv != NULL);
248
nbd_export_close_all();
40
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
249
41
goto error;
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
}
42
}
311
43
312
s->should_complete = true;
44
+ drv_prepared = true;
313
- block_job_enter(&s->common);
45
+
314
+ job_enter(job);
46
/* Options that are not handled are only okay if they are unchanged
315
}
47
* compared to the old state. It is expected that some options are only
316
48
* used for the initial open, but not reopen (e.g. filename) */
317
static void mirror_pause(Job *job)
49
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue,
318
diff --git a/block/replication.c b/block/replication.c
50
reopen_state->options = qobject_ref(orig_reopen_opts);
319
index XXXXXXX..XXXXXXX 100644
51
320
--- a/block/replication.c
52
error:
321
+++ b/block/replication.c
53
+ if (ret < 0 && drv_prepared) {
322
@@ -XXX,XX +XXX,XX @@ static void replication_close(BlockDriverState *bs)
54
+ /* drv->bdrv_reopen_prepare() has succeeded, so we need to
323
replication_stop(s->rs, false, NULL);
55
+ * call drv->bdrv_reopen_abort() before signaling an error
324
}
56
+ * (bdrv_reopen_multiple() will not call bdrv_reopen_abort()
325
if (s->stage == BLOCK_REPLICATION_FAILOVER) {
57
+ * when the respective bdrv_reopen_prepare() has failed) */
326
- block_job_cancel_sync(s->active_disk->bs->job);
58
+ if (drv->bdrv_reopen_abort) {
327
+ job_cancel_sync(&s->active_disk->bs->job->job);
59
+ drv->bdrv_reopen_abort(reopen_state);
328
}
60
+ }
329
330
if (s->mode == REPLICATION_MODE_SECONDARY) {
331
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
332
* disk, secondary disk in backup_job_completed().
333
*/
334
if (s->secondary_disk->bs->job) {
335
- block_job_cancel_sync(s->secondary_disk->bs->job);
336
+ job_cancel_sync(&s->secondary_disk->bs->job->job);
337
}
338
339
if (!failover) {
340
diff --git a/block/stream.c b/block/stream.c
341
index XXXXXXX..XXXXXXX 100644
342
--- a/block/stream.c
343
+++ b/block/stream.c
344
@@ -XXX,XX +XXX,XX @@ out:
345
}
346
347
g_free(s->backing_file_str);
348
- block_job_completed(&s->common, data->ret);
349
+ job_completed(job, data->ret);
350
g_free(data);
351
}
352
353
diff --git a/blockdev.c b/blockdev.c
354
index XXXXXXX..XXXXXXX 100644
355
--- a/blockdev.c
356
+++ b/blockdev.c
357
@@ -XXX,XX +XXX,XX @@ void blockdev_mark_auto_del(BlockBackend *blk)
358
aio_context_acquire(aio_context);
359
360
if (bs->job) {
361
- block_job_cancel(bs->job, false);
362
+ job_cancel(&bs->job->job, false);
363
}
364
365
aio_context_release(aio_context);
366
@@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(BlkActionState *common)
367
aio_context = bdrv_get_aio_context(state->bs);
368
aio_context_acquire(aio_context);
369
370
- block_job_cancel_sync(state->job);
371
+ job_cancel_sync(&state->job->job);
372
373
aio_context_release(aio_context);
374
}
375
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(BlkActionState *common)
376
aio_context = bdrv_get_aio_context(state->bs);
377
aio_context_acquire(aio_context);
378
379
- block_job_cancel_sync(state->job);
380
+ job_cancel_sync(&state->job->job);
381
382
aio_context_release(aio_context);
383
}
384
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
385
}
386
387
trace_qmp_block_job_cancel(job);
388
- block_job_user_cancel(job, force, errp);
389
+ job_user_cancel(&job->job, force, errp);
390
out:
391
aio_context_release(aio_context);
392
}
393
diff --git a/blockjob.c b/blockjob.c
394
index XXXXXXX..XXXXXXX 100644
395
--- a/blockjob.c
396
+++ b/blockjob.c
397
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
398
*jobptr = NULL;
399
}
400
401
-void block_job_cancel(BlockJob *job, bool force)
402
-{
403
- if (job->job.status == JOB_STATUS_CONCLUDED) {
404
- job_do_dismiss(&job->job);
405
- return;
406
- }
407
- job_cancel_async(&job->job, force);
408
- if (!job_started(&job->job)) {
409
- block_job_completed(job, -ECANCELED);
410
- } else if (job->job.deferred_to_main_loop) {
411
- job_completed_txn_abort(&job->job);
412
- } else {
413
- block_job_enter(job);
414
- }
415
-}
416
-
417
-void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
418
-{
419
- if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
420
- return;
421
- }
422
- block_job_cancel(job, force);
423
-}
424
-
425
-/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
426
- * used with job_finish_sync() without the need for (rather nasty) function
427
- * pointer casts there. */
428
-static void block_job_cancel_err(Job *job, Error **errp)
429
-{
430
- BlockJob *bjob = container_of(job, BlockJob, job);
431
- assert(is_block_job(job));
432
- block_job_cancel(bjob, false);
433
-}
434
-
435
-int block_job_cancel_sync(BlockJob *job)
436
-{
437
- return job_finish_sync(&job->job, &block_job_cancel_err, NULL);
438
-}
439
-
440
-void block_job_cancel_sync_all(void)
441
-{
442
- BlockJob *job;
443
- AioContext *aio_context;
444
-
445
- while ((job = block_job_next(NULL))) {
446
- aio_context = blk_get_aio_context(job->blk);
447
- aio_context_acquire(aio_context);
448
- block_job_cancel_sync(job);
449
- aio_context_release(aio_context);
450
- }
451
-}
452
-
453
-int block_job_complete_sync(BlockJob *job, Error **errp)
454
-{
455
- return job_finish_sync(&job->job, job_complete, errp);
456
-}
457
-
458
void block_job_progress_update(BlockJob *job, uint64_t done)
459
{
460
job->offset += done;
461
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
462
return job;
463
}
464
465
-void block_job_completed(BlockJob *job, int ret)
466
-{
467
- assert(job && job->job.txn && !job_is_completed(&job->job));
468
- assert(blk_bs(job->blk)->job == job);
469
- job->job.ret = ret;
470
- job_update_rc(&job->job);
471
- trace_block_job_completed(job, ret, job->job.ret);
472
- if (job->job.ret) {
473
- job_completed_txn_abort(&job->job);
474
- } else {
475
- job_completed_txn_success(&job->job);
476
- }
477
-}
478
-
479
-void block_job_enter(BlockJob *job)
480
-{
481
- job_enter_cond(&job->job, NULL);
482
-}
483
-
484
void block_job_yield(BlockJob *job)
485
{
486
assert(job->job.busy);
487
diff --git a/job.c b/job.c
488
index XXXXXXX..XXXXXXX 100644
489
--- a/job.c
490
+++ b/job.c
491
@@ -XXX,XX +XXX,XX @@ bool job_is_completed(Job *job)
492
return false;
493
}
494
495
-bool job_started(Job *job)
496
+static bool job_started(Job *job)
497
{
498
return job->co;
499
}
500
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
501
}
502
503
/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
504
- * Reentering the job coroutine with block_job_enter() before the timer has
505
- * expired is allowed and cancels the timer.
506
+ * Reentering the job coroutine with job_enter() before the timer has expired
507
+ * is allowed and cancels the timer.
508
*
509
- * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
510
+ * If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
511
* called explicitly. */
512
void coroutine_fn job_do_yield(Job *job, uint64_t ns)
513
{
514
@@ -XXX,XX +XXX,XX @@ static void job_conclude(Job *job)
515
}
516
}
517
518
-void job_update_rc(Job *job)
519
+static void job_update_rc(Job *job)
520
{
521
if (!job->ret && job_is_cancelled(job)) {
522
job->ret = -ECANCELED;
523
@@ -XXX,XX +XXX,XX @@ static int job_finalize_single(Job *job)
524
return 0;
525
}
526
527
-void job_cancel_async(Job *job, bool force)
528
+static void job_cancel_async(Job *job, bool force)
529
{
530
if (job->user_paused) {
531
/* Do not call job_enter here, the caller will handle it. */
532
@@ -XXX,XX +XXX,XX @@ void job_cancel_async(Job *job, bool force)
533
job->force_cancel |= force;
534
}
535
536
-void job_completed_txn_abort(Job *job)
537
+static void job_completed_txn_abort(Job *job)
538
{
539
AioContext *ctx;
540
JobTxn *txn = job->txn;
541
@@ -XXX,XX +XXX,XX @@ static int job_transition_to_pending(Job *job)
542
return 0;
543
}
544
545
-void job_completed_txn_success(Job *job)
546
+static void job_completed_txn_success(Job *job)
547
{
548
JobTxn *txn = job->txn;
549
Job *other_job;
550
@@ -XXX,XX +XXX,XX @@ void job_completed_txn_success(Job *job)
551
}
552
}
553
554
+void job_completed(Job *job, int ret)
555
+{
556
+ assert(job && job->txn && !job_is_completed(job));
557
+ job->ret = ret;
558
+ job_update_rc(job);
559
+ trace_job_completed(job, ret, job->ret);
560
+ if (job->ret) {
561
+ job_completed_txn_abort(job);
562
+ } else {
563
+ job_completed_txn_success(job);
564
+ }
61
+ }
565
+}
62
qemu_opts_del(opts);
566
+
63
qobject_unref(orig_reopen_opts);
567
+void job_cancel(Job *job, bool force)
64
g_free(discard);
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
--
65
--
825
2.13.6
66
2.19.1
826
67
827
68
diff view generated by jsdifflib
1
The reference output file only works for file. 'qemu-img convert -p'
1
From: Max Reitz <mreitz@redhat.com>
2
makes a lot more progress updates for NFS than for file, so disable the
3
test for NFS.
4
2
3
s->locked_shared_perm is the set of bits locked in the file, which is
4
the inverse of the permissions actually shared. So we need to pass them
5
as they are to raw_apply_lock_bytes() instead of inverting them again.
6
7
Reported-by: Alberto Garcia <berto@igalia.com>
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Alberto Garcia <berto@igalia.com>
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/086 | 2 +-
12
block/file-posix.c | 2 +-
9
1 file changed, 1 insertion(+), 1 deletion(-)
13
1 file changed, 1 insertion(+), 1 deletion(-)
10
14
11
diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086
15
diff --git a/block/file-posix.c b/block/file-posix.c
12
index XXXXXXX..XXXXXXX 100755
16
index XXXXXXX..XXXXXXX 100644
13
--- a/tests/qemu-iotests/086
17
--- a/block/file-posix.c
14
+++ b/tests/qemu-iotests/086
18
+++ b/block/file-posix.c
15
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
19
@@ -XXX,XX +XXX,XX @@ static void raw_reopen_commit(BDRVReopenState *state)
16
. ./common.filter
20
17
21
/* Copy locks to the new fd before closing the old one. */
18
_supported_fmt qcow2 raw
22
raw_apply_lock_bytes(NULL, rs->fd, s->locked_perm,
19
-_supported_proto file nfs
23
- ~s->locked_shared_perm, false, &local_err);
20
+_supported_proto file
24
+ s->locked_shared_perm, false, &local_err);
21
_supported_os Linux
25
if (local_err) {
22
26
/* shouldn't fail in a sane host, but report it just in case. */
23
function run_qemu_img()
27
error_report_err(local_err);
24
--
28
--
25
2.13.6
29
2.19.1
26
30
27
31
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
1
185 and 191 define a MIG_SOCKET even though they don't do anything with
1
From: Max Reitz <mreitz@redhat.com>
2
migration. Remove the useless variable.
3
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Reviewed-by: Alberto Garcia <berto@igalia.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Juan Quintela <quintela@redhat.com>
6
---
6
---
7
tests/qemu-iotests/185 | 2 --
7
tests/qemu-iotests/182 | 71 ++++++++++++++++++++++++++++++++++++++
8
tests/qemu-iotests/191 | 2 --
8
tests/qemu-iotests/182.out | 9 +++++
9
2 files changed, 4 deletions(-)
9
2 files changed, 80 insertions(+)
10
10
11
diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185
11
diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182
12
index XXXXXXX..XXXXXXX 100755
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/185
13
--- a/tests/qemu-iotests/182
14
+++ b/tests/qemu-iotests/185
14
+++ b/tests/qemu-iotests/182
15
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
15
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
16
here=`pwd`
17
status=1 # failure is the default!
18
19
-MIG_SOCKET="${TEST_DIR}/migrate"
20
-
21
_cleanup()
16
_cleanup()
22
{
17
{
23
rm -f "${TEST_IMG}.mid"
18
_cleanup_test_img
24
diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191
19
+ rm -f "$TEST_IMG.overlay"
25
index XXXXXXX..XXXXXXX 100755
20
}
26
--- a/tests/qemu-iotests/191
21
trap "_cleanup; exit \$status" 0 1 2 3 15
27
+++ b/tests/qemu-iotests/191
22
28
@@ -XXX,XX +XXX,XX @@ echo "QA output created by $seq"
23
@@ -XXX,XX +XXX,XX @@ echo 'quit' | $QEMU -nographic -monitor stdio \
29
here=`pwd`
24
30
status=1 # failure is the default!
25
_cleanup_qemu
31
26
32
-MIG_SOCKET="${TEST_DIR}/migrate"
27
+echo
33
-
28
+echo '=== Testing reopen ==='
34
_cleanup()
29
+echo
35
{
30
+
36
rm -f "${TEST_IMG}.mid"
31
+# This tests that reopening does not unshare any permissions it should
32
+# not unshare
33
+# (There was a bug where reopening shared exactly the opposite of the
34
+# permissions it was supposed to share)
35
+
36
+_launch_qemu
37
+
38
+_send_qemu_cmd $QEMU_HANDLE \
39
+ "{'execute': 'qmp_capabilities'}" \
40
+ 'return'
41
+
42
+# Open the image without any format layer (we are not going to access
43
+# it, so that is fine)
44
+# This should keep all permissions shared.
45
+success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \
46
+ "{'execute': 'blockdev-add',
47
+ 'arguments': {
48
+ 'node-name': 'node0',
49
+ 'driver': 'file',
50
+ 'filename': '$TEST_IMG',
51
+ 'locking': 'on'
52
+ } }" \
53
+ 'return' \
54
+ 'error'
55
+
56
+# This snapshot will perform a reopen to drop R/W to RO.
57
+# It should still keep all permissions shared.
58
+success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \
59
+ "{'execute': 'blockdev-snapshot-sync',
60
+ 'arguments': {
61
+ 'node-name': 'node0',
62
+ 'snapshot-file': '$TEST_IMG.overlay',
63
+ 'snapshot-node-name': 'node1'
64
+ } }" \
65
+ 'return' \
66
+ 'error'
67
+
68
+# Now open the same file again
69
+# This does not require any permissions (and does not unshare any), so
70
+# this will not conflict with node0.
71
+success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \
72
+ "{'execute': 'blockdev-add',
73
+ 'arguments': {
74
+ 'node-name': 'node1',
75
+ 'driver': 'file',
76
+ 'filename': '$TEST_IMG',
77
+ 'locking': 'on'
78
+ } }" \
79
+ 'return' \
80
+ 'error'
81
+
82
+# Now we attach the image to a virtio-blk device. This device does
83
+# require some permissions (at least WRITE and READ_CONSISTENT), so if
84
+# reopening node0 unshared any (which it should not have), this will
85
+# fail (but it should not).
86
+success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \
87
+ "{'execute': 'device_add',
88
+ 'arguments': {
89
+ 'driver': 'virtio-blk',
90
+ 'drive': 'node1'
91
+ } }" \
92
+ 'return' \
93
+ 'error'
94
+
95
+_cleanup_qemu
96
+
97
# success, all done
98
echo "*** done"
99
rm -f $seq.full
100
diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out
101
index XXXXXXX..XXXXXXX 100644
102
--- a/tests/qemu-iotests/182.out
103
+++ b/tests/qemu-iotests/182.out
104
@@ -XXX,XX +XXX,XX @@ Starting QEMU
105
Starting a second QEMU using the same image should fail
106
QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock
107
Is another process using the image [TEST_DIR/t.qcow2]?
108
+
109
+=== Testing reopen ===
110
+
111
+{"return": {}}
112
+{"return": {}}
113
+Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16
114
+{"return": {}}
115
+{"return": {}}
116
+{"return": {}}
117
*** done
37
--
118
--
38
2.13.6
119
2.19.1
39
120
40
121
diff view generated by jsdifflib
Deleted patch
1
Commit 0ec4dfb8d changed block-job_pause/resume so that they return an
2
error if they don't do anything because the job is already
3
paused/running. It forgot to update the documentation, so do that now.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
9
qapi/block-core.json | 5 ++---
10
1 file changed, 2 insertions(+), 3 deletions(-)
11
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
13
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
15
+++ b/qapi/block-core.json
16
@@ -XXX,XX +XXX,XX @@
17
#
18
# This command returns immediately after marking the active background block
19
# operation for pausing. It is an error to call this command if no
20
-# operation is in progress. Pausing an already paused job has no cumulative
21
-# effect; a single block-job-resume command will resume the job.
22
+# operation is in progress or if the job is already paused.
23
#
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
--
36
2.13.6
37
38
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
This is the first step towards creating an infrastructure for generic
2
background jobs that aren't tied to a block device. For now, Job only
3
stores its ID and JobDriver, the rest stays in BlockJob.
4
1
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
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
14
Reviewed-by: John Snow <jsnow@redhat.com>
15
---
16
include/block/blockjob.h | 9 +++----
17
include/block/blockjob_int.h | 4 +--
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
34
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/block/blockjob.h
37
+++ b/include/block/blockjob.h
38
@@ -XXX,XX +XXX,XX @@
39
#ifndef BLOCKJOB_H
40
#define BLOCKJOB_H
41
42
+#include "qemu/job.h"
43
#include "block/block.h"
44
#include "qemu/ratelimit.h"
45
46
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJobTxn BlockJobTxn;
47
* Long-running operation on a BlockDriverState.
48
*/
49
typedef struct BlockJob {
50
+ /** Data belonging to the generic Job infrastructure */
51
+ Job job;
52
+
53
/** The job type, including the job vtable. */
54
const BlockJobDriver *driver;
55
56
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
57
BlockBackend *blk;
58
59
/**
60
- * The ID of the block job. May be NULL for internal jobs.
61
- */
62
- char *id;
63
-
64
- /**
65
* The coroutine that executes the job. If not NULL, it is
66
* reentered when busy is false and the job is cancelled.
67
*/
68
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
69
index XXXXXXX..XXXXXXX 100644
70
--- a/include/block/blockjob_int.h
71
+++ b/include/block/blockjob_int.h
72
@@ -XXX,XX +XXX,XX @@
73
* A class type for block job driver.
74
*/
75
struct BlockJobDriver {
76
- /** Derived BlockJob struct size */
77
- size_t instance_size;
78
+ /** Generic JobDriver callbacks and settings */
79
+ JobDriver job_driver;
80
81
/** String describing the operation, part of query-block-jobs QMP API */
82
BlockJobType job_type;
83
diff --git a/include/qemu/job.h b/include/qemu/job.h
84
new file mode 100644
85
index XXXXXXX..XXXXXXX
86
--- /dev/null
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@
89
+/*
90
+ * Declarations for background jobs
91
+ *
92
+ * Copyright (c) 2011 IBM Corp.
93
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
94
+ *
95
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
96
+ * of this software and associated documentation files (the "Software"), to deal
97
+ * in the Software without restriction, including without limitation the rights
98
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
99
+ * copies of the Software, and to permit persons to whom the Software is
100
+ * furnished to do so, subject to the following conditions:
101
+ *
102
+ * The above copyright notice and this permission notice shall be included in
103
+ * all copies or substantial portions of the Software.
104
+ *
105
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
106
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
107
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
108
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
109
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
110
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
111
+ * THE SOFTWARE.
112
+ */
113
+
114
+#ifndef JOB_H
115
+#define JOB_H
116
+
117
+typedef struct JobDriver JobDriver;
118
+
119
+/**
120
+ * Long-running operation.
121
+ */
122
+typedef struct Job {
123
+ /** The ID of the job. May be NULL for internal jobs. */
124
+ char *id;
125
+
126
+ /** The type of this job. */
127
+ const JobDriver *driver;
128
+} Job;
129
+
130
+/**
131
+ * Callbacks and other information about a Job driver.
132
+ */
133
+struct JobDriver {
134
+ /** Derived Job struct size */
135
+ size_t instance_size;
136
+};
137
+
138
+
139
+/**
140
+ * Create a new long-running job and return it.
141
+ *
142
+ * @job_id: The id of the newly-created job, or %NULL for internal jobs
143
+ * @driver: The class object for the newly-created job.
144
+ * @errp: Error object.
145
+ */
146
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
147
+
148
+#endif
149
diff --git a/block/backup.c b/block/backup.c
150
index XXXXXXX..XXXXXXX 100644
151
--- a/block/backup.c
152
+++ b/block/backup.c
153
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
154
}
155
156
static const BlockJobDriver backup_job_driver = {
157
- .instance_size = sizeof(BackupBlockJob),
158
+ .job_driver = {
159
+ .instance_size = sizeof(BackupBlockJob),
160
+ },
161
.job_type = BLOCK_JOB_TYPE_BACKUP,
162
.start = backup_run,
163
.commit = backup_commit,
164
diff --git a/block/commit.c b/block/commit.c
165
index XXXXXXX..XXXXXXX 100644
166
--- a/block/commit.c
167
+++ b/block/commit.c
168
@@ -XXX,XX +XXX,XX @@ out:
169
}
170
171
static const BlockJobDriver commit_job_driver = {
172
- .instance_size = sizeof(CommitBlockJob),
173
+ .job_driver = {
174
+ .instance_size = sizeof(CommitBlockJob),
175
+ },
176
.job_type = BLOCK_JOB_TYPE_COMMIT,
177
.start = commit_run,
178
};
179
diff --git a/block/mirror.c b/block/mirror.c
180
index XXXXXXX..XXXXXXX 100644
181
--- a/block/mirror.c
182
+++ b/block/mirror.c
183
@@ -XXX,XX +XXX,XX @@ static void mirror_complete(BlockJob *job, Error **errp)
184
185
if (!s->synced) {
186
error_setg(errp, "The active block job '%s' cannot be completed",
187
- job->id);
188
+ job->job.id);
189
return;
190
}
191
192
@@ -XXX,XX +XXX,XX @@ static void mirror_drain(BlockJob *job)
193
}
194
195
static const BlockJobDriver mirror_job_driver = {
196
- .instance_size = sizeof(MirrorBlockJob),
197
+ .job_driver = {
198
+ .instance_size = sizeof(MirrorBlockJob),
199
+ },
200
.job_type = BLOCK_JOB_TYPE_MIRROR,
201
.start = mirror_run,
202
.complete = mirror_complete,
203
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
204
};
205
206
static const BlockJobDriver commit_active_job_driver = {
207
- .instance_size = sizeof(MirrorBlockJob),
208
+ .job_driver = {
209
+ .instance_size = sizeof(MirrorBlockJob),
210
+ },
211
.job_type = BLOCK_JOB_TYPE_COMMIT,
212
.start = mirror_run,
213
.complete = mirror_complete,
214
diff --git a/block/stream.c b/block/stream.c
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/stream.c
217
+++ b/block/stream.c
218
@@ -XXX,XX +XXX,XX @@ out:
219
}
220
221
static const BlockJobDriver stream_job_driver = {
222
- .instance_size = sizeof(StreamBlockJob),
223
+ .job_driver = {
224
+ .instance_size = sizeof(StreamBlockJob),
225
+ },
226
.job_type = BLOCK_JOB_TYPE_STREAM,
227
.start = stream_run,
228
};
229
diff --git a/blockjob.c b/blockjob.c
230
index XXXXXXX..XXXXXXX 100644
231
--- a/blockjob.c
232
+++ b/blockjob.c
233
@@ -XXX,XX +XXX,XX @@
234
#include "qapi/qapi-events-block-core.h"
235
#include "qapi/qmp/qerror.h"
236
#include "qemu/coroutine.h"
237
-#include "qemu/id.h"
238
#include "qemu/timer.h"
239
240
/* Right now, this mutex is only needed to synchronize accesses to job->busy
241
@@ -XXX,XX +XXX,XX @@ static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
242
return 0;
243
}
244
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
245
- job->id, BlockJobStatus_str(job->status), BlockJobVerb_str(bv));
246
+ job->job.id, BlockJobStatus_str(job->status),
247
+ BlockJobVerb_str(bv));
248
return -EPERM;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
252
BlockJob *job;
253
254
QLIST_FOREACH(job, &block_jobs, job_list) {
255
- if (job->id && !strcmp(id, job->id)) {
256
+ if (job->job.id && !strcmp(id, job->job.id)) {
257
return job;
258
}
259
}
260
@@ -XXX,XX +XXX,XX @@ void block_job_unref(BlockJob *job)
261
block_job_detach_aio_context, job);
262
blk_unref(job->blk);
263
error_free(job->blocker);
264
- g_free(job->id);
265
+ g_free(job->job.id);
266
assert(!timer_pending(&job->sleep_timer));
267
g_free(job);
268
}
269
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
270
BlockJob *job = c->opaque;
271
return g_strdup_printf("%s job '%s'",
272
BlockJobType_str(job->driver->job_type),
273
- job->id);
274
+ job->job.id);
275
}
276
277
static void child_job_drained_begin(BdrvChild *c)
278
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
279
280
bool block_job_is_internal(BlockJob *job)
281
{
282
- return (job->id == NULL);
283
+ return (job->job.id == NULL);
284
}
285
286
static bool block_job_started(BlockJob *job)
287
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
288
void block_job_complete(BlockJob *job, Error **errp)
289
{
290
/* Should not be reachable via external interface for internal jobs */
291
- assert(job->id);
292
+ assert(job->job.id);
293
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
294
return;
295
}
296
if (job->pause_count || job->cancelled || !job->driver->complete) {
297
error_setg(errp, "The active block job '%s' cannot be completed",
298
- job->id);
299
+ job->job.id);
300
return;
301
}
302
303
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
304
305
void block_job_finalize(BlockJob *job, Error **errp)
306
{
307
- assert(job && job->id);
308
+ assert(job && job->job.id);
309
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
310
return;
311
}
312
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
313
{
314
BlockJob *job = *jobptr;
315
/* similarly to _complete, this is QMP-interface only. */
316
- assert(job->id);
317
+ assert(job->job.id);
318
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
319
return;
320
}
321
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
322
}
323
info = g_new0(BlockJobInfo, 1);
324
info->type = g_strdup(BlockJobType_str(job->driver->job_type));
325
- info->device = g_strdup(job->id);
326
+ info->device = g_strdup(job->job.id);
327
info->len = job->len;
328
info->busy = atomic_read(&job->busy);
329
info->paused = job->pause_count > 0;
330
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(BlockJob *job)
331
}
332
333
qapi_event_send_block_job_cancelled(job->driver->job_type,
334
- job->id,
335
+ job->job.id,
336
job->len,
337
job->offset,
338
job->speed,
339
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
340
}
341
342
qapi_event_send_block_job_completed(job->driver->job_type,
343
- job->id,
344
+ job->job.id,
345
job->len,
346
job->offset,
347
job->speed,
348
@@ -XXX,XX +XXX,XX @@ static int block_job_event_pending(BlockJob *job)
349
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
350
if (!job->auto_finalize && !block_job_is_internal(job)) {
351
qapi_event_send_block_job_pending(job->driver->job_type,
352
- job->id,
353
+ job->job.id,
354
&error_abort);
355
}
356
return 0;
357
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
358
error_setg(errp, "Cannot specify job ID for internal block job");
359
return NULL;
360
}
361
-
362
- if (!id_wellformed(job_id)) {
363
- error_setg(errp, "Invalid job ID '%s'", job_id);
364
- return NULL;
365
- }
366
-
367
if (block_job_get(job_id)) {
368
error_setg(errp, "Job ID '%s' already in use", job_id);
369
return NULL;
370
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
371
return NULL;
372
}
373
374
- job = g_malloc0(driver->instance_size);
375
+ job = job_create(job_id, &driver->job_driver, errp);
376
+ if (job == NULL) {
377
+ blk_unref(blk);
378
+ return NULL;
379
+ }
380
+
381
job->driver = driver;
382
- job->id = g_strdup(job_id);
383
job->blk = blk;
384
job->cb = cb;
385
job->opaque = opaque;
386
@@ -XXX,XX +XXX,XX @@ void block_job_event_ready(BlockJob *job)
387
}
388
389
qapi_event_send_block_job_ready(job->driver->job_type,
390
- job->id,
391
+ job->job.id,
392
job->len,
393
job->offset,
394
job->speed, &error_abort);
395
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
396
abort();
397
}
398
if (!block_job_is_internal(job)) {
399
- qapi_event_send_block_job_error(job->id,
400
+ qapi_event_send_block_job_error(job->job.id,
401
is_read ? IO_OPERATION_TYPE_READ :
402
IO_OPERATION_TYPE_WRITE,
403
action, &error_abort);
404
diff --git a/job.c b/job.c
405
new file mode 100644
406
index XXXXXXX..XXXXXXX
407
--- /dev/null
408
+++ b/job.c
409
@@ -XXX,XX +XXX,XX @@
410
+/*
411
+ * Background jobs (long-running operations)
412
+ *
413
+ * Copyright (c) 2011 IBM Corp.
414
+ * Copyright (c) 2012, 2018 Red Hat, Inc.
415
+ *
416
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
417
+ * of this software and associated documentation files (the "Software"), to deal
418
+ * in the Software without restriction, including without limitation the rights
419
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
420
+ * copies of the Software, and to permit persons to whom the Software is
421
+ * furnished to do so, subject to the following conditions:
422
+ *
423
+ * The above copyright notice and this permission notice shall be included in
424
+ * all copies or substantial portions of the Software.
425
+ *
426
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
427
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
428
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
429
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
430
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
431
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
432
+ * THE SOFTWARE.
433
+ */
434
+
435
+#include "qemu/osdep.h"
436
+#include "qemu-common.h"
437
+#include "qapi/error.h"
438
+#include "qemu/job.h"
439
+#include "qemu/id.h"
440
+
441
+void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
442
+{
443
+ Job *job;
444
+
445
+ if (job_id) {
446
+ if (!id_wellformed(job_id)) {
447
+ error_setg(errp, "Invalid job ID '%s'", job_id);
448
+ return NULL;
449
+ }
450
+ }
451
+
452
+ job = g_malloc0(driver->instance_size);
453
+ job->driver = driver;
454
+ job->id = g_strdup(job_id);
455
+
456
+ return job;
457
+}
458
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
459
index XXXXXXX..XXXXXXX 100644
460
--- a/tests/test-bdrv-drain.c
461
+++ b/tests/test-bdrv-drain.c
462
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
463
}
464
465
BlockJobDriver test_job_driver = {
466
- .instance_size = sizeof(TestBlockJob),
467
+ .job_driver = {
468
+ .instance_size = sizeof(TestBlockJob),
469
+ },
470
.start = test_job_start,
471
.complete = test_job_complete,
472
};
473
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
474
index XXXXXXX..XXXXXXX 100644
475
--- a/tests/test-blockjob-txn.c
476
+++ b/tests/test-blockjob-txn.c
477
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
478
}
479
480
static const BlockJobDriver test_block_job_driver = {
481
- .instance_size = sizeof(TestBlockJob),
482
+ .job_driver = {
483
+ .instance_size = sizeof(TestBlockJob),
484
+ },
485
.start = test_block_job_run,
486
};
487
488
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
489
index XXXXXXX..XXXXXXX 100644
490
--- a/tests/test-blockjob.c
491
+++ b/tests/test-blockjob.c
492
@@ -XXX,XX +XXX,XX @@
493
#include "sysemu/block-backend.h"
494
495
static const BlockJobDriver test_block_job_driver = {
496
- .instance_size = sizeof(BlockJob),
497
+ .job_driver = {
498
+ .instance_size = sizeof(BlockJob),
499
+ },
500
};
501
502
static void block_job_cb(void *opaque, int ret)
503
@@ -XXX,XX +XXX,XX @@ static BlockJob *mk_job(BlockBackend *blk, const char *id,
504
g_assert_null(errp);
505
g_assert_nonnull(job);
506
if (id) {
507
- g_assert_cmpstr(job->id, ==, id);
508
+ g_assert_cmpstr(job->job.id, ==, id);
509
} else {
510
- g_assert_cmpstr(job->id, ==, blk_name(blk));
511
+ g_assert_cmpstr(job->job.id, ==, blk_name(blk));
512
}
513
} else {
514
g_assert_nonnull(errp);
515
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
516
}
517
518
static const BlockJobDriver test_cancel_driver = {
519
- .instance_size = sizeof(CancelJob),
520
+ .job_driver = {
521
+ .instance_size = sizeof(CancelJob),
522
+ },
523
.start = cancel_job_start,
524
.complete = cancel_job_complete,
525
};
526
diff --git a/MAINTAINERS b/MAINTAINERS
527
index XXXXXXX..XXXXXXX 100644
528
--- a/MAINTAINERS
529
+++ b/MAINTAINERS
530
@@ -XXX,XX +XXX,XX @@ L: qemu-block@nongnu.org
531
S: Supported
532
F: blockjob.c
533
F: include/block/blockjob.h
534
+F: job.c
535
+F: include/block/job.h
536
F: block/backup.c
537
F: block/commit.c
538
F: block/stream.c
539
diff --git a/Makefile.objs b/Makefile.objs
540
index XXXXXXX..XXXXXXX 100644
541
--- a/Makefile.objs
542
+++ b/Makefile.objs
543
@@ -XXX,XX +XXX,XX @@ chardev-obj-y = chardev/
544
# block-obj-y is code used by both qemu system emulation and qemu-img
545
546
block-obj-y += nbd/
547
-block-obj-y += block.o blockjob.o
548
+block-obj-y += block.o blockjob.o job.o
549
block-obj-y += block/ scsi/
550
block-obj-y += qemu-io-cmds.o
551
block-obj-$(CONFIG_REPLICATION) += replication.o
552
--
553
2.13.6
554
555
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
This moves the job list from BlockJob to Job. Now we can check for
2
duplicate IDs in job_create().
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/block/blockjob.h | 3 ---
9
include/qemu/job.h | 19 +++++++++++++++++++
10
blockjob.c | 46 ++++++++++++++++++++++++----------------------
11
job.c | 31 +++++++++++++++++++++++++++++++
12
4 files changed, 74 insertions(+), 25 deletions(-)
13
14
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
17
+++ b/include/block/blockjob.h
18
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
19
*/
20
bool deferred_to_main_loop;
21
22
- /** Element of the list of block jobs */
23
- QLIST_ENTRY(BlockJob) job_list;
24
-
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
+
45
+ /** Element of the list of jobs */
46
+ QLIST_ENTRY(Job) job_list;
47
} Job;
48
49
/**
50
@@ -XXX,XX +XXX,XX @@ JobType job_type(const Job *job);
51
/** Returns the enum string for the JobType of a given Job. */
52
const char *job_type_str(const Job *job);
53
54
+/**
55
+ * Get the next element from the list of block jobs after @job, or the
56
+ * first one if @job is %NULL.
57
+ *
58
+ * Returns the requested job, or %NULL if there are no more jobs left.
59
+ */
60
+Job *job_next(Job *job);
61
+
62
+/**
63
+ * Get the job identified by @id (which must not be %NULL).
64
+ *
65
+ * Returns the requested job, or %NULL if it doesn't exist.
66
+ */
67
+Job *job_get(const char *id);
68
+
69
#endif
70
diff --git a/blockjob.c b/blockjob.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/blockjob.c
73
+++ b/blockjob.c
74
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
75
int refcnt;
76
};
77
78
-static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
79
-
80
/*
81
* The block job API is composed of two categories of functions.
82
*
83
@@ -XXX,XX +XXX,XX @@ static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
84
* blockjob_int.h.
85
*/
86
87
-BlockJob *block_job_next(BlockJob *job)
88
+static bool is_block_job(Job *job)
89
{
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
+ }
225
}
226
227
job = g_malloc0(driver->instance_size);
228
job->driver = driver;
229
job->id = g_strdup(job_id);
230
231
+ QLIST_INSERT_HEAD(&jobs, job, job_list);
232
+
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
--
244
2.13.6
245
246
diff view generated by jsdifflib
Deleted patch
1
This moves BlockJob.status and the closely related functions
2
(block_)job_state_transition() and (block_)job_apply_verb to Job. The
3
two QAPI enums are renamed to JobStatus and JobVerb.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
---
10
qapi/block-core.json | 16 ++++----
11
include/block/blockjob.h | 3 --
12
include/qemu/job.h | 13 ++++++
13
blockjob.c | 102 +++++++++++------------------------------------
14
job.c | 56 ++++++++++++++++++++++++++
15
tests/test-blockjob.c | 39 +++++++++---------
16
block/trace-events | 2 -
17
trace-events | 4 ++
18
8 files changed, 123 insertions(+), 112 deletions(-)
19
20
diff --git a/qapi/block-core.json b/qapi/block-core.json
21
index XXXXXXX..XXXXXXX 100644
22
--- a/qapi/block-core.json
23
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
25
'data': ['commit', 'stream', 'mirror', 'backup'] }
26
27
##
28
-# @BlockJobVerb:
29
+# @JobVerb:
30
#
31
-# Represents command verbs that can be applied to a blockjob.
32
+# Represents command verbs that can be applied to a job.
33
#
34
# @cancel: see @block-job-cancel
35
#
36
@@ -XXX,XX +XXX,XX @@
37
#
38
# Since: 2.12
39
##
40
-{ 'enum': 'BlockJobVerb',
41
+{ 'enum': 'JobVerb',
42
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
43
'finalize' ] }
44
45
##
46
-# @BlockJobStatus:
47
+# @JobStatus:
48
#
49
-# Indicates the present state of a given blockjob in its lifetime.
50
+# Indicates the present state of a given job in its lifetime.
51
#
52
# @undefined: Erroneous, default state. Should not ever be visible.
53
#
54
@@ -XXX,XX +XXX,XX @@
55
#
56
# Since: 2.12
57
##
58
-{ 'enum': 'BlockJobStatus',
59
+{ 'enum': 'JobStatus',
60
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
61
'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
62
63
@@ -XXX,XX +XXX,XX @@
64
'data': {'type': 'str', 'device': 'str', 'len': 'int',
65
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
66
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
67
- 'status': 'BlockJobStatus',
68
+ 'status': 'JobStatus',
69
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
70
'*error': 'str' } }
71
72
@@ -XXX,XX +XXX,XX @@
73
# QEMU 2.12+ job lifetime management semantics.
74
#
75
# This command will refuse to operate on any job that has not yet reached
76
-# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
77
+# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the
78
# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
79
# to be used as appropriate.
80
#
81
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
82
index XXXXXXX..XXXXXXX 100644
83
--- a/include/block/blockjob.h
84
+++ b/include/block/blockjob.h
85
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
86
*/
87
QEMUTimer sleep_timer;
88
89
- /** Current state; See @BlockJobStatus for details. */
90
- BlockJobStatus status;
91
-
92
/** True if this job should automatically finalize itself */
93
bool auto_finalize;
94
95
diff --git a/include/qemu/job.h b/include/qemu/job.h
96
index XXXXXXX..XXXXXXX 100644
97
--- a/include/qemu/job.h
98
+++ b/include/qemu/job.h
99
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
100
/** The type of this job. */
101
const JobDriver *driver;
102
103
+ /** Current state; See @JobStatus for details. */
104
+ JobStatus status;
105
+
106
/** Element of the list of jobs */
107
QLIST_ENTRY(Job) job_list;
108
} Job;
109
@@ -XXX,XX +XXX,XX @@ Job *job_next(Job *job);
110
*/
111
Job *job_get(const char *id);
112
113
+/**
114
+ * Check whether the verb @verb can be applied to @job in its current state.
115
+ * Returns 0 if the verb can be applied; otherwise errp is set and -EPERM
116
+ * returned.
117
+ */
118
+int job_apply_verb(Job *job, JobVerb verb, Error **errp);
119
+
120
+/* TODO To be removed from the public interface */
121
+void job_state_transition(Job *job, JobStatus s1);
122
+
123
#endif
124
diff --git a/blockjob.c b/blockjob.c
125
index XXXXXXX..XXXXXXX 100644
126
--- a/blockjob.c
127
+++ b/blockjob.c
128
@@ -XXX,XX +XXX,XX @@
129
* block_job_enter. */
130
static QemuMutex block_job_mutex;
131
132
-/* BlockJob State Transition Table */
133
-bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
134
- /* U, C, R, P, Y, S, W, D, X, E, N */
135
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
136
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
137
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
138
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
139
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
140
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
141
- /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
142
- /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
143
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
144
- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
145
- /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
146
-};
147
-
148
-bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
149
- /* U, C, R, P, Y, S, W, D, X, E, N */
150
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
151
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
152
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
153
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
154
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
155
- [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
156
- [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
157
-};
158
-
159
-static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
160
-{
161
- BlockJobStatus s0 = job->status;
162
- assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
163
- trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
164
- "allowed" : "disallowed",
165
- BlockJobStatus_str(s0),
166
- BlockJobStatus_str(s1));
167
- assert(BlockJobSTT[s0][s1]);
168
- job->status = s1;
169
-}
170
-
171
-static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
172
-{
173
- assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
174
- trace_block_job_apply_verb(job, BlockJobStatus_str(job->status),
175
- BlockJobVerb_str(bv),
176
- BlockJobVerbTable[bv][job->status] ?
177
- "allowed" : "prohibited");
178
- if (BlockJobVerbTable[bv][job->status]) {
179
- return 0;
180
- }
181
- error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
182
- job->job.id, BlockJobStatus_str(job->status),
183
- BlockJobVerb_str(bv));
184
- return -EPERM;
185
-}
186
-
187
static void block_job_lock(void)
188
{
189
qemu_mutex_lock(&block_job_mutex);
190
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque);
191
void block_job_unref(BlockJob *job)
192
{
193
if (--job->refcnt == 0) {
194
- assert(job->status == BLOCK_JOB_STATUS_NULL);
195
+ assert(job->job.status == JOB_STATUS_NULL);
196
assert(!job->txn);
197
BlockDriverState *bs = blk_bs(job->blk);
198
bs->job = NULL;
199
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
200
job->pause_count--;
201
job->busy = true;
202
job->paused = false;
203
- block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
204
+ job_state_transition(&job->job, JOB_STATUS_RUNNING);
205
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
206
}
207
208
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
209
job->paused = false;
210
job->deferred_to_main_loop = true;
211
block_job_txn_del_job(job);
212
- block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
213
+ job_state_transition(&job->job, JOB_STATUS_NULL);
214
block_job_unref(job);
215
}
216
217
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
218
219
static void block_job_conclude(BlockJob *job)
220
{
221
- block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
222
+ job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
223
if (job->auto_dismiss || !block_job_started(job)) {
224
block_job_do_dismiss(job);
225
}
226
@@ -XXX,XX +XXX,XX @@ static void block_job_update_rc(BlockJob *job)
227
job->ret = -ECANCELED;
228
}
229
if (job->ret) {
230
- block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
231
+ job_state_transition(&job->job, JOB_STATUS_ABORTING);
232
}
233
}
234
235
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
236
BlockJobTxn *txn = job->txn;
237
BlockJob *other_job;
238
239
- block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
240
+ job_state_transition(&job->job, JOB_STATUS_WAITING);
241
242
/*
243
* Successful completion, see if there are other running jobs in this
244
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
245
{
246
int64_t old_speed = job->speed;
247
248
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
249
+ if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
250
return;
251
}
252
if (speed < 0) {
253
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
254
{
255
/* Should not be reachable via external interface for internal jobs */
256
assert(job->job.id);
257
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
258
+ if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
259
return;
260
}
261
if (job->pause_count || job->cancelled || !job->driver->complete) {
262
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
263
void block_job_finalize(BlockJob *job, Error **errp)
264
{
265
assert(job && job->job.id);
266
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
267
+ if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) {
268
return;
269
}
270
block_job_do_finalize(job);
271
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
272
BlockJob *job = *jobptr;
273
/* similarly to _complete, this is QMP-interface only. */
274
assert(job->job.id);
275
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
276
+ if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
277
return;
278
}
279
280
@@ -XXX,XX +XXX,XX @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
281
282
void block_job_user_pause(BlockJob *job, Error **errp)
283
{
284
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
285
+ if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) {
286
return;
287
}
288
if (job->user_paused) {
289
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
290
error_setg(errp, "Can't resume a job that was not paused");
291
return;
292
}
293
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
294
+ if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) {
295
return;
296
}
297
block_job_iostatus_reset(job);
298
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
299
300
void block_job_cancel(BlockJob *job, bool force)
301
{
302
- if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
303
+ if (job->job.status == JOB_STATUS_CONCLUDED) {
304
block_job_do_dismiss(job);
305
return;
306
}
307
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
308
309
void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
310
{
311
- if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
312
+ if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
313
return;
314
}
315
block_job_cancel(job, force);
316
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
317
info->speed = job->speed;
318
info->io_status = job->iostatus;
319
info->ready = job->ready;
320
- info->status = job->status;
321
+ info->status = job->job.status;
322
info->auto_finalize = job->auto_finalize;
323
info->auto_dismiss = job->auto_dismiss;
324
info->has_error = job->ret != 0;
325
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
326
327
static int block_job_event_pending(BlockJob *job)
328
{
329
- block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
330
+ job_state_transition(&job->job, JOB_STATUS_PENDING);
331
if (!job->auto_finalize && !block_job_is_internal(job)) {
332
qapi_event_send_block_job_pending(job_type(&job->job),
333
job->job.id,
334
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
335
job->refcnt = 1;
336
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
337
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
338
- block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
339
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
340
QEMU_CLOCK_REALTIME, SCALE_NS,
341
block_job_sleep_timer_cb, job);
342
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
343
344
void block_job_early_fail(BlockJob *job)
345
{
346
- assert(job->status == BLOCK_JOB_STATUS_CREATED);
347
+ assert(job->job.status == JOB_STATUS_CREATED);
348
block_job_decommission(job);
349
}
350
351
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
352
}
353
354
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
355
- BlockJobStatus status = job->status;
356
- block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
357
- BLOCK_JOB_STATUS_STANDBY : \
358
- BLOCK_JOB_STATUS_PAUSED);
359
+ JobStatus status = job->job.status;
360
+ job_state_transition(&job->job, status == JOB_STATUS_READY
361
+ ? JOB_STATUS_STANDBY
362
+ : JOB_STATUS_PAUSED);
363
job->paused = true;
364
block_job_do_yield(job, -1);
365
job->paused = false;
366
- block_job_state_transition(job, status);
367
+ job_state_transition(&job->job, status);
368
}
369
370
if (job->driver->resume) {
371
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
372
373
void block_job_event_ready(BlockJob *job)
374
{
375
- block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
376
+ job_state_transition(&job->job, JOB_STATUS_READY);
377
job->ready = true;
378
379
if (block_job_is_internal(job)) {
380
diff --git a/job.c b/job.c
381
index XXXXXXX..XXXXXXX 100644
382
--- a/job.c
383
+++ b/job.c
384
@@ -XXX,XX +XXX,XX @@
385
#include "qapi/error.h"
386
#include "qemu/job.h"
387
#include "qemu/id.h"
388
+#include "trace-root.h"
389
390
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
391
392
+/* Job State Transition Table */
393
+bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = {
394
+ /* U, C, R, P, Y, S, W, D, X, E, N */
395
+ /* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
396
+ /* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
397
+ /* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
398
+ /* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
399
+ /* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
400
+ /* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
401
+ /* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
402
+ /* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
403
+ /* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
404
+ /* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
405
+ /* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
406
+};
407
+
408
+bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
409
+ /* U, C, R, P, Y, S, W, D, X, E, N */
410
+ [JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
411
+ [JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
412
+ [JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
413
+ [JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
414
+ [JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
415
+ [JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
416
+ [JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
417
+};
418
+
419
+/* TODO Make static once the whole state machine is in job.c */
420
+void job_state_transition(Job *job, JobStatus s1)
421
+{
422
+ JobStatus s0 = job->status;
423
+ assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
424
+ trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
425
+ JobSTT[s0][s1] ? "allowed" : "disallowed",
426
+ JobStatus_str(s0), JobStatus_str(s1));
427
+ assert(JobSTT[s0][s1]);
428
+ job->status = s1;
429
+}
430
+
431
+int job_apply_verb(Job *job, JobVerb verb, Error **errp)
432
+{
433
+ JobStatus s0 = job->status;
434
+ assert(verb >= 0 && verb <= JOB_VERB__MAX);
435
+ trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
436
+ JobVerbTable[verb][s0] ? "allowed" : "prohibited");
437
+ if (JobVerbTable[verb][s0]) {
438
+ return 0;
439
+ }
440
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
441
+ job->id, JobStatus_str(s0), JobVerb_str(verb));
442
+ return -EPERM;
443
+}
444
+
445
JobType job_type(const Job *job)
446
{
447
return job->driver->job_type;
448
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
449
job->driver = driver;
450
job->id = g_strdup(job_id);
451
452
+ job_state_transition(job, JOB_STATUS_CREATED);
453
+
454
QLIST_INSERT_HEAD(&jobs, job, job_list);
455
456
return job;
457
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
458
index XXXXXXX..XXXXXXX 100644
459
--- a/tests/test-blockjob.c
460
+++ b/tests/test-blockjob.c
461
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
462
job = mk_job(blk, "Steve", &test_cancel_driver, true,
463
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
464
block_job_ref(job);
465
- assert(job->status == BLOCK_JOB_STATUS_CREATED);
466
+ assert(job->job.status == JOB_STATUS_CREATED);
467
s = container_of(job, CancelJob, common);
468
s->blk = blk;
469
470
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
471
{
472
BlockJob *job = &s->common;
473
BlockBackend *blk = s->blk;
474
- BlockJobStatus sts = job->status;
475
+ JobStatus sts = job->job.status;
476
477
block_job_cancel_sync(job);
478
- if ((sts != BLOCK_JOB_STATUS_CREATED) &&
479
- (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
480
+ if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
481
BlockJob *dummy = job;
482
block_job_dismiss(&dummy, &error_abort);
483
}
484
- assert(job->status == BLOCK_JOB_STATUS_NULL);
485
+ assert(job->job.status == JOB_STATUS_NULL);
486
block_job_unref(job);
487
destroy_blk(blk);
488
}
489
@@ -XXX,XX +XXX,XX @@ static void test_cancel_running(void)
490
s = create_common(&job);
491
492
block_job_start(job);
493
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
494
+ assert(job->job.status == JOB_STATUS_RUNNING);
495
496
cancel_common(s);
497
}
498
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
499
s = create_common(&job);
500
501
block_job_start(job);
502
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
503
+ assert(job->job.status == JOB_STATUS_RUNNING);
504
505
block_job_user_pause(job, &error_abort);
506
block_job_enter(job);
507
- assert(job->status == BLOCK_JOB_STATUS_PAUSED);
508
+ assert(job->job.status == JOB_STATUS_PAUSED);
509
510
cancel_common(s);
511
}
512
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
513
s = create_common(&job);
514
515
block_job_start(job);
516
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
517
+ assert(job->job.status == JOB_STATUS_RUNNING);
518
519
s->should_converge = true;
520
block_job_enter(job);
521
- assert(job->status == BLOCK_JOB_STATUS_READY);
522
+ assert(job->job.status == JOB_STATUS_READY);
523
524
cancel_common(s);
525
}
526
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
527
s = create_common(&job);
528
529
block_job_start(job);
530
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
531
+ assert(job->job.status == JOB_STATUS_RUNNING);
532
533
s->should_converge = true;
534
block_job_enter(job);
535
- assert(job->status == BLOCK_JOB_STATUS_READY);
536
+ assert(job->job.status == JOB_STATUS_READY);
537
538
block_job_user_pause(job, &error_abort);
539
block_job_enter(job);
540
- assert(job->status == BLOCK_JOB_STATUS_STANDBY);
541
+ assert(job->job.status == JOB_STATUS_STANDBY);
542
543
cancel_common(s);
544
}
545
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
546
s = create_common(&job);
547
548
block_job_start(job);
549
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
550
+ assert(job->job.status == JOB_STATUS_RUNNING);
551
552
s->should_converge = true;
553
block_job_enter(job);
554
- assert(job->status == BLOCK_JOB_STATUS_READY);
555
+ assert(job->job.status == JOB_STATUS_READY);
556
557
block_job_complete(job, &error_abort);
558
block_job_enter(job);
559
while (!s->completed) {
560
aio_poll(qemu_get_aio_context(), true);
561
}
562
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
563
+ assert(job->job.status == JOB_STATUS_PENDING);
564
565
cancel_common(s);
566
}
567
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
568
s = create_common(&job);
569
570
block_job_start(job);
571
- assert(job->status == BLOCK_JOB_STATUS_RUNNING);
572
+ assert(job->job.status == JOB_STATUS_RUNNING);
573
574
s->should_converge = true;
575
block_job_enter(job);
576
- assert(job->status == BLOCK_JOB_STATUS_READY);
577
+ assert(job->job.status == JOB_STATUS_READY);
578
579
block_job_complete(job, &error_abort);
580
block_job_enter(job);
581
while (!s->completed) {
582
aio_poll(qemu_get_aio_context(), true);
583
}
584
- assert(job->status == BLOCK_JOB_STATUS_PENDING);
585
+ assert(job->job.status == JOB_STATUS_PENDING);
586
587
block_job_finalize(job, &error_abort);
588
- assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
589
+ assert(job->job.status == JOB_STATUS_CONCLUDED);
590
591
cancel_common(s);
592
}
593
diff --git a/block/trace-events b/block/trace-events
594
index XXXXXXX..XXXXXXX 100644
595
--- a/block/trace-events
596
+++ b/block/trace-events
597
@@ -XXX,XX +XXX,XX @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
598
599
# blockjob.c
600
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
601
-block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
602
-block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
603
604
# block/block-backend.c
605
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
606
diff --git a/trace-events b/trace-events
607
index XXXXXXX..XXXXXXX 100644
608
--- a/trace-events
609
+++ b/trace-events
610
@@ -XXX,XX +XXX,XX @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
611
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
612
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
613
614
+# job.c
615
+job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
616
+job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
617
+
618
### Guest events, keep at bottom
619
620
621
--
622
2.13.6
623
624
diff view generated by jsdifflib
Deleted patch
1
This moves reference counting from BlockJob to Job.
2
1
3
In order to keep calling the BlockJob cleanup code when the job is
4
deleted via job_unref(), introduce a new JobDriver.free callback. Every
5
block job must use block_job_free() for this callback, this is asserted
6
in block_job_create().
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: John Snow <jsnow@redhat.com>
11
---
12
include/block/blockjob.h | 21 -------------------
13
include/block/blockjob_int.h | 7 +++++++
14
include/qemu/job.h | 19 ++++++++++++++++--
15
block/backup.c | 1 +
16
block/commit.c | 1 +
17
block/mirror.c | 2 ++
18
block/stream.c | 1 +
19
blockjob.c | 48 +++++++++++++++++++-------------------------
20
job.c | 22 ++++++++++++++++----
21
qemu-img.c | 4 ++--
22
tests/test-bdrv-drain.c | 1 +
23
tests/test-blockjob-txn.c | 1 +
24
tests/test-blockjob.c | 6 ++++--
25
13 files changed, 76 insertions(+), 58 deletions(-)
26
27
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
28
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/blockjob.h
30
+++ b/include/block/blockjob.h
31
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
32
/** The opaque value that is passed to the completion function. */
33
void *opaque;
34
35
- /** Reference count of the block job */
36
- int refcnt;
37
-
38
/** True when job has reported completion by calling block_job_completed. */
39
bool completed;
40
41
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job);
42
BlockJobTxn *block_job_txn_new(void);
43
44
/**
45
- * block_job_ref:
46
- *
47
- * Add a reference to BlockJob refcnt, it will be decreased with
48
- * block_job_unref, and then be freed if it comes to be the last
49
- * reference.
50
- */
51
-void block_job_ref(BlockJob *job);
52
-
53
-/**
54
- * block_job_unref:
55
- *
56
- * Release a reference that was previously acquired with block_job_ref
57
- * or block_job_create. If it's the last reference to the object, it will be
58
- * freed.
59
- */
60
-void block_job_unref(BlockJob *job);
61
-
62
-/**
63
* block_job_txn_unref:
64
*
65
* Release a reference that was previously acquired with block_job_txn_add_job
66
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
67
index XXXXXXX..XXXXXXX 100644
68
--- a/include/block/blockjob_int.h
69
+++ b/include/block/blockjob_int.h
70
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
71
BlockCompletionFunc *cb, void *opaque, Error **errp);
72
73
/**
74
+ * block_job_free:
75
+ * Callback to be used for JobDriver.free in all block jobs. Frees block job
76
+ * specific resources in @job.
77
+ */
78
+void block_job_free(Job *job);
79
+
80
+/**
81
* block_job_sleep_ns:
82
* @job: The job that calls the function.
83
* @ns: How many nanoseconds to stop for.
84
diff --git a/include/qemu/job.h b/include/qemu/job.h
85
index XXXXXXX..XXXXXXX 100644
86
--- a/include/qemu/job.h
87
+++ b/include/qemu/job.h
88
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
89
/** The type of this job. */
90
const JobDriver *driver;
91
92
+ /** Reference count of the block job */
93
+ int refcnt;
94
+
95
/** Current state; See @JobStatus for details. */
96
JobStatus status;
97
98
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
99
100
/** Enum describing the operation */
101
JobType job_type;
102
+
103
+ /** Called when the job is freed */
104
+ void (*free)(Job *job);
105
};
106
107
108
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
109
*/
110
void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
111
112
-/** Frees the @job object. */
113
-void job_delete(Job *job);
114
+/**
115
+ * Add a reference to Job refcnt, it will be decreased with job_unref, and then
116
+ * be freed if it comes to be the last reference.
117
+ */
118
+void job_ref(Job *job);
119
+
120
+/**
121
+ * Release a reference that was previously acquired with job_ref() or
122
+ * job_create(). If it's the last reference to the object, it will be freed.
123
+ */
124
+void job_unref(Job *job);
125
126
/** Returns the JobType of a given Job. */
127
JobType job_type(const Job *job);
128
diff --git a/block/backup.c b/block/backup.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/block/backup.c
131
+++ b/block/backup.c
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
133
.job_driver = {
134
.instance_size = sizeof(BackupBlockJob),
135
.job_type = JOB_TYPE_BACKUP,
136
+ .free = block_job_free,
137
},
138
.start = backup_run,
139
.commit = backup_commit,
140
diff --git a/block/commit.c b/block/commit.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/block/commit.c
143
+++ b/block/commit.c
144
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
145
.job_driver = {
146
.instance_size = sizeof(CommitBlockJob),
147
.job_type = JOB_TYPE_COMMIT,
148
+ .free = block_job_free,
149
},
150
.start = commit_run,
151
};
152
diff --git a/block/mirror.c b/block/mirror.c
153
index XXXXXXX..XXXXXXX 100644
154
--- a/block/mirror.c
155
+++ b/block/mirror.c
156
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
157
.job_driver = {
158
.instance_size = sizeof(MirrorBlockJob),
159
.job_type = JOB_TYPE_MIRROR,
160
+ .free = block_job_free,
161
},
162
.start = mirror_run,
163
.complete = mirror_complete,
164
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
165
.job_driver = {
166
.instance_size = sizeof(MirrorBlockJob),
167
.job_type = JOB_TYPE_COMMIT,
168
+ .free = block_job_free,
169
},
170
.start = mirror_run,
171
.complete = mirror_complete,
172
diff --git a/block/stream.c b/block/stream.c
173
index XXXXXXX..XXXXXXX 100644
174
--- a/block/stream.c
175
+++ b/block/stream.c
176
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
177
.job_driver = {
178
.instance_size = sizeof(StreamBlockJob),
179
.job_type = JOB_TYPE_STREAM,
180
+ .free = block_job_free,
181
},
182
.start = stream_run,
183
};
184
diff --git a/blockjob.c b/blockjob.c
185
index XXXXXXX..XXXXXXX 100644
186
--- a/blockjob.c
187
+++ b/blockjob.c
188
@@ -XXX,XX +XXX,XX @@ static void block_job_resume(BlockJob *job)
189
block_job_enter_cond(job, block_job_timer_not_pending);
190
}
191
192
-void block_job_ref(BlockJob *job)
193
-{
194
- ++job->refcnt;
195
-}
196
-
197
static void block_job_attached_aio_context(AioContext *new_context,
198
void *opaque);
199
static void block_job_detach_aio_context(void *opaque);
200
201
-void block_job_unref(BlockJob *job)
202
+void block_job_free(Job *job)
203
{
204
- if (--job->refcnt == 0) {
205
- assert(job->job.status == JOB_STATUS_NULL);
206
- assert(!job->txn);
207
- BlockDriverState *bs = blk_bs(job->blk);
208
- bs->job = NULL;
209
- block_job_remove_all_bdrv(job);
210
- blk_remove_aio_context_notifier(job->blk,
211
- block_job_attached_aio_context,
212
- block_job_detach_aio_context, job);
213
- blk_unref(job->blk);
214
- error_free(job->blocker);
215
- assert(!timer_pending(&job->sleep_timer));
216
- job_delete(&job->job);
217
- }
218
+ BlockJob *bjob = container_of(job, BlockJob, job);
219
+ BlockDriverState *bs = blk_bs(bjob->blk);
220
+
221
+ assert(!bjob->txn);
222
+
223
+ bs->job = NULL;
224
+ block_job_remove_all_bdrv(bjob);
225
+ blk_remove_aio_context_notifier(bjob->blk,
226
+ block_job_attached_aio_context,
227
+ block_job_detach_aio_context, bjob);
228
+ blk_unref(bjob->blk);
229
+ error_free(bjob->blocker);
230
+ assert(!timer_pending(&bjob->sleep_timer));
231
}
232
233
static void block_job_attached_aio_context(AioContext *new_context,
234
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
235
BlockJob *job = opaque;
236
237
/* In case the job terminates during aio_poll()... */
238
- block_job_ref(job);
239
+ job_ref(&job->job);
240
241
block_job_pause(job);
242
243
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
244
block_job_drain(job);
245
}
246
247
- block_job_unref(job);
248
+ job_unref(&job->job);
249
}
250
251
static char *child_job_get_parent_desc(BdrvChild *c)
252
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
253
job->deferred_to_main_loop = true;
254
block_job_txn_del_job(job);
255
job_state_transition(&job->job, JOB_STATUS_NULL);
256
- block_job_unref(job);
257
+ job_unref(&job->job);
258
}
259
260
static void block_job_do_dismiss(BlockJob *job)
261
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
262
263
assert(blk_bs(job->blk)->job == job);
264
265
- block_job_ref(job);
266
+ job_ref(&job->job);
267
268
if (finish) {
269
finish(job, &local_err);
270
}
271
if (local_err) {
272
error_propagate(errp, local_err);
273
- block_job_unref(job);
274
+ job_unref(&job->job);
275
return -EBUSY;
276
}
277
/* block_job_drain calls block_job_enter, and it should be enough to
278
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
279
aio_poll(qemu_get_aio_context(), true);
280
}
281
ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
282
- block_job_unref(job);
283
+ job_unref(&job->job);
284
return ret;
285
}
286
287
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
288
}
289
290
assert(is_block_job(&job->job));
291
+ assert(job->job.driver->free == &block_job_free);
292
293
job->driver = driver;
294
job->blk = blk;
295
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
296
job->busy = false;
297
job->paused = true;
298
job->pause_count = 1;
299
- job->refcnt = 1;
300
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
301
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
302
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
303
diff --git a/job.c b/job.c
304
index XXXXXXX..XXXXXXX 100644
305
--- a/job.c
306
+++ b/job.c
307
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
308
job = g_malloc0(driver->instance_size);
309
job->driver = driver;
310
job->id = g_strdup(job_id);
311
+ job->refcnt = 1;
312
313
job_state_transition(job, JOB_STATUS_CREATED);
314
315
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
316
return job;
317
}
318
319
-void job_delete(Job *job)
320
+void job_ref(Job *job)
321
{
322
- QLIST_REMOVE(job, job_list);
323
+ ++job->refcnt;
324
+}
325
+
326
+void job_unref(Job *job)
327
+{
328
+ if (--job->refcnt == 0) {
329
+ assert(job->status == JOB_STATUS_NULL);
330
331
- g_free(job->id);
332
- g_free(job);
333
+ if (job->driver->free) {
334
+ job->driver->free(job);
335
+ }
336
+
337
+ QLIST_REMOVE(job, job_list);
338
+
339
+ g_free(job->id);
340
+ g_free(job);
341
+ }
342
}
343
diff --git a/qemu-img.c b/qemu-img.c
344
index XXXXXXX..XXXXXXX 100644
345
--- a/qemu-img.c
346
+++ b/qemu-img.c
347
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
348
int ret = 0;
349
350
aio_context_acquire(aio_context);
351
- block_job_ref(job);
352
+ job_ref(&job->job);
353
do {
354
aio_poll(aio_context, true);
355
qemu_progress_print(job->len ?
356
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
357
} else {
358
ret = job->ret;
359
}
360
- block_job_unref(job);
361
+ job_unref(&job->job);
362
aio_context_release(aio_context);
363
364
/* publish completion progress only when success */
365
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
366
index XXXXXXX..XXXXXXX 100644
367
--- a/tests/test-bdrv-drain.c
368
+++ b/tests/test-bdrv-drain.c
369
@@ -XXX,XX +XXX,XX @@ static void test_job_complete(BlockJob *job, Error **errp)
370
BlockJobDriver test_job_driver = {
371
.job_driver = {
372
.instance_size = sizeof(TestBlockJob),
373
+ .free = block_job_free,
374
},
375
.start = test_job_start,
376
.complete = test_job_complete,
377
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
378
index XXXXXXX..XXXXXXX 100644
379
--- a/tests/test-blockjob-txn.c
380
+++ b/tests/test-blockjob-txn.c
381
@@ -XXX,XX +XXX,XX @@ static void test_block_job_cb(void *opaque, int ret)
382
static const BlockJobDriver test_block_job_driver = {
383
.job_driver = {
384
.instance_size = sizeof(TestBlockJob),
385
+ .free = block_job_free,
386
},
387
.start = test_block_job_run,
388
};
389
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
390
index XXXXXXX..XXXXXXX 100644
391
--- a/tests/test-blockjob.c
392
+++ b/tests/test-blockjob.c
393
@@ -XXX,XX +XXX,XX @@
394
static const BlockJobDriver test_block_job_driver = {
395
.job_driver = {
396
.instance_size = sizeof(BlockJob),
397
+ .free = block_job_free,
398
},
399
};
400
401
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
402
static const BlockJobDriver test_cancel_driver = {
403
.job_driver = {
404
.instance_size = sizeof(CancelJob),
405
+ .free = block_job_free,
406
},
407
.start = cancel_job_start,
408
.complete = cancel_job_complete,
409
@@ -XXX,XX +XXX,XX @@ static CancelJob *create_common(BlockJob **pjob)
410
blk = create_blk(NULL);
411
job = mk_job(blk, "Steve", &test_cancel_driver, true,
412
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
413
- block_job_ref(job);
414
+ job_ref(&job->job);
415
assert(job->job.status == JOB_STATUS_CREATED);
416
s = container_of(job, CancelJob, common);
417
s->blk = blk;
418
@@ -XXX,XX +XXX,XX @@ static void cancel_common(CancelJob *s)
419
block_job_dismiss(&dummy, &error_abort);
420
}
421
assert(job->job.status == JOB_STATUS_NULL);
422
- block_job_unref(job);
423
+ job_unref(&job->job);
424
destroy_blk(blk);
425
}
426
427
--
428
2.13.6
429
430
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
When block jobs need an AioContext, they just take it from their main
2
block node. Generic jobs don't have a main block node, so we need to
3
assign them an AioContext explicitly.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
9
include/qemu/job.h | 7 ++++++-
10
blockjob.c | 5 ++++-
11
job.c | 4 +++-
12
3 files changed, 13 insertions(+), 3 deletions(-)
13
14
diff --git a/include/qemu/job.h b/include/qemu/job.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/qemu/job.h
17
+++ b/include/qemu/job.h
18
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
19
/** Current state; See @JobStatus for details. */
20
JobStatus status;
21
22
+ /** AioContext to run the job coroutine in */
23
+ AioContext *aio_context;
24
+
25
/**
26
* Set to true if the job should cancel itself. The flag must
27
* always be tested just before toggling the busy flag from false
28
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
29
*
30
* @job_id: The id of the newly-created job, or %NULL for internal jobs
31
* @driver: The class object for the newly-created job.
32
+ * @ctx: The AioContext to run the job coroutine in.
33
* @errp: Error object.
34
*/
35
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp);
36
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
37
+ Error **errp);
38
39
/**
40
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
41
diff --git a/blockjob.c b/blockjob.c
42
index XXXXXXX..XXXXXXX 100644
43
--- a/blockjob.c
44
+++ b/blockjob.c
45
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
46
{
47
BlockJob *job = opaque;
48
49
+ job->job.aio_context = new_context;
50
if (job->driver->attached_aio_context) {
51
job->driver->attached_aio_context(job, new_context);
52
}
53
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
54
block_job_drain(job);
55
}
56
57
+ job->job.aio_context = NULL;
58
job_unref(&job->job);
59
}
60
61
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
62
return NULL;
63
}
64
65
- job = job_create(job_id, &driver->job_driver, errp);
66
+ job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
67
+ errp);
68
if (job == NULL) {
69
blk_unref(blk);
70
return NULL;
71
diff --git a/job.c b/job.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/job.c
74
+++ b/job.c
75
@@ -XXX,XX +XXX,XX @@ Job *job_get(const char *id)
76
return NULL;
77
}
78
79
-void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
80
+void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
81
+ Error **errp)
82
{
83
Job *job;
84
85
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
86
job->driver = driver;
87
job->id = g_strdup(job_id);
88
job->refcnt = 1;
89
+ job->aio_context = ctx;
90
91
job_state_transition(job, JOB_STATUS_CREATED);
92
93
--
94
2.13.6
95
96
diff view generated by jsdifflib
Deleted patch
1
This commit moves some core functions for dealing with the job coroutine
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
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
---
9
include/block/blockjob.h | 40 --------
10
include/block/blockjob_int.h | 26 -----
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
25
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/block/blockjob.h
28
+++ b/include/block/blockjob.h
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
30
BlockBackend *blk;
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;
76
77
- /**
78
- * Timer that is used by @block_job_sleep_ns. Accessed under
79
- * block_job_mutex (in blockjob.c).
80
- */
81
- QEMUTimer sleep_timer;
82
-
83
/** True if this job should automatically finalize itself */
84
bool auto_finalize;
85
86
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
87
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
88
89
/**
90
- * block_job_start:
91
- * @job: A job that has not yet been started.
92
- *
93
- * Begins execution of a block job.
94
- * Takes ownership of one reference to the job object.
95
- */
96
-void block_job_start(BlockJob *job);
97
-
98
-/**
99
* block_job_cancel:
100
* @job: The job to be canceled.
101
* @force: Quit a job without waiting for data to be in sync.
102
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
103
index XXXXXXX..XXXXXXX 100644
104
--- a/include/block/blockjob_int.h
105
+++ b/include/block/blockjob_int.h
106
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
107
/** Generic JobDriver callbacks and settings */
108
JobDriver job_driver;
109
110
- /** Mandatory: Entrypoint for the Coroutine. */
111
- CoroutineEntry *start;
112
-
113
/**
114
* Optional callback for job types whose completion must be triggered
115
* manually.
116
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
117
*/
118
void (*clean)(BlockJob *job);
119
120
- /**
121
- * If the callback is not NULL, it will be invoked when the job transitions
122
- * into the paused state. Paused jobs must not perform any asynchronous
123
- * I/O or event loop activity. This callback is used to quiesce jobs.
124
- */
125
- void coroutine_fn (*pause)(BlockJob *job);
126
-
127
- /**
128
- * If the callback is not NULL, it will be invoked when the job transitions
129
- * out of the paused state. Any asynchronous I/O or event loop activity
130
- * should be restarted from this callback.
131
- */
132
- void coroutine_fn (*resume)(BlockJob *job);
133
-
134
/*
135
* If the callback is not NULL, it will be invoked before the job is
136
* resumed in a new AioContext. This is the place to move any resources
137
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job);
138
void block_job_completed(BlockJob *job, int ret);
139
140
/**
141
- * block_job_pause_point:
142
- * @job: The job that is ready to pause.
143
- *
144
- * Pause now if block_job_pause() has been called. Block jobs that perform
145
- * lots of I/O must call this between requests so that the job can be paused.
146
- */
147
-void coroutine_fn block_job_pause_point(BlockJob *job);
148
-
149
-/**
150
* block_job_enter:
151
* @job: The job to enter.
152
*
153
diff --git a/include/qemu/job.h b/include/qemu/job.h
154
index XXXXXXX..XXXXXXX 100644
155
--- a/include/qemu/job.h
156
+++ b/include/qemu/job.h
157
@@ -XXX,XX +XXX,XX @@
158
159
#include "qapi/qapi-types-block-core.h"
160
#include "qemu/queue.h"
161
+#include "qemu/coroutine.h"
162
163
typedef struct JobDriver JobDriver;
164
165
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
166
AioContext *aio_context;
167
168
/**
169
+ * The coroutine that executes the job. If not NULL, it is reentered when
170
+ * busy is false and the job is cancelled.
171
+ */
172
+ Coroutine *co;
173
+
174
+ /**
175
+ * Timer that is used by @block_job_sleep_ns. Accessed under job_mutex (in
176
+ * job.c).
177
+ */
178
+ QEMUTimer sleep_timer;
179
+
180
+ /**
181
+ * Counter for pause request. If non-zero, the block job is either paused,
182
+ * or if busy == true will pause itself as soon as possible.
183
+ */
184
+ int pause_count;
185
+
186
+ /**
187
+ * Set to false by the job while the coroutine has yielded and may be
188
+ * re-entered by block_job_enter(). There may still be I/O or event loop
189
+ * activity pending. Accessed under block_job_mutex (in blockjob.c).
190
+ */
191
+ bool busy;
192
+
193
+ /**
194
+ * Set to true by the job while it is in a quiescent state, where
195
+ * no I/O or event loop activity is pending.
196
+ */
197
+ bool paused;
198
+
199
+ /**
200
* Set to true if the job should cancel itself. The flag must
201
* always be tested just before toggling the busy flag from false
202
* to true. After a job has been cancelled, it should only yield
203
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
204
/** Enum describing the operation */
205
JobType job_type;
206
207
+ /** Mandatory: Entrypoint for the Coroutine. */
208
+ CoroutineEntry *start;
209
+
210
+ /**
211
+ * If the callback is not NULL, it will be invoked when the job transitions
212
+ * into the paused state. Paused jobs must not perform any asynchronous
213
+ * I/O or event loop activity. This callback is used to quiesce jobs.
214
+ */
215
+ void coroutine_fn (*pause)(Job *job);
216
+
217
+ /**
218
+ * If the callback is not NULL, it will be invoked when the job transitions
219
+ * out of the paused state. Any asynchronous I/O or event loop activity
220
+ * should be restarted from this callback.
221
+ */
222
+ void coroutine_fn (*resume)(Job *job);
223
+
224
/** Called when the job is freed */
225
void (*free)(Job *job);
226
};
227
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
228
*/
229
void job_unref(Job *job);
230
231
+/**
232
+ * Conditionally enter the job coroutine if the job is ready to run, not
233
+ * already busy and fn() returns true. fn() is called while under the job_lock
234
+ * critical section.
235
+ */
236
+void job_enter_cond(Job *job, bool(*fn)(Job *job));
237
+
238
+/**
239
+ * @job: A job that has not yet been started.
240
+ *
241
+ * Begins execution of a job.
242
+ * Takes ownership of one reference to the job object.
243
+ */
244
+void job_start(Job *job);
245
+
246
+/**
247
+ * @job: The job that is ready to pause.
248
+ *
249
+ * Pause now if job_pause() has been called. Jobs that perform lots of I/O
250
+ * must call this between requests so that the job can be paused.
251
+ */
252
+void coroutine_fn job_pause_point(Job *job);
253
+
254
+
255
/** Returns the JobType of a given Job. */
256
JobType job_type(const Job *job);
257
258
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
259
260
/* TODO To be removed from the public interface */
261
void job_state_transition(Job *job, JobStatus s1);
262
+void coroutine_fn job_do_yield(Job *job, uint64_t ns);
263
+bool job_should_pause(Job *job);
264
+bool job_started(Job *job);
265
266
#endif
267
diff --git a/block/backup.c b/block/backup.c
268
index XXXXXXX..XXXXXXX 100644
269
--- a/block/backup.c
270
+++ b/block/backup.c
271
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
272
.instance_size = sizeof(BackupBlockJob),
273
.job_type = JOB_TYPE_BACKUP,
274
.free = block_job_free,
275
+ .start = backup_run,
276
},
277
- .start = backup_run,
278
.commit = backup_commit,
279
.abort = backup_abort,
280
.clean = backup_clean,
281
diff --git a/block/commit.c b/block/commit.c
282
index XXXXXXX..XXXXXXX 100644
283
--- a/block/commit.c
284
+++ b/block/commit.c
285
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
286
.instance_size = sizeof(CommitBlockJob),
287
.job_type = JOB_TYPE_COMMIT,
288
.free = block_job_free,
289
+ .start = commit_run,
290
},
291
- .start = commit_run,
292
};
293
294
static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs,
295
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
296
s->on_error = on_error;
297
298
trace_commit_start(bs, base, top, s);
299
- block_job_start(&s->common);
300
+ job_start(&s->common.job);
301
return;
302
303
fail:
304
diff --git a/block/mirror.c b/block/mirror.c
305
index XXXXXXX..XXXXXXX 100644
306
--- a/block/mirror.c
307
+++ b/block/mirror.c
308
@@ -XXX,XX +XXX,XX @@ static void mirror_iteration_done(MirrorOp *op, int ret)
309
g_free(op);
310
311
if (s->waiting_for_io) {
312
- qemu_coroutine_enter(s->common.co);
313
+ qemu_coroutine_enter(s->common.job.co);
314
}
315
}
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;
945
+ }
946
+
947
+ job_lock();
948
+ if (job->busy) {
949
+ job_unlock();
950
+ return;
951
+ }
952
+
953
+ if (fn && !fn(job)) {
954
+ job_unlock();
955
+ return;
956
+ }
957
+
958
+ assert(!job->deferred_to_main_loop);
959
+ timer_del(&job->sleep_timer);
960
+ job->busy = true;
961
+ job_unlock();
962
+ aio_co_wake(job->co);
963
+}
964
+
965
+/* Yield, and schedule a timer to reenter the coroutine after @ns nanoseconds.
966
+ * Reentering the job coroutine with block_job_enter() before the timer has
967
+ * expired is allowed and cancels the timer.
968
+ *
969
+ * If @ns is (uint64_t) -1, no timer is scheduled and block_job_enter() must be
970
+ * called explicitly. */
971
+void coroutine_fn job_do_yield(Job *job, uint64_t ns)
972
+{
973
+ job_lock();
974
+ if (ns != -1) {
975
+ timer_mod(&job->sleep_timer, ns);
976
+ }
977
+ job->busy = false;
978
+ job_unlock();
979
+ qemu_coroutine_yield();
980
+
981
+ /* Set by job_enter_cond() before re-entering the coroutine. */
982
+ assert(job->busy);
983
+}
984
+
985
+void coroutine_fn job_pause_point(Job *job)
986
+{
987
+ assert(job && job_started(job));
988
+
989
+ if (!job_should_pause(job)) {
990
+ return;
991
+ }
992
+ if (job_is_cancelled(job)) {
993
+ return;
994
+ }
995
+
996
+ if (job->driver->pause) {
997
+ job->driver->pause(job);
998
+ }
999
+
1000
+ if (job_should_pause(job) && !job_is_cancelled(job)) {
1001
+ JobStatus status = job->status;
1002
+ job_state_transition(job, status == JOB_STATUS_READY
1003
+ ? JOB_STATUS_STANDBY
1004
+ : JOB_STATUS_PAUSED);
1005
+ job->paused = true;
1006
+ job_do_yield(job, -1);
1007
+ job->paused = false;
1008
+ job_state_transition(job, status);
1009
+ }
1010
+
1011
+ if (job->driver->resume) {
1012
+ job->driver->resume(job);
1013
+ }
1014
+}
1015
+
1016
+/**
1017
+ * All jobs must allow a pause point before entering their job proper. This
1018
+ * ensures that jobs can be paused prior to being started, then resumed later.
1019
+ */
1020
+static void coroutine_fn job_co_entry(void *opaque)
1021
+{
1022
+ Job *job = opaque;
1023
+
1024
+ assert(job && job->driver && job->driver->start);
1025
+ job_pause_point(job);
1026
+ job->driver->start(job);
1027
+}
1028
+
1029
+
1030
+void job_start(Job *job)
1031
+{
1032
+ assert(job && !job_started(job) && job->paused &&
1033
+ job->driver && job->driver->start);
1034
+ job->co = qemu_coroutine_create(job_co_entry, job);
1035
+ job->pause_count--;
1036
+ job->busy = true;
1037
+ job->paused = false;
1038
+ job_state_transition(job, JOB_STATUS_RUNNING);
1039
+ aio_co_enter(job->aio_context, job->co);
1040
+}
1041
+
1042
typedef struct {
1043
Job *job;
1044
JobDeferToMainLoopFn *fn;
1045
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
1046
index XXXXXXX..XXXXXXX 100644
1047
--- a/tests/test-bdrv-drain.c
1048
+++ b/tests/test-bdrv-drain.c
1049
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
1050
.job_driver = {
1051
.instance_size = sizeof(TestBlockJob),
1052
.free = block_job_free,
1053
+ .start = test_job_start,
1054
},
1055
- .start = test_job_start,
1056
.complete = test_job_complete,
1057
};
1058
1059
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
1060
job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL,
1061
0, 0, NULL, NULL, &error_abort);
1062
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
1063
- block_job_start(job);
1064
+ job_start(&job->job);
1065
1066
- g_assert_cmpint(job->pause_count, ==, 0);
1067
- g_assert_false(job->paused);
1068
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1069
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1070
+ g_assert_false(job->job.paused);
1071
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1072
1073
do_drain_begin(drain_type, src);
1074
1075
if (drain_type == BDRV_DRAIN_ALL) {
1076
/* bdrv_drain_all() drains both src and target */
1077
- g_assert_cmpint(job->pause_count, ==, 2);
1078
+ g_assert_cmpint(job->job.pause_count, ==, 2);
1079
} else {
1080
- g_assert_cmpint(job->pause_count, ==, 1);
1081
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1082
}
1083
/* XXX We don't wait until the job is actually paused. Is this okay? */
1084
- /* g_assert_true(job->paused); */
1085
- g_assert_false(job->busy); /* The job is paused */
1086
+ /* g_assert_true(job->job.paused); */
1087
+ g_assert_false(job->job.busy); /* The job is paused */
1088
1089
do_drain_end(drain_type, src);
1090
1091
- g_assert_cmpint(job->pause_count, ==, 0);
1092
- g_assert_false(job->paused);
1093
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1094
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1095
+ g_assert_false(job->job.paused);
1096
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1097
1098
do_drain_begin(drain_type, target);
1099
1100
if (drain_type == BDRV_DRAIN_ALL) {
1101
/* bdrv_drain_all() drains both src and target */
1102
- g_assert_cmpint(job->pause_count, ==, 2);
1103
+ g_assert_cmpint(job->job.pause_count, ==, 2);
1104
} else {
1105
- g_assert_cmpint(job->pause_count, ==, 1);
1106
+ g_assert_cmpint(job->job.pause_count, ==, 1);
1107
}
1108
/* XXX We don't wait until the job is actually paused. Is this okay? */
1109
- /* g_assert_true(job->paused); */
1110
- g_assert_false(job->busy); /* The job is paused */
1111
+ /* g_assert_true(job->job.paused); */
1112
+ g_assert_false(job->job.busy); /* The job is paused */
1113
1114
do_drain_end(drain_type, target);
1115
1116
- g_assert_cmpint(job->pause_count, ==, 0);
1117
- g_assert_false(job->paused);
1118
- g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
1119
+ g_assert_cmpint(job->job.pause_count, ==, 0);
1120
+ g_assert_false(job->job.paused);
1121
+ g_assert_false(job->job.busy); /* We're in block_job_sleep_ns() */
1122
1123
ret = block_job_complete_sync(job, &error_abort);
1124
g_assert_cmpint(ret, ==, 0);
1125
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
1126
index XXXXXXX..XXXXXXX 100644
1127
--- a/tests/test-blockjob-txn.c
1128
+++ b/tests/test-blockjob-txn.c
1129
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
1130
.job_driver = {
1131
.instance_size = sizeof(TestBlockJob),
1132
.free = block_job_free,
1133
+ .start = test_block_job_run,
1134
},
1135
- .start = test_block_job_run,
1136
};
1137
1138
/* Create a block job that completes with a given return code after a given
1139
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
1140
1141
txn = block_job_txn_new();
1142
job = test_block_job_start(1, true, expected, &result, txn);
1143
- block_job_start(job);
1144
+ job_start(&job->job);
1145
1146
if (expected == -ECANCELED) {
1147
block_job_cancel(job, false);
1148
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
1149
txn = block_job_txn_new();
1150
job1 = test_block_job_start(1, true, expected1, &result1, txn);
1151
job2 = test_block_job_start(2, true, expected2, &result2, txn);
1152
- block_job_start(job1);
1153
- block_job_start(job2);
1154
+ job_start(&job1->job);
1155
+ job_start(&job2->job);
1156
1157
/* Release our reference now to trigger as many nice
1158
* use-after-free bugs as possible.
1159
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
1160
txn = block_job_txn_new();
1161
job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
1162
job2 = test_block_job_start(2, false, 0, &result2, txn);
1163
- block_job_start(job1);
1164
- block_job_start(job2);
1165
+ job_start(&job1->job);
1166
+ job_start(&job2->job);
1167
1168
block_job_cancel(job1, false);
1169
1170
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
1171
index XXXXXXX..XXXXXXX 100644
1172
--- a/tests/test-blockjob.c
1173
+++ b/tests/test-blockjob.c
1174
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
1175
.job_driver = {
1176
.instance_size = sizeof(CancelJob),
1177
.free = block_job_free,
1178
+ .start = cancel_job_start,
1179
},
1180
- .start = cancel_job_start,
1181
.complete = cancel_job_complete,
1182
};
1183
1184
@@ -XXX,XX +XXX,XX @@ static void test_cancel_running(void)
1185
1186
s = create_common(&job);
1187
1188
- block_job_start(job);
1189
+ job_start(&job->job);
1190
assert(job->job.status == JOB_STATUS_RUNNING);
1191
1192
cancel_common(s);
1193
@@ -XXX,XX +XXX,XX @@ static void test_cancel_paused(void)
1194
1195
s = create_common(&job);
1196
1197
- block_job_start(job);
1198
+ job_start(&job->job);
1199
assert(job->job.status == JOB_STATUS_RUNNING);
1200
1201
block_job_user_pause(job, &error_abort);
1202
@@ -XXX,XX +XXX,XX @@ static void test_cancel_ready(void)
1203
1204
s = create_common(&job);
1205
1206
- block_job_start(job);
1207
+ job_start(&job->job);
1208
assert(job->job.status == JOB_STATUS_RUNNING);
1209
1210
s->should_converge = true;
1211
@@ -XXX,XX +XXX,XX @@ static void test_cancel_standby(void)
1212
1213
s = create_common(&job);
1214
1215
- block_job_start(job);
1216
+ job_start(&job->job);
1217
assert(job->job.status == JOB_STATUS_RUNNING);
1218
1219
s->should_converge = true;
1220
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
1221
1222
s = create_common(&job);
1223
1224
- block_job_start(job);
1225
+ job_start(&job->job);
1226
assert(job->job.status == JOB_STATUS_RUNNING);
1227
1228
s->should_converge = true;
1229
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
1230
1231
s = create_common(&job);
1232
1233
- block_job_start(job);
1234
+ job_start(&job->job);
1235
assert(job->job.status == JOB_STATUS_RUNNING);
1236
1237
s->should_converge = true;
1238
--
1239
2.13.6
1240
1241
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
While we already moved the state related to job pausing to Job, the
2
functions to do were still BlockJob only. This commit moves them over to
3
Job.
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 | 32 -----------------
10
include/block/blockjob_int.h | 7 ++++
11
include/qemu/job.h | 37 ++++++++++++++++++++
12
block/backup.c | 1 +
13
block/commit.c | 1 +
14
block/mirror.c | 2 ++
15
block/stream.c | 1 +
16
blockdev.c | 6 ++--
17
blockjob.c | 81 +++++++++-----------------------------------
18
job.c | 59 ++++++++++++++++++++++++++++++++
19
tests/test-bdrv-drain.c | 1 +
20
tests/test-blockjob-txn.c | 1 +
21
tests/test-blockjob.c | 6 ++--
22
13 files changed, 133 insertions(+), 102 deletions(-)
23
24
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
25
index XXXXXXX..XXXXXXX 100644
26
--- a/include/block/blockjob.h
27
+++ b/include/block/blockjob.h
28
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
29
bool force;
30
31
/**
32
- * Set to true if the job is paused by user. Can be unpaused with the
33
- * block-job-resume QMP command.
34
- */
35
- bool user_paused;
36
-
37
- /**
38
* Set to true when the job is ready to be completed.
39
*/
40
bool ready;
41
@@ -XXX,XX +XXX,XX @@ void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
42
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
43
44
/**
45
- * block_job_user_pause:
46
- * @job: The job to be paused.
47
- *
48
- * Asynchronously pause the specified job.
49
- * Do not allow a resume until a matching call to block_job_user_resume.
50
- */
51
-void block_job_user_pause(BlockJob *job, Error **errp);
52
-
53
-/**
54
- * block_job_paused:
55
- * @job: The job to query.
56
- *
57
- * Returns true if the job is user-paused.
58
- */
59
-bool block_job_user_paused(BlockJob *job);
60
-
61
-/**
62
- * block_job_user_resume:
63
- * @job: The job to be resumed.
64
- *
65
- * Resume the specified job.
66
- * Must be paired with a preceding block_job_user_pause.
67
- */
68
-void block_job_user_resume(BlockJob *job, Error **errp);
69
-
70
-/**
71
* block_job_user_cancel:
72
* @job: The job to be cancelled.
73
* @force: Quit a job without waiting for data to be in sync.
74
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
75
index XXXXXXX..XXXXXXX 100644
76
--- a/include/block/blockjob_int.h
77
+++ b/include/block/blockjob_int.h
78
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
79
void block_job_free(Job *job);
80
81
/**
82
+ * block_job_user_resume:
83
+ * Callback to be used for JobDriver.user_resume in all block jobs. Resets the
84
+ * iostatus when the user resumes @job.
85
+ */
86
+void block_job_user_resume(Job *job);
87
+
88
+/**
89
* block_job_yield:
90
* @job: The job that calls the function.
91
*
92
diff --git a/include/qemu/job.h b/include/qemu/job.h
93
index XXXXXXX..XXXXXXX 100644
94
--- a/include/qemu/job.h
95
+++ b/include/qemu/job.h
96
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
97
bool paused;
98
99
/**
100
+ * Set to true if the job is paused by user. Can be unpaused with the
101
+ * block-job-resume QMP command.
102
+ */
103
+ bool user_paused;
104
+
105
+ /**
106
* Set to true if the job should cancel itself. The flag must
107
* always be tested just before toggling the busy flag from false
108
* to true. After a job has been cancelled, it should only yield
109
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
110
*/
111
void coroutine_fn (*resume)(Job *job);
112
113
+ /**
114
+ * Called when the job is resumed by the user (i.e. user_paused becomes
115
+ * false). .user_resume is called before .resume.
116
+ */
117
+ void (*user_resume)(Job *job);
118
+
119
/** Called when the job is freed */
120
void (*free)(Job *job);
121
};
122
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
123
bool job_is_cancelled(Job *job);
124
125
/**
126
+ * Request @job to pause at the next pause point. Must be paired with
127
+ * job_resume(). If the job is supposed to be resumed by user action, call
128
+ * job_user_pause() instead.
129
+ */
130
+void job_pause(Job *job);
131
+
132
+/** Resumes a @job paused with job_pause. */
133
+void job_resume(Job *job);
134
+
135
+/**
136
+ * Asynchronously pause the specified @job.
137
+ * Do not allow a resume until a matching call to job_user_resume.
138
+ */
139
+void job_user_pause(Job *job, Error **errp);
140
+
141
+/** Returns true if the job is user-paused. */
142
+bool job_user_paused(Job *job);
143
+
144
+/**
145
+ * Resume the specified @job.
146
+ * Must be paired with a preceding job_user_pause.
147
+ */
148
+void job_user_resume(Job *job, Error **errp);
149
+
150
+/**
151
* Get the next element from the list of block jobs after @job, or the
152
* first one if @job is %NULL.
153
*
154
diff --git a/block/backup.c b/block/backup.c
155
index XXXXXXX..XXXXXXX 100644
156
--- a/block/backup.c
157
+++ b/block/backup.c
158
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
159
.instance_size = sizeof(BackupBlockJob),
160
.job_type = JOB_TYPE_BACKUP,
161
.free = block_job_free,
162
+ .user_resume = block_job_user_resume,
163
.start = backup_run,
164
},
165
.commit = backup_commit,
166
diff --git a/block/commit.c b/block/commit.c
167
index XXXXXXX..XXXXXXX 100644
168
--- a/block/commit.c
169
+++ b/block/commit.c
170
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
171
.instance_size = sizeof(CommitBlockJob),
172
.job_type = JOB_TYPE_COMMIT,
173
.free = block_job_free,
174
+ .user_resume = block_job_user_resume,
175
.start = commit_run,
176
},
177
};
178
diff --git a/block/mirror.c b/block/mirror.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/block/mirror.c
181
+++ b/block/mirror.c
182
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
183
.instance_size = sizeof(MirrorBlockJob),
184
.job_type = JOB_TYPE_MIRROR,
185
.free = block_job_free,
186
+ .user_resume = block_job_user_resume,
187
.start = mirror_run,
188
.pause = mirror_pause,
189
},
190
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
191
.instance_size = sizeof(MirrorBlockJob),
192
.job_type = JOB_TYPE_COMMIT,
193
.free = block_job_free,
194
+ .user_resume = block_job_user_resume,
195
.start = mirror_run,
196
.pause = mirror_pause,
197
},
198
diff --git a/block/stream.c b/block/stream.c
199
index XXXXXXX..XXXXXXX 100644
200
--- a/block/stream.c
201
+++ b/block/stream.c
202
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
203
.job_type = JOB_TYPE_STREAM,
204
.free = block_job_free,
205
.start = stream_run,
206
+ .user_resume = block_job_user_resume,
207
},
208
};
209
210
diff --git a/blockdev.c b/blockdev.c
211
index XXXXXXX..XXXXXXX 100644
212
--- a/blockdev.c
213
+++ b/blockdev.c
214
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
215
force = false;
216
}
217
218
- if (block_job_user_paused(job) && !force) {
219
+ if (job_user_paused(&job->job) && !force) {
220
error_setg(errp, "The block job for device '%s' is currently paused",
221
device);
222
goto out;
223
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_pause(const char *device, Error **errp)
224
}
225
226
trace_qmp_block_job_pause(job);
227
- block_job_user_pause(job, errp);
228
+ job_user_pause(&job->job, errp);
229
aio_context_release(aio_context);
230
}
231
232
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_resume(const char *device, Error **errp)
233
}
234
235
trace_qmp_block_job_resume(job);
236
- block_job_user_resume(job, errp);
237
+ job_user_resume(&job->job, errp);
238
aio_context_release(aio_context);
239
}
240
241
diff --git a/blockjob.c b/blockjob.c
242
index XXXXXXX..XXXXXXX 100644
243
--- a/blockjob.c
244
+++ b/blockjob.c
245
@@ -XXX,XX +XXX,XX @@ static void block_job_txn_del_job(BlockJob *job)
246
}
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
--
532
2.13.6
533
534
diff view generated by jsdifflib
Deleted patch
1
Since we introduced an explicit status to block job, BlockJob.completed
2
is redundant because it can be derived from the status. Remove the field
3
from BlockJob and add a function to derive it from the status at the Job
4
level.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
---
10
include/block/blockjob.h | 3 ---
11
include/qemu/job.h | 3 +++
12
blockjob.c | 16 +++++++---------
13
job.c | 22 ++++++++++++++++++++++
14
qemu-img.c | 4 ++--
15
5 files changed, 34 insertions(+), 14 deletions(-)
16
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
20
+++ b/include/block/blockjob.h
21
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
22
/** The opaque value that is passed to the completion function. */
23
void *opaque;
24
25
- /** True when job has reported completion by calling block_job_completed. */
26
- bool completed;
27
-
28
/** ret code passed to block_job_completed. */
29
int ret;
30
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/qemu/job.h
34
+++ b/include/qemu/job.h
35
@@ -XXX,XX +XXX,XX @@ const char *job_type_str(const Job *job);
36
/** Returns whether the job is scheduled for cancellation. */
37
bool job_is_cancelled(Job *job);
38
39
+/** Returns whether the job is in a completed state. */
40
+bool job_is_completed(Job *job);
41
+
42
/**
43
* Request @job to pause at the next pause point. Must be paired with
44
* job_resume(). If the job is supposed to be resumed by user action, call
45
diff --git a/blockjob.c b/blockjob.c
46
index XXXXXXX..XXXXXXX 100644
47
--- a/blockjob.c
48
+++ b/blockjob.c
49
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
50
51
job_pause(&job->job);
52
53
- while (!job->job.paused && !job->completed) {
54
+ while (!job->job.paused && !job_is_completed(&job->job)) {
55
block_job_drain(job);
56
}
57
58
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
59
static void block_job_decommission(BlockJob *job)
60
{
61
assert(job);
62
- job->completed = true;
63
job->job.busy = false;
64
job->job.paused = false;
65
job->job.deferred_to_main_loop = true;
66
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
67
68
static int block_job_finalize_single(BlockJob *job)
69
{
70
- assert(job->completed);
71
+ assert(job_is_completed(&job->job));
72
73
/* Ensure abort is called for late-transactional failures */
74
block_job_update_rc(job);
75
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
76
/* block_job_drain calls block_job_enter, and it should be enough to
77
* induce progress until the job completes or moves to the main thread.
78
*/
79
- while (!job->job.deferred_to_main_loop && !job->completed) {
80
+ while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
81
block_job_drain(job);
82
}
83
- while (!job->completed) {
84
+ while (!job_is_completed(&job->job)) {
85
aio_poll(qemu_get_aio_context(), true);
86
}
87
ret = (job_is_cancelled(&job->job) && job->ret == 0)
88
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
89
while (!QLIST_EMPTY(&txn->jobs)) {
90
other_job = QLIST_FIRST(&txn->jobs);
91
ctx = blk_get_aio_context(other_job->blk);
92
- if (!other_job->completed) {
93
+ if (!job_is_completed(&other_job->job)) {
94
assert(job_is_cancelled(&other_job->job));
95
block_job_finish_sync(other_job, NULL, NULL);
96
}
97
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
98
* txn.
99
*/
100
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
101
- if (!other_job->completed) {
102
+ if (!job_is_completed(&other_job->job)) {
103
return;
104
}
105
assert(other_job->ret == 0);
106
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job)
107
108
void block_job_completed(BlockJob *job, int ret)
109
{
110
- assert(job && job->txn && !job->completed);
111
+ assert(job && job->txn && !job_is_completed(&job->job));
112
assert(blk_bs(job->blk)->job == job);
113
- job->completed = true;
114
job->ret = ret;
115
block_job_update_rc(job);
116
trace_block_job_completed(job, ret, job->ret);
117
diff --git a/job.c b/job.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/job.c
120
+++ b/job.c
121
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
122
return job->cancelled;
123
}
124
125
+bool job_is_completed(Job *job)
126
+{
127
+ switch (job->status) {
128
+ case JOB_STATUS_UNDEFINED:
129
+ case JOB_STATUS_CREATED:
130
+ case JOB_STATUS_RUNNING:
131
+ case JOB_STATUS_PAUSED:
132
+ case JOB_STATUS_READY:
133
+ case JOB_STATUS_STANDBY:
134
+ return false;
135
+ case JOB_STATUS_WAITING:
136
+ case JOB_STATUS_PENDING:
137
+ case JOB_STATUS_ABORTING:
138
+ case JOB_STATUS_CONCLUDED:
139
+ case JOB_STATUS_NULL:
140
+ return true;
141
+ default:
142
+ g_assert_not_reached();
143
+ }
144
+ return false;
145
+}
146
+
147
bool job_started(Job *job)
148
{
149
return job->co;
150
diff --git a/qemu-img.c b/qemu-img.c
151
index XXXXXXX..XXXXXXX 100644
152
--- a/qemu-img.c
153
+++ b/qemu-img.c
154
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
155
aio_poll(aio_context, true);
156
qemu_progress_print(job->len ?
157
((float)job->offset / job->len * 100.f) : 0.0f, 0);
158
- } while (!job->ready && !job->completed);
159
+ } while (!job->ready && !job_is_completed(&job->job));
160
161
- if (!job->completed) {
162
+ if (!job_is_completed(&job->job)) {
163
ret = block_job_complete_sync(job, errp);
164
} else {
165
ret = job->ret;
166
--
167
2.13.6
168
169
diff view generated by jsdifflib
Deleted patch
1
This renames the BlockJobCreateFlags constants, moves a few JOB_INTERNAL
2
checks to job_create() and the auto_{finalize,dismiss} fields from
3
BlockJob to Job.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
8
include/block/blockjob.h | 17 -----------------
9
include/block/blockjob_int.h | 3 +--
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
23
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob.h
26
+++ b/include/block/blockjob.h
27
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
28
/** ret code passed to block_job_completed. */
29
int ret;
30
31
- /** True if this job should automatically finalize itself */
32
- bool auto_finalize;
33
-
34
- /** True if this job should automatically dismiss itself */
35
- bool auto_dismiss;
36
-
37
BlockJobTxn *txn;
38
QLIST_ENTRY(BlockJob) txn_list;
39
} BlockJob;
40
41
-typedef enum BlockJobCreateFlags {
42
- /* Default behavior */
43
- BLOCK_JOB_DEFAULT = 0x00,
44
- /* BlockJob is not QMP-created and should not send QMP events */
45
- BLOCK_JOB_INTERNAL = 0x01,
46
- /* BlockJob requires manual finalize step */
47
- BLOCK_JOB_MANUAL_FINALIZE = 0x02,
48
- /* BlockJob requires manual dismiss step */
49
- BLOCK_JOB_MANUAL_DISMISS = 0x04,
50
-} BlockJobCreateFlags;
51
-
52
/**
53
* block_job_next:
54
* @job: A block job, or %NULL.
55
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
56
index XXXXXXX..XXXXXXX 100644
57
--- a/include/block/blockjob_int.h
58
+++ b/include/block/blockjob_int.h
59
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
60
* @bs: The block
61
* @perm, @shared_perm: Permissions to request for @bs
62
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
63
- * @flags: Creation flags for the Block Job.
64
- * See @BlockJobCreateFlags
65
+ * @flags: Creation flags for the Block Job. See @JobCreateFlags.
66
* @cb: Completion function for the job.
67
* @opaque: Opaque pointer value passed to @cb.
68
* @errp: Error object.
69
diff --git a/include/qemu/job.h b/include/qemu/job.h
70
index XXXXXXX..XXXXXXX 100644
71
--- a/include/qemu/job.h
72
+++ b/include/qemu/job.h
73
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
74
/** Set to true when the job has deferred work to the main loop. */
75
bool deferred_to_main_loop;
76
77
+ /** True if this job should automatically finalize itself */
78
+ bool auto_finalize;
79
+
80
+ /** True if this job should automatically dismiss itself */
81
+ bool auto_dismiss;
82
+
83
/** Element of the list of jobs */
84
QLIST_ENTRY(Job) job_list;
85
} Job;
86
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
87
void (*free)(Job *job);
88
};
89
90
+typedef enum JobCreateFlags {
91
+ /* Default behavior */
92
+ JOB_DEFAULT = 0x00,
93
+ /* Job is not QMP-created and should not send QMP events */
94
+ JOB_INTERNAL = 0x01,
95
+ /* Job requires manual finalize step */
96
+ JOB_MANUAL_FINALIZE = 0x02,
97
+ /* Job requires manual dismiss step */
98
+ JOB_MANUAL_DISMISS = 0x04,
99
+} JobCreateFlags;
100
+
101
102
/**
103
* Create a new long-running job and return it.
104
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
105
* @job_id: The id of the newly-created job, or %NULL for internal jobs
106
* @driver: The class object for the newly-created job.
107
* @ctx: The AioContext to run the job coroutine in.
108
+ * @flags: Creation flags for the job. See @JobCreateFlags.
109
* @errp: Error object.
110
*/
111
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
112
- Error **errp);
113
+ int flags, Error **errp);
114
115
/**
116
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
117
diff --git a/block/commit.c b/block/commit.c
118
index XXXXXXX..XXXXXXX 100644
119
--- a/block/commit.c
120
+++ b/block/commit.c
121
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
122
}
123
124
s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
125
- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
126
+ speed, JOB_DEFAULT, NULL, NULL, errp);
127
if (!s) {
128
return;
129
}
130
diff --git a/block/mirror.c b/block/mirror.c
131
index XXXXXXX..XXXXXXX 100644
132
--- a/block/mirror.c
133
+++ b/block/mirror.c
134
@@ -XXX,XX +XXX,XX @@ void mirror_start(const char *job_id, BlockDriverState *bs,
135
}
136
is_none_mode = mode == MIRROR_SYNC_MODE_NONE;
137
base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL;
138
- mirror_start_job(job_id, bs, BLOCK_JOB_DEFAULT, target, replaces,
139
+ mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces,
140
speed, granularity, buf_size, backing_mode,
141
on_source_error, on_target_error, unmap, NULL, NULL,
142
&mirror_job_driver, is_none_mode, base, false,
143
diff --git a/block/replication.c b/block/replication.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/replication.c
146
+++ b/block/replication.c
147
@@ -XXX,XX +XXX,XX @@ static void replication_start(ReplicationState *rs, ReplicationMode mode,
148
job = backup_job_create(NULL, s->secondary_disk->bs, s->hidden_disk->bs,
149
0, MIRROR_SYNC_MODE_NONE, NULL, false,
150
BLOCKDEV_ON_ERROR_REPORT,
151
- BLOCKDEV_ON_ERROR_REPORT, BLOCK_JOB_INTERNAL,
152
+ BLOCKDEV_ON_ERROR_REPORT, JOB_INTERNAL,
153
backup_job_completed, bs, NULL, &local_err);
154
if (local_err) {
155
error_propagate(errp, local_err);
156
@@ -XXX,XX +XXX,XX @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp)
157
158
s->stage = BLOCK_REPLICATION_FAILOVER;
159
commit_active_start(NULL, s->active_disk->bs, s->secondary_disk->bs,
160
- BLOCK_JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
161
+ JOB_INTERNAL, 0, BLOCKDEV_ON_ERROR_REPORT,
162
NULL, replication_done, bs, true, errp);
163
break;
164
default:
165
diff --git a/block/stream.c b/block/stream.c
166
index XXXXXXX..XXXXXXX 100644
167
--- a/block/stream.c
168
+++ b/block/stream.c
169
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
170
BLK_PERM_GRAPH_MOD,
171
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
172
BLK_PERM_WRITE,
173
- speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
174
+ speed, JOB_DEFAULT, NULL, NULL, errp);
175
if (!s) {
176
goto fail;
177
}
178
diff --git a/blockdev.c b/blockdev.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/blockdev.c
181
+++ b/blockdev.c
182
@@ -XXX,XX +XXX,XX @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device,
183
goto out;
184
}
185
commit_active_start(has_job_id ? job_id : NULL, bs, base_bs,
186
- BLOCK_JOB_DEFAULT, speed, on_error,
187
+ JOB_DEFAULT, speed, on_error,
188
filter_node_name, NULL, NULL, false, &local_err);
189
} else {
190
BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs);
191
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
192
AioContext *aio_context;
193
QDict *options = NULL;
194
Error *local_err = NULL;
195
- int flags, job_flags = BLOCK_JOB_DEFAULT;
196
+ int flags, job_flags = JOB_DEFAULT;
197
int64_t size;
198
bool set_backing_hd = false;
199
200
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
201
}
202
}
203
if (!backup->auto_finalize) {
204
- job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
205
+ job_flags |= JOB_MANUAL_FINALIZE;
206
}
207
if (!backup->auto_dismiss) {
208
- job_flags |= BLOCK_JOB_MANUAL_DISMISS;
209
+ job_flags |= JOB_MANUAL_DISMISS;
210
}
211
212
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
213
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
214
Error *local_err = NULL;
215
AioContext *aio_context;
216
BlockJob *job = NULL;
217
- int job_flags = BLOCK_JOB_DEFAULT;
218
+ int job_flags = JOB_DEFAULT;
219
220
if (!backup->has_speed) {
221
backup->speed = 0;
222
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
223
}
224
}
225
if (!backup->auto_finalize) {
226
- job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
227
+ job_flags |= JOB_MANUAL_FINALIZE;
228
}
229
if (!backup->auto_dismiss) {
230
- job_flags |= BLOCK_JOB_MANUAL_DISMISS;
231
+ job_flags |= JOB_MANUAL_DISMISS;
232
}
233
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
234
backup->sync, NULL, backup->compress,
235
diff --git a/blockjob.c b/blockjob.c
236
index XXXXXXX..XXXXXXX 100644
237
--- a/blockjob.c
238
+++ b/blockjob.c
239
@@ -XXX,XX +XXX,XX @@ static void block_job_do_dismiss(BlockJob *job)
240
static void block_job_conclude(BlockJob *job)
241
{
242
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
243
- if (job->auto_dismiss || !job_started(&job->job)) {
244
+ if (job->job.auto_dismiss || !job_started(&job->job)) {
245
block_job_do_dismiss(job);
246
}
247
}
248
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
249
250
static int block_job_needs_finalize(BlockJob *job)
251
{
252
- return !job->auto_finalize;
253
+ return !job->job.auto_finalize;
254
}
255
256
static void block_job_do_finalize(BlockJob *job)
257
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
258
info->io_status = job->iostatus;
259
info->ready = job->ready;
260
info->status = job->job.status;
261
- info->auto_finalize = job->auto_finalize;
262
- info->auto_dismiss = job->auto_dismiss;
263
+ info->auto_finalize = job->job.auto_finalize;
264
+ info->auto_dismiss = job->job.auto_dismiss;
265
info->has_error = job->ret != 0;
266
info->error = job->ret ? g_strdup(strerror(-job->ret)) : NULL;
267
return info;
268
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
269
static int block_job_event_pending(BlockJob *job)
270
{
271
job_state_transition(&job->job, JOB_STATUS_PENDING);
272
- if (!job->auto_finalize && !block_job_is_internal(job)) {
273
+ if (!job->job.auto_finalize && !block_job_is_internal(job)) {
274
qapi_event_send_block_job_pending(job_type(&job->job),
275
job->job.id,
276
&error_abort);
277
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
278
return NULL;
279
}
280
281
- if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) {
282
+ if (job_id == NULL && !(flags & JOB_INTERNAL)) {
283
job_id = bdrv_get_device_name(bs);
284
- if (!*job_id) {
285
- error_setg(errp, "An explicit job ID is required for this node");
286
- return NULL;
287
- }
288
- }
289
-
290
- if (job_id) {
291
- if (flags & BLOCK_JOB_INTERNAL) {
292
- error_setg(errp, "Cannot specify job ID for internal block job");
293
- return NULL;
294
- }
295
}
296
297
blk = blk_new(perm, shared_perm);
298
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
299
}
300
301
job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
302
- errp);
303
+ flags, errp);
304
if (job == NULL) {
305
blk_unref(blk);
306
return NULL;
307
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
308
job->blk = blk;
309
job->cb = cb;
310
job->opaque = opaque;
311
- job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
312
- job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
313
314
error_setg(&job->blocker, "block device is in use by block job: %s",
315
job_type_str(&job->job));
316
diff --git a/job.c b/job.c
317
index XXXXXXX..XXXXXXX 100644
318
--- a/job.c
319
+++ b/job.c
320
@@ -XXX,XX +XXX,XX @@ static void job_sleep_timer_cb(void *opaque)
321
}
322
323
void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
324
- Error **errp)
325
+ int flags, Error **errp)
326
{
327
Job *job;
328
329
if (job_id) {
330
+ if (flags & JOB_INTERNAL) {
331
+ error_setg(errp, "Cannot specify job ID for internal job");
332
+ return NULL;
333
+ }
334
if (!id_wellformed(job_id)) {
335
error_setg(errp, "Invalid job ID '%s'", job_id);
336
return NULL;
337
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
338
error_setg(errp, "Job ID '%s' already in use", job_id);
339
return NULL;
340
}
341
+ } else if (!(flags & JOB_INTERNAL)) {
342
+ error_setg(errp, "An explicit job ID is required");
343
+ return NULL;
344
}
345
346
job = g_malloc0(driver->instance_size);
347
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
348
job->busy = false;
349
job->paused = true;
350
job->pause_count = 1;
351
+ job->auto_finalize = !(flags & JOB_MANUAL_FINALIZE);
352
+ job->auto_dismiss = !(flags & JOB_MANUAL_DISMISS);
353
354
job_state_transition(job, JOB_STATUS_CREATED);
355
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
356
diff --git a/qemu-img.c b/qemu-img.c
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
--
405
2.13.6
406
407
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
Deleted patch
1
Go through the Job layer in order to send QMP events. For the moment,
2
these functions only call a notifier in the BlockJob layer that sends
3
the existing commands.
4
1
5
This uses notifiers rather than JobDriver callbacks because internal
6
users of jobs won't receive QMP events, but might still be interested
7
in getting notified for the events.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
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
20
--- a/include/block/blockjob.h
21
+++ b/include/block/blockjob.h
22
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
23
/** Block other operations when block job is running */
24
Error *blocker;
25
26
+ /** Called when a cancelled job is finalised. */
27
+ Notifier finalize_cancelled_notifier;
28
+
29
+ /** Called when a successfully completed job is finalised. */
30
+ Notifier finalize_completed_notifier;
31
+
32
+ /** Called when the job transitions to PENDING */
33
+ Notifier pending_notifier;
34
+
35
/** BlockDriverStates that are involved in this block job */
36
GSList *nodes;
37
38
diff --git a/include/qemu/job.h b/include/qemu/job.h
39
index XXXXXXX..XXXXXXX 100644
40
--- a/include/qemu/job.h
41
+++ b/include/qemu/job.h
42
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
43
/** True if this job should automatically dismiss itself */
44
bool auto_dismiss;
45
46
+ /** Notifiers called when a cancelled job is finalised */
47
+ NotifierList on_finalize_cancelled;
48
+
49
+ /** Notifiers called when a successfully completed job is finalised */
50
+ NotifierList on_finalize_completed;
51
+
52
+ /** Notifiers called when the job transitions to PENDING */
53
+ NotifierList on_pending;
54
+
55
/** Element of the list of jobs */
56
QLIST_ENTRY(Job) job_list;
57
} Job;
58
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
59
*/
60
void job_unref(Job *job);
61
62
+/** To be called when a cancelled job is finalised. */
63
+void job_event_cancelled(Job *job);
64
+
65
+/** To be called when a successfully completed job is finalised. */
66
+void job_event_completed(Job *job);
67
+
68
+/** To be called when the job transitions to PENDING */
69
+void job_event_pending(Job *job);
70
+
71
/**
72
* Conditionally enter the job coroutine if the job is ready to run, not
73
* already busy and fn() returns true. fn() is called while under the job_lock
74
diff --git a/blockjob.c b/blockjob.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/blockjob.c
77
+++ b/blockjob.c
78
@@ -XXX,XX +XXX,XX @@
79
#include "qemu/coroutine.h"
80
#include "qemu/timer.h"
81
82
-static void block_job_event_cancelled(BlockJob *job);
83
-static void block_job_event_completed(BlockJob *job, const char *msg);
84
-static void block_job_event_pending(BlockJob *job);
85
-
86
/* Transactional group of block jobs */
87
struct BlockJobTxn {
88
89
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
90
/* Emit events only if we actually started */
91
if (job_started(&job->job)) {
92
if (job_is_cancelled(&job->job)) {
93
- block_job_event_cancelled(job);
94
+ job_event_cancelled(&job->job);
95
} else {
96
- const char *msg = NULL;
97
- if (job->ret < 0) {
98
- msg = strerror(-job->ret);
99
- }
100
- block_job_event_completed(job, msg);
101
+ job_event_completed(&job->job);
102
}
103
}
104
105
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
106
{
107
job_state_transition(&job->job, JOB_STATUS_PENDING);
108
if (!job->job.auto_finalize) {
109
- block_job_event_pending(job);
110
+ job_event_pending(&job->job);
111
}
112
return 0;
113
}
114
@@ -XXX,XX +XXX,XX @@ static void block_job_iostatus_set_err(BlockJob *job, int error)
115
}
116
}
117
118
-static void block_job_event_cancelled(BlockJob *job)
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
--
214
2.13.6
215
216
diff view generated by jsdifflib
Deleted patch
1
This moves the finalisation of a single job from BlockJob to Job.
2
1
3
Some part of this code depends on job transactions, and job transactions
4
call this code, we introduce some temporary calls from Job functions to
5
BlockJob ones. This will be fixed once transactions move to Job, too.
6
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
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
24
--- a/include/block/blockjob.h
25
+++ b/include/block/blockjob.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
27
/** Rate limiting data structure for implementing @speed. */
28
RateLimit limit;
29
30
- /** The completion function that will be called when the job completes. */
31
- BlockCompletionFunc *cb;
32
-
33
/** Block other operations when block job is running */
34
Error *blocker;
35
36
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
37
/** BlockDriverStates that are involved in this block job */
38
GSList *nodes;
39
40
- /** The opaque value that is passed to the completion function. */
41
- void *opaque;
42
-
43
- /** ret code passed to block_job_completed. */
44
- int ret;
45
-
46
BlockJobTxn *txn;
47
QLIST_ENTRY(BlockJob) txn_list;
48
} BlockJob;
49
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
50
index XXXXXXX..XXXXXXX 100644
51
--- a/include/block/blockjob_int.h
52
+++ b/include/block/blockjob_int.h
53
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
54
*/
55
int (*prepare)(BlockJob *job);
56
57
- /**
58
- * If the callback is not NULL, it will be invoked when all the jobs
59
- * belonging to the same transaction complete; or upon this job's
60
- * completion if it is not in a transaction. Skipped if NULL.
61
- *
62
- * All jobs will complete with a call to either .commit() or .abort() but
63
- * never both.
64
- */
65
- void (*commit)(BlockJob *job);
66
-
67
- /**
68
- * If the callback is not NULL, it will be invoked when any job in the
69
- * same transaction fails; or upon this job's failure (due to error or
70
- * cancellation) if it is not in a transaction. Skipped if NULL.
71
- *
72
- * All jobs will complete with a call to either .commit() or .abort() but
73
- * never both.
74
- */
75
- void (*abort)(BlockJob *job);
76
-
77
- /**
78
- * If the callback is not NULL, it will be invoked after a call to either
79
- * .commit() or .abort(). Regardless of which callback is invoked after
80
- * completion, .clean() will always be called, even if the job does not
81
- * belong to a transaction group.
82
- */
83
- void (*clean)(BlockJob *job);
84
-
85
/*
86
* If the callback is not NULL, it will be invoked before the job is
87
* resumed in a new AioContext. This is the place to move any resources
88
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job);
89
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
90
91
/**
92
- * block_job_early_fail:
93
- * @bs: The block device.
94
- *
95
- * The block job could not be started, free it.
96
- */
97
-void block_job_early_fail(BlockJob *job);
98
-
99
-/**
100
* block_job_completed:
101
* @job: The job being completed.
102
* @ret: The status code.
103
diff --git a/include/qemu/job.h b/include/qemu/job.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/qemu/job.h
106
+++ b/include/qemu/job.h
107
@@ -XXX,XX +XXX,XX @@
108
#include "qapi/qapi-types-block-core.h"
109
#include "qemu/queue.h"
110
#include "qemu/coroutine.h"
111
+#include "block/aio.h"
112
113
typedef struct JobDriver JobDriver;
114
115
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
116
/** True if this job should automatically dismiss itself */
117
bool auto_dismiss;
118
119
+ /** ret code passed to block_job_completed. */
120
+ int ret;
121
+
122
+ /** The completion function that will be called when the job completes. */
123
+ BlockCompletionFunc *cb;
124
+
125
+ /** The opaque value that is passed to the completion function. */
126
+ void *opaque;
127
+
128
/** Notifiers called when a cancelled job is finalised */
129
NotifierList on_finalize_cancelled;
130
131
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
132
*/
133
void (*user_resume)(Job *job);
134
135
+ /**
136
+ * If the callback is not NULL, it will be invoked when all the jobs
137
+ * belonging to the same transaction complete; or upon this job's
138
+ * completion if it is not in a transaction. Skipped if NULL.
139
+ *
140
+ * All jobs will complete with a call to either .commit() or .abort() but
141
+ * never both.
142
+ */
143
+ void (*commit)(Job *job);
144
+
145
+ /**
146
+ * If the callback is not NULL, it will be invoked when any job in the
147
+ * same transaction fails; or upon this job's failure (due to error or
148
+ * cancellation) if it is not in a transaction. Skipped if NULL.
149
+ *
150
+ * All jobs will complete with a call to either .commit() or .abort() but
151
+ * never both.
152
+ */
153
+ void (*abort)(Job *job);
154
+
155
+ /**
156
+ * If the callback is not NULL, it will be invoked after a call to either
157
+ * .commit() or .abort(). Regardless of which callback is invoked after
158
+ * completion, .clean() will always be called, even if the job does not
159
+ * belong to a transaction group.
160
+ */
161
+ void (*clean)(Job *job);
162
+
163
+
164
/** Called when the job is freed */
165
void (*free)(Job *job);
166
};
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
}
210
}
211
212
-static void backup_commit(BlockJob *job)
213
+static void backup_commit(Job *job)
214
{
215
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
216
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
217
if (s->sync_bitmap) {
218
backup_cleanup_sync_bitmap(s, 0);
219
}
220
}
221
222
-static void backup_abort(BlockJob *job)
223
+static void backup_abort(Job *job)
224
{
225
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
226
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
227
if (s->sync_bitmap) {
228
backup_cleanup_sync_bitmap(s, -1);
229
}
230
}
231
232
-static void backup_clean(BlockJob *job)
233
+static void backup_clean(Job *job)
234
{
235
- BackupBlockJob *s = container_of(job, BackupBlockJob, common);
236
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
237
assert(s->target);
238
blk_unref(s->target);
239
s->target = NULL;
240
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
241
.free = block_job_free,
242
.user_resume = block_job_user_resume,
243
.start = backup_run,
244
+ .commit = backup_commit,
245
+ .abort = backup_abort,
246
+ .clean = backup_clean,
247
},
248
- .commit = backup_commit,
249
- .abort = backup_abort,
250
- .clean = backup_clean,
251
.attached_aio_context = backup_attached_aio_context,
252
.drain = backup_drain,
253
};
254
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
255
bdrv_reclaim_dirty_bitmap(bs, sync_bitmap, NULL);
256
}
257
if (job) {
258
- backup_clean(&job->common);
259
- block_job_early_fail(&job->common);
260
+ backup_clean(&job->common.job);
261
+ job_early_fail(&job->common.job);
262
}
263
264
return NULL;
265
diff --git a/block/commit.c b/block/commit.c
266
index XXXXXXX..XXXXXXX 100644
267
--- a/block/commit.c
268
+++ b/block/commit.c
269
@@ -XXX,XX +XXX,XX @@ fail:
270
if (commit_top_bs) {
271
bdrv_replace_node(commit_top_bs, top, &error_abort);
272
}
273
- block_job_early_fail(&s->common);
274
+ job_early_fail(&s->common.job);
275
}
276
277
278
diff --git a/block/mirror.c b/block/mirror.c
279
index XXXXXXX..XXXXXXX 100644
280
--- a/block/mirror.c
281
+++ b/block/mirror.c
282
@@ -XXX,XX +XXX,XX @@ fail:
283
284
g_free(s->replaces);
285
blk_unref(s->target);
286
- block_job_early_fail(&s->common);
287
+ job_early_fail(&s->common.job);
288
}
289
290
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
291
diff --git a/blockjob.c b/blockjob.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockjob.c
294
+++ b/blockjob.c
295
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
296
block_job_txn_ref(txn);
297
}
298
299
-static void block_job_txn_del_job(BlockJob *job)
300
+void block_job_txn_del_job(BlockJob *job)
301
{
302
if (job->txn) {
303
QLIST_REMOVE(job, txn_list);
304
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
305
return job->driver;
306
}
307
308
-static void block_job_decommission(BlockJob *job)
309
-{
310
- assert(job);
311
- job->job.busy = false;
312
- job->job.paused = false;
313
- job->job.deferred_to_main_loop = true;
314
- block_job_txn_del_job(job);
315
- job_state_transition(&job->job, JOB_STATUS_NULL);
316
- job_unref(&job->job);
317
-}
318
-
319
-static void block_job_do_dismiss(BlockJob *job)
320
-{
321
- block_job_decommission(job);
322
-}
323
-
324
-static void block_job_conclude(BlockJob *job)
325
-{
326
- job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
327
- if (job->job.auto_dismiss || !job_started(&job->job)) {
328
- block_job_do_dismiss(job);
329
- }
330
-}
331
-
332
-static void block_job_update_rc(BlockJob *job)
333
-{
334
- if (!job->ret && job_is_cancelled(&job->job)) {
335
- job->ret = -ECANCELED;
336
- }
337
- if (job->ret) {
338
- job_state_transition(&job->job, JOB_STATUS_ABORTING);
339
- }
340
-}
341
-
342
static int block_job_prepare(BlockJob *job)
343
{
344
- if (job->ret == 0 && job->driver->prepare) {
345
- job->ret = job->driver->prepare(job);
346
- }
347
- return job->ret;
348
-}
349
-
350
-static void block_job_commit(BlockJob *job)
351
-{
352
- assert(!job->ret);
353
- if (job->driver->commit) {
354
- job->driver->commit(job);
355
- }
356
-}
357
-
358
-static void block_job_abort(BlockJob *job)
359
-{
360
- assert(job->ret);
361
- if (job->driver->abort) {
362
- job->driver->abort(job);
363
- }
364
-}
365
-
366
-static void block_job_clean(BlockJob *job)
367
-{
368
- if (job->driver->clean) {
369
- job->driver->clean(job);
370
+ if (job->job.ret == 0 && job->driver->prepare) {
371
+ job->job.ret = job->driver->prepare(job);
372
}
373
-}
374
-
375
-static int block_job_finalize_single(BlockJob *job)
376
-{
377
- assert(job_is_completed(&job->job));
378
-
379
- /* Ensure abort is called for late-transactional failures */
380
- block_job_update_rc(job);
381
-
382
- if (!job->ret) {
383
- block_job_commit(job);
384
- } else {
385
- block_job_abort(job);
386
- }
387
- block_job_clean(job);
388
-
389
- if (job->cb) {
390
- job->cb(job->opaque, job->ret);
391
- }
392
-
393
- /* Emit events only if we actually started */
394
- if (job_started(&job->job)) {
395
- if (job_is_cancelled(&job->job)) {
396
- job_event_cancelled(&job->job);
397
- } else {
398
- job_event_completed(&job->job);
399
- }
400
- }
401
-
402
- block_job_txn_del_job(job);
403
- block_job_conclude(job);
404
- return 0;
405
+ return job->job.ret;
406
}
407
408
static void block_job_cancel_async(BlockJob *job, bool force)
409
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
410
while (!job_is_completed(&job->job)) {
411
aio_poll(qemu_get_aio_context(), true);
412
}
413
- ret = (job_is_cancelled(&job->job) && job->ret == 0)
414
- ? -ECANCELED : job->ret;
415
+ ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
416
+ ? -ECANCELED : job->job.ret;
417
job_unref(&job->job);
418
return ret;
419
}
420
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
421
assert(job_is_cancelled(&other_job->job));
422
block_job_finish_sync(other_job, NULL, NULL);
423
}
424
- block_job_finalize_single(other_job);
425
+ job_finalize_single(&other_job->job);
426
aio_context_release(ctx);
427
}
428
429
@@ -XXX,XX +XXX,XX @@ static int block_job_needs_finalize(BlockJob *job)
430
return !job->job.auto_finalize;
431
}
432
433
+static int block_job_finalize_single(BlockJob *job)
434
+{
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
--
718
2.13.6
719
720
diff view generated by jsdifflib
Deleted patch
1
block_job_cancel_async() did two things that were still block job
2
specific:
3
1
4
* Setting job->force. This field makes sense on the Job level, so we can
5
just move it. While at it, rename it to job->force_cancel to make its
6
purpose more obvious.
7
8
* Resetting the I/O status. This can't be moved because generic Jobs
9
don't have an I/O status. What the function really implements is a
10
user resume, except without entering the coroutine. Consequently, it
11
makes sense to call the .user_resume driver callback here which
12
already resets the I/O status.
13
14
The old block_job_cancel_async() has two separate if statements that
15
check job->iostatus != BLOCK_DEVICE_IO_STATUS_OK and job->user_paused.
16
However, the former condition always implies the latter (as is
17
asserted in block_job_iostatus_reset()), so changing the explicit call
18
of block_job_iostatus_reset() on the former condition with the
19
.user_resume callback on the latter condition is equivalent and
20
doesn't need to access any BlockJob specific state.
21
22
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
23
Reviewed-by: Max Reitz <mreitz@redhat.com>
24
---
25
include/block/blockjob.h | 6 ------
26
include/qemu/job.h | 6 ++++++
27
block/mirror.c | 4 ++--
28
blockjob.c | 25 +++++++++++++------------
29
4 files changed, 21 insertions(+), 20 deletions(-)
30
31
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
32
index XXXXXXX..XXXXXXX 100644
33
--- a/include/block/blockjob.h
34
+++ b/include/block/blockjob.h
35
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
36
BlockBackend *blk;
37
38
/**
39
- * Set to true if the job should abort immediately without waiting
40
- * for data to be in sync.
41
- */
42
- bool force;
43
-
44
- /**
45
* Set to true when the job is ready to be completed.
46
*/
47
bool ready;
48
diff --git a/include/qemu/job.h b/include/qemu/job.h
49
index XXXXXXX..XXXXXXX 100644
50
--- a/include/qemu/job.h
51
+++ b/include/qemu/job.h
52
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
53
*/
54
bool cancelled;
55
56
+ /**
57
+ * Set to true if the job should abort immediately without waiting
58
+ * for data to be in sync.
59
+ */
60
+ bool force_cancel;
61
+
62
/** Set to true when the job has deferred work to the main loop. */
63
bool deferred_to_main_loop;
64
65
diff --git a/block/mirror.c b/block/mirror.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/block/mirror.c
68
+++ b/block/mirror.c
69
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
70
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
71
job_sleep_ns(&s->common.job, delay_ns);
72
if (job_is_cancelled(&s->common.job) &&
73
- (!s->synced || s->common.force))
74
+ (!s->synced || s->common.job.force_cancel))
75
{
76
break;
77
}
78
@@ -XXX,XX +XXX,XX @@ immediate_exit:
79
* or it was cancelled prematurely so that we do not guarantee that
80
* the target is a copy of the source.
81
*/
82
- assert(ret < 0 || ((s->common.force || !s->synced) &&
83
+ assert(ret < 0 || ((s->common.job.force_cancel || !s->synced) &&
84
job_is_cancelled(&s->common.job)));
85
assert(need_drain);
86
mirror_wait_for_all_io(s);
87
diff --git a/blockjob.c b/blockjob.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/blockjob.c
90
+++ b/blockjob.c
91
@@ -XXX,XX +XXX,XX @@ static int block_job_prepare(BlockJob *job)
92
return job->job.ret;
93
}
94
95
-static void block_job_cancel_async(BlockJob *job, bool force)
96
+static void job_cancel_async(Job *job, bool force)
97
{
98
- if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
99
- block_job_iostatus_reset(job);
100
- }
101
- if (job->job.user_paused) {
102
- /* Do not call block_job_enter here, the caller will handle it. */
103
- job->job.user_paused = false;
104
- job->job.pause_count--;
105
+ if (job->user_paused) {
106
+ /* Do not call job_enter here, the caller will handle it. */
107
+ job->user_paused = false;
108
+ if (job->driver->user_resume) {
109
+ job->driver->user_resume(job);
110
+ }
111
+ assert(job->pause_count > 0);
112
+ job->pause_count--;
113
}
114
- job->job.cancelled = true;
115
+ job->cancelled = true;
116
/* To prevent 'force == false' overriding a previous 'force == true' */
117
- job->force |= force;
118
+ job->force_cancel |= force;
119
}
120
121
static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
122
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
123
* on the caller, so leave it. */
124
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
125
if (other_job != job) {
126
- block_job_cancel_async(other_job, false);
127
+ job_cancel_async(&other_job->job, false);
128
}
129
}
130
while (!QLIST_EMPTY(&txn->jobs)) {
131
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
132
job_do_dismiss(&job->job);
133
return;
134
}
135
- block_job_cancel_async(job, force);
136
+ job_cancel_async(&job->job, force);
137
if (!job_started(&job->job)) {
138
block_job_completed(job, -ECANCELED);
139
} else if (job->job.deferred_to_main_loop) {
140
--
141
2.13.6
142
143
diff view generated by jsdifflib
Deleted patch
1
block_job_drain() contains a blk_drain() call which cannot be moved to
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
1
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
---
10
include/block/blockjob_int.h | 12 ++++++++++++
11
include/qemu/job.h | 13 +++++++++++++
12
block/backup.c | 1 +
13
block/commit.c | 1 +
14
block/mirror.c | 2 ++
15
block/stream.c | 1 +
16
blockjob.c | 20 ++++++++++----------
17
job.c | 11 +++++++++++
18
tests/test-bdrv-drain.c | 1 +
19
tests/test-blockjob-txn.c | 1 +
20
tests/test-blockjob.c | 2 ++
21
11 files changed, 55 insertions(+), 10 deletions(-)
22
23
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
24
index XXXXXXX..XXXXXXX 100644
25
--- a/include/block/blockjob_int.h
26
+++ b/include/block/blockjob_int.h
27
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
28
* If the callback is not NULL, it will be invoked when the job has to be
29
* synchronously cancelled or completed; it should drain BlockDriverStates
30
* as required to ensure progress.
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
+
49
+/**
50
* block_job_yield:
51
* @job: The job that calls the function.
52
*
53
diff --git a/include/qemu/job.h b/include/qemu/job.h
54
index XXXXXXX..XXXXXXX 100644
55
--- a/include/qemu/job.h
56
+++ b/include/qemu/job.h
57
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
58
*/
59
void (*user_resume)(Job *job);
60
61
+ /*
62
+ * If the callback is not NULL, it will be invoked when the job has to be
63
+ * synchronously cancelled or completed; it should drain any activities
64
+ * as required to ensure progress.
65
+ */
66
+ void (*drain)(Job *job);
67
+
68
/**
69
* If the callback is not NULL, it will be invoked when all the jobs
70
* belonging to the same transaction complete; or upon this job's
71
@@ -XXX,XX +XXX,XX @@ bool job_user_paused(Job *job);
72
*/
73
void job_user_resume(Job *job, Error **errp);
74
75
+/*
76
+ * Drain any activities as required to ensure progress. This can be called in a
77
+ * loop to synchronously complete a job.
78
+ */
79
+void job_drain(Job *job);
80
+
81
/**
82
* Get the next element from the list of block jobs after @job, or the
83
* first one if @job is %NULL.
84
diff --git a/block/backup.c b/block/backup.c
85
index XXXXXXX..XXXXXXX 100644
86
--- a/block/backup.c
87
+++ b/block/backup.c
88
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
89
.job_type = JOB_TYPE_BACKUP,
90
.free = block_job_free,
91
.user_resume = block_job_user_resume,
92
+ .drain = block_job_drain,
93
.start = backup_run,
94
.commit = backup_commit,
95
.abort = backup_abort,
96
diff --git a/block/commit.c b/block/commit.c
97
index XXXXXXX..XXXXXXX 100644
98
--- a/block/commit.c
99
+++ b/block/commit.c
100
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
101
.job_type = JOB_TYPE_COMMIT,
102
.free = block_job_free,
103
.user_resume = block_job_user_resume,
104
+ .drain = block_job_drain,
105
.start = commit_run,
106
},
107
};
108
diff --git a/block/mirror.c b/block/mirror.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/mirror.c
111
+++ b/block/mirror.c
112
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
113
.job_type = JOB_TYPE_MIRROR,
114
.free = block_job_free,
115
.user_resume = block_job_user_resume,
116
+ .drain = block_job_drain,
117
.start = mirror_run,
118
.pause = mirror_pause,
119
},
120
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
121
.job_type = JOB_TYPE_COMMIT,
122
.free = block_job_free,
123
.user_resume = block_job_user_resume,
124
+ .drain = block_job_drain,
125
.start = mirror_run,
126
.pause = mirror_pause,
127
},
128
diff --git a/block/stream.c b/block/stream.c
129
index XXXXXXX..XXXXXXX 100644
130
--- a/block/stream.c
131
+++ b/block/stream.c
132
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
133
.free = block_job_free,
134
.start = stream_run,
135
.user_resume = block_job_user_resume,
136
+ .drain = block_job_drain,
137
},
138
};
139
140
diff --git a/blockjob.c b/blockjob.c
141
index XXXXXXX..XXXXXXX 100644
142
--- a/blockjob.c
143
+++ b/blockjob.c
144
@@ -XXX,XX +XXX,XX @@ static void block_job_attached_aio_context(AioContext *new_context,
145
job_resume(&job->job);
146
}
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
+
214
+
215
/**
216
* All jobs must allow a pause point before entering their job proper. This
217
* ensures that jobs can be paused prior to being started, then resumed later.
218
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
219
index XXXXXXX..XXXXXXX 100644
220
--- a/tests/test-bdrv-drain.c
221
+++ b/tests/test-bdrv-drain.c
222
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
223
.instance_size = sizeof(TestBlockJob),
224
.free = block_job_free,
225
.user_resume = block_job_user_resume,
226
+ .drain = block_job_drain,
227
.start = test_job_start,
228
},
229
.complete = test_job_complete,
230
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
231
index XXXXXXX..XXXXXXX 100644
232
--- a/tests/test-blockjob-txn.c
233
+++ b/tests/test-blockjob-txn.c
234
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
235
.instance_size = sizeof(TestBlockJob),
236
.free = block_job_free,
237
.user_resume = block_job_user_resume,
238
+ .drain = block_job_drain,
239
.start = test_block_job_run,
240
},
241
};
242
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
243
index XXXXXXX..XXXXXXX 100644
244
--- a/tests/test-blockjob.c
245
+++ b/tests/test-blockjob.c
246
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
247
.instance_size = sizeof(BlockJob),
248
.free = block_job_free,
249
.user_resume = block_job_user_resume,
250
+ .drain = block_job_drain,
251
},
252
};
253
254
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
255
.instance_size = sizeof(CancelJob),
256
.free = block_job_free,
257
.user_resume = block_job_user_resume,
258
+ .drain = block_job_drain,
259
.start = cancel_job_start,
260
},
261
.complete = cancel_job_complete,
262
--
263
2.13.6
264
265
diff view generated by jsdifflib
Deleted patch
1
This moves the .complete callback that tells a READY job to complete
2
from BlockJobDriver to JobDriver. The wrapper function job_complete()
3
doesn't require anything block job specific any more and can be moved
4
to Job.
5
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Max Reitz <mreitz@redhat.com>
8
---
9
include/block/blockjob.h | 10 ----------
10
include/block/blockjob_int.h | 6 ------
11
include/qemu/job.h | 8 ++++++++
12
block/mirror.c | 10 +++++-----
13
blockdev.c | 2 +-
14
blockjob.c | 23 +++++------------------
15
job.c | 16 ++++++++++++++++
16
tests/test-bdrv-drain.c | 6 +++---
17
tests/test-blockjob.c | 10 +++++-----
18
9 files changed, 43 insertions(+), 48 deletions(-)
19
20
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
129
index XXXXXXX..XXXXXXX 100644
130
--- a/blockdev.c
131
+++ b/blockdev.c
132
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_complete(const char *device, Error **errp)
133
}
134
135
trace_qmp_block_job_complete(job);
136
- block_job_complete(job, errp);
137
+ job_complete(&job->job, errp);
138
aio_context_release(aio_context);
139
}
140
141
diff --git a/blockjob.c b/blockjob.c
142
index XXXXXXX..XXXXXXX 100644
143
--- a/blockjob.c
144
+++ b/blockjob.c
145
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
146
return ratelimit_calculate_delay(&job->limit, n);
147
}
148
149
-void block_job_complete(BlockJob *job, Error **errp)
150
-{
151
- /* Should not be reachable via external interface for internal jobs */
152
- assert(job->job.id);
153
- if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
154
- return;
155
- }
156
- if (job->job.pause_count || job_is_cancelled(&job->job) ||
157
- !job->driver->complete)
158
- {
159
- error_setg(errp, "The active block job '%s' cannot be completed",
160
- job->job.id);
161
- return;
162
- }
163
-
164
- job->driver->complete(job, errp);
165
-}
166
-
167
void block_job_finalize(BlockJob *job, Error **errp)
168
{
169
assert(job && job->job.id);
170
@@ -XXX,XX +XXX,XX @@ void block_job_cancel_sync_all(void)
171
}
172
}
173
174
+static void block_job_complete(BlockJob *job, Error **errp)
175
+{
176
+ job_complete(&job->job, errp);
177
+}
178
+
179
int block_job_complete_sync(BlockJob *job, Error **errp)
180
{
181
return block_job_finish_sync(job, &block_job_complete, errp);
182
diff --git a/job.c b/job.c
183
index XXXXXXX..XXXXXXX 100644
184
--- a/job.c
185
+++ b/job.c
186
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
187
return 0;
188
}
189
190
+void job_complete(Job *job, Error **errp)
191
+{
192
+ /* Should not be reachable via external interface for internal jobs */
193
+ assert(job->id);
194
+ if (job_apply_verb(job, JOB_VERB_COMPLETE, errp)) {
195
+ return;
196
+ }
197
+ if (job->pause_count || job_is_cancelled(job) || !job->driver->complete) {
198
+ error_setg(errp, "The active block job '%s' cannot be completed",
199
+ job->id);
200
+ return;
201
+ }
202
+
203
+ job->driver->complete(job, errp);
204
+}
205
+
206
207
typedef struct {
208
Job *job;
209
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
210
index XXXXXXX..XXXXXXX 100644
211
--- a/tests/test-bdrv-drain.c
212
+++ b/tests/test-bdrv-drain.c
213
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
214
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
215
}
216
217
-static void test_job_complete(BlockJob *job, Error **errp)
218
+static void test_job_complete(Job *job, Error **errp)
219
{
220
- TestBlockJob *s = container_of(job, TestBlockJob, common);
221
+ TestBlockJob *s = container_of(job, TestBlockJob, common.job);
222
s->should_complete = true;
223
}
224
225
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
226
.user_resume = block_job_user_resume,
227
.drain = block_job_drain,
228
.start = test_job_start,
229
+ .complete = test_job_complete,
230
},
231
- .complete = test_job_complete,
232
};
233
234
static void test_blockjob_common(enum drain_type drain_type)
235
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
236
index XXXXXXX..XXXXXXX 100644
237
--- a/tests/test-blockjob.c
238
+++ b/tests/test-blockjob.c
239
@@ -XXX,XX +XXX,XX @@ static void cancel_job_completed(Job *job, void *opaque)
240
block_job_completed(bjob, 0);
241
}
242
243
-static void cancel_job_complete(BlockJob *job, Error **errp)
244
+static void cancel_job_complete(Job *job, Error **errp)
245
{
246
- CancelJob *s = container_of(job, CancelJob, common);
247
+ CancelJob *s = container_of(job, CancelJob, common.job);
248
s->should_complete = true;
249
}
250
251
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
252
.user_resume = block_job_user_resume,
253
.drain = block_job_drain,
254
.start = cancel_job_start,
255
+ .complete = cancel_job_complete,
256
},
257
- .complete = cancel_job_complete,
258
};
259
260
static CancelJob *create_common(BlockJob **pjob)
261
@@ -XXX,XX +XXX,XX @@ static void test_cancel_pending(void)
262
block_job_enter(job);
263
assert(job->job.status == JOB_STATUS_READY);
264
265
- block_job_complete(job, &error_abort);
266
+ job_complete(&job->job, &error_abort);
267
block_job_enter(job);
268
while (!s->completed) {
269
aio_poll(qemu_get_aio_context(), true);
270
@@ -XXX,XX +XXX,XX @@ static void test_cancel_concluded(void)
271
block_job_enter(job);
272
assert(job->job.status == JOB_STATUS_READY);
273
274
- block_job_complete(job, &error_abort);
275
+ job_complete(&job->job, &error_abort);
276
block_job_enter(job);
277
while (!s->completed) {
278
aio_poll(qemu_get_aio_context(), true);
279
--
280
2.13.6
281
282
diff view generated by jsdifflib
Deleted patch
1
block_job_finish_sync() doesn't contain anything block job specific any
2
more, so it can be moved to Job.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
7
include/qemu/job.h | 9 +++++++++
8
block/commit.c | 6 +++---
9
blockjob.c | 55 +++++++++---------------------------------------------
10
job.c | 28 +++++++++++++++++++++++++++
11
4 files changed, 49 insertions(+), 49 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 @@ typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
18
*/
19
void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
20
21
+/**
22
+ * Synchronously finishes the given @job. If @finish is given, it is called to
23
+ * trigger completion or cancellation of the job.
24
+ *
25
+ * Returns 0 if the job is successfully completed, -ECANCELED if the job was
26
+ * cancelled before completing, and -errno in other error cases.
27
+ */
28
+int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
29
+
30
/* TODO To be removed from the public interface */
31
void job_state_transition(Job *job, JobStatus s1);
32
void coroutine_fn job_do_yield(Job *job, uint64_t ns);
33
diff --git a/block/commit.c b/block/commit.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/block/commit.c
36
+++ b/block/commit.c
37
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
38
blk_unref(s->top);
39
40
/* If there is more than one reference to the job (e.g. if called from
41
- * block_job_finish_sync()), block_job_completed() won't free it and
42
- * therefore the blockers on the intermediate nodes remain. This would
43
- * cause bdrv_set_backing_hd() to fail. */
44
+ * job_finish_sync()), block_job_completed() won't free it and therefore
45
+ * the blockers on the intermediate nodes remain. This would cause
46
+ * bdrv_set_backing_hd() to fail. */
47
block_job_remove_all_bdrv(bjob);
48
49
block_job_completed(&s->common, ret);
50
diff --git a/blockjob.c b/blockjob.c
51
index XXXXXXX..XXXXXXX 100644
52
--- a/blockjob.c
53
+++ b/blockjob.c
54
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
55
return rc;
56
}
57
58
-static int block_job_finish_sync(BlockJob *job,
59
- void (*finish)(BlockJob *, Error **errp),
60
- Error **errp)
61
-{
62
- Error *local_err = NULL;
63
- int ret;
64
-
65
- assert(blk_bs(job->blk)->job == job);
66
-
67
- job_ref(&job->job);
68
-
69
- if (finish) {
70
- finish(job, &local_err);
71
- }
72
- if (local_err) {
73
- error_propagate(errp, local_err);
74
- job_unref(&job->job);
75
- return -EBUSY;
76
- }
77
- /* job_drain calls job_enter, and it should be enough to induce progress
78
- * until the job completes or moves to the main thread.
79
- */
80
- while (!job->job.deferred_to_main_loop && !job_is_completed(&job->job)) {
81
- job_drain(&job->job);
82
- }
83
- while (!job_is_completed(&job->job)) {
84
- aio_poll(qemu_get_aio_context(), true);
85
- }
86
- ret = (job_is_cancelled(&job->job) && job->job.ret == 0)
87
- ? -ECANCELED : job->job.ret;
88
- job_unref(&job->job);
89
- return ret;
90
-}
91
-
92
static void block_job_completed_txn_abort(BlockJob *job)
93
{
94
AioContext *ctx;
95
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
96
ctx = blk_get_aio_context(other_job->blk);
97
if (!job_is_completed(&other_job->job)) {
98
assert(job_is_cancelled(&other_job->job));
99
- block_job_finish_sync(other_job, NULL, NULL);
100
+ job_finish_sync(&other_job->job, NULL, NULL);
101
}
102
job_finalize_single(&other_job->job);
103
aio_context_release(ctx);
104
@@ -XXX,XX +XXX,XX @@ void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
105
}
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
}
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
--
181
2.13.6
182
183
diff view generated by jsdifflib
Deleted patch
1
This doesn't actually move any transaction code to Job yet, but it
2
renames the type for transactions from BlockJobTxn to JobTxn and makes
3
them contain Jobs rather than BlockJobs
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
8
include/block/block_int.h | 2 +-
9
include/block/blockjob.h | 11 ++++----
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
18
diff --git a/include/block/block_int.h b/include/block/block_int.h
19
index XXXXXXX..XXXXXXX 100644
20
--- a/include/block/block_int.h
21
+++ b/include/block/block_int.h
22
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
23
BlockdevOnError on_target_error,
24
int creation_flags,
25
BlockCompletionFunc *cb, void *opaque,
26
- BlockJobTxn *txn, Error **errp);
27
+ JobTxn *txn, Error **errp);
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
}
210
}
211
212
-BlockJobTxn *block_job_txn_new(void)
213
+JobTxn *block_job_txn_new(void)
214
{
215
- BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
216
+ JobTxn *txn = g_new0(JobTxn, 1);
217
QLIST_INIT(&txn->jobs);
218
txn->refcnt = 1;
219
return txn;
220
}
221
222
-static void block_job_txn_ref(BlockJobTxn *txn)
223
+static void block_job_txn_ref(JobTxn *txn)
224
{
225
txn->refcnt++;
226
}
227
228
-void block_job_txn_unref(BlockJobTxn *txn)
229
+void block_job_txn_unref(JobTxn *txn)
230
{
231
if (txn && --txn->refcnt == 0) {
232
g_free(txn);
233
}
234
}
235
236
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
237
+void block_job_txn_add_job(JobTxn *txn, BlockJob *job)
238
{
239
if (!txn) {
240
return;
241
@@ -XXX,XX +XXX,XX @@ void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
242
assert(!job->txn);
243
job->txn = txn;
244
245
- QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
246
+ QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list);
247
block_job_txn_ref(txn);
248
}
249
250
void block_job_txn_del_job(BlockJob *job)
251
{
252
if (job->txn) {
253
- QLIST_REMOVE(job, txn_list);
254
+ QLIST_REMOVE(&job->job, txn_list);
255
block_job_txn_unref(job->txn);
256
job->txn = NULL;
257
}
258
@@ -XXX,XX +XXX,XX @@ static void job_cancel_async(Job *job, bool force)
259
job->force_cancel |= force;
260
}
261
262
-static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
263
+static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock)
264
{
265
AioContext *ctx;
266
- BlockJob *job, *next;
267
+ Job *job, *next;
268
+ BlockJob *bjob;
269
int rc = 0;
270
271
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
272
+ assert(is_block_job(job));
273
+ bjob = container_of(job, BlockJob, job);
274
+
275
if (lock) {
276
- ctx = blk_get_aio_context(job->blk);
277
+ ctx = job->aio_context;
278
aio_context_acquire(ctx);
279
}
280
- rc = fn(job);
281
+ rc = fn(bjob);
282
if (lock) {
283
aio_context_release(ctx);
284
}
285
@@ -XXX,XX +XXX,XX @@ static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
286
static void block_job_completed_txn_abort(BlockJob *job)
287
{
288
AioContext *ctx;
289
- BlockJobTxn *txn = job->txn;
290
- BlockJob *other_job;
291
+ JobTxn *txn = job->txn;
292
+ Job *other_job;
293
294
if (txn->aborting) {
295
/*
296
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
297
298
/* We are the first failed job. Cancel other jobs. */
299
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
300
- ctx = blk_get_aio_context(other_job->blk);
301
+ ctx = other_job->aio_context;
302
aio_context_acquire(ctx);
303
}
304
305
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
306
* them; this job, however, may or may not be cancelled, depending
307
* on the caller, so leave it. */
308
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
309
- if (other_job != job) {
310
- job_cancel_async(&other_job->job, false);
311
+ if (other_job != &job->job) {
312
+ job_cancel_async(other_job, false);
313
}
314
}
315
while (!QLIST_EMPTY(&txn->jobs)) {
316
other_job = QLIST_FIRST(&txn->jobs);
317
- ctx = blk_get_aio_context(other_job->blk);
318
- if (!job_is_completed(&other_job->job)) {
319
- assert(job_is_cancelled(&other_job->job));
320
- job_finish_sync(&other_job->job, NULL, NULL);
321
+ ctx = other_job->aio_context;
322
+ if (!job_is_completed(other_job)) {
323
+ assert(job_is_cancelled(other_job));
324
+ job_finish_sync(other_job, NULL, NULL);
325
}
326
- job_finalize_single(&other_job->job);
327
+ job_finalize_single(other_job);
328
aio_context_release(ctx);
329
}
330
331
@@ -XXX,XX +XXX,XX @@ static int block_job_transition_to_pending(BlockJob *job)
332
333
static void block_job_completed_txn_success(BlockJob *job)
334
{
335
- BlockJobTxn *txn = job->txn;
336
- BlockJob *other_job;
337
+ JobTxn *txn = job->txn;
338
+ Job *other_job;
339
340
job_state_transition(&job->job, JOB_STATUS_WAITING);
341
342
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
343
* txn.
344
*/
345
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
346
- if (!job_is_completed(&other_job->job)) {
347
+ if (!job_is_completed(other_job)) {
348
return;
349
}
350
- assert(other_job->job.ret == 0);
351
+ assert(other_job->ret == 0);
352
}
353
354
block_job_txn_apply(txn, block_job_transition_to_pending, false);
355
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
356
*/
357
358
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
359
- BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
360
+ JobTxn *txn, BlockDriverState *bs, uint64_t perm,
361
uint64_t shared_perm, int64_t speed, int flags,
362
BlockCompletionFunc *cb, void *opaque, Error **errp)
363
{
364
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
365
index XXXXXXX..XXXXXXX 100644
366
--- a/tests/test-blockjob-txn.c
367
+++ b/tests/test-blockjob-txn.c
368
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
369
*/
370
static BlockJob *test_block_job_start(unsigned int iterations,
371
bool use_timer,
372
- int rc, int *result, BlockJobTxn *txn)
373
+ int rc, int *result, JobTxn *txn)
374
{
375
BlockDriverState *bs;
376
TestBlockJob *s;
377
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
378
static void test_single_job(int expected)
379
{
380
BlockJob *job;
381
- BlockJobTxn *txn;
382
+ JobTxn *txn;
383
int result = -EINPROGRESS;
384
385
txn = block_job_txn_new();
386
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
387
{
388
BlockJob *job1;
389
BlockJob *job2;
390
- BlockJobTxn *txn;
391
+ JobTxn *txn;
392
int result1 = -EINPROGRESS;
393
int result2 = -EINPROGRESS;
394
395
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
396
{
397
BlockJob *job1;
398
BlockJob *job2;
399
- BlockJobTxn *txn;
400
+ JobTxn *txn;
401
int result1 = -EINPROGRESS;
402
int result2 = -EINPROGRESS;
403
404
--
405
2.13.6
406
407
diff view generated by jsdifflib
Deleted patch
1
This moves the logic that implements job transactions from BlockJob to
2
Job.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
7
include/block/blockjob.h | 54 ----------
8
include/block/blockjob_int.h | 10 --
9
include/qemu/job.h | 71 +++++++++++--
10
blockdev.c | 6 +-
11
blockjob.c | 238 +------------------------------------------
12
job.c | 234 ++++++++++++++++++++++++++++++++++++++++--
13
tests/test-blockjob-txn.c | 12 +--
14
tests/test-blockjob.c | 2 +-
15
8 files changed, 303 insertions(+), 324 deletions(-)
16
17
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
18
index XXXXXXX..XXXXXXX 100644
19
--- a/include/block/blockjob.h
20
+++ b/include/block/blockjob.h
21
@@ -XXX,XX +XXX,XX @@
22
#define BLOCK_JOB_SLICE_TIME 100000000ULL /* ns */
23
24
typedef struct BlockJobDriver BlockJobDriver;
25
-typedef struct JobTxn JobTxn;
26
27
/**
28
* BlockJob:
29
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
30
31
/** BlockDriverStates that are involved in this block job */
32
GSList *nodes;
33
-
34
- JobTxn *txn;
35
} BlockJob;
36
37
/**
38
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
39
void block_job_cancel(BlockJob *job, bool force);
40
41
/**
42
- * block_job_finalize:
43
- * @job: The job to fully commit and finish.
44
- * @errp: Error object.
45
- *
46
- * For jobs that have finished their work and are pending
47
- * awaiting explicit acknowledgement to commit their work,
48
- * This will commit that work.
49
- *
50
- * FIXME: Make the below statement universally true:
51
- * For jobs that support the manual workflow mode, all graph
52
- * changes that occur as a result will occur after this command
53
- * and before a successful reply.
54
- */
55
-void block_job_finalize(BlockJob *job, Error **errp);
56
-
57
-/**
58
* block_job_dismiss:
59
* @job: The job to be dismissed.
60
* @errp: Error object.
61
@@ -XXX,XX +XXX,XX @@ int block_job_complete_sync(BlockJob *job, Error **errp);
62
void block_job_iostatus_reset(BlockJob *job);
63
64
/**
65
- * block_job_txn_new:
66
- *
67
- * Allocate and return a new block job transaction. Jobs can be added to the
68
- * transaction using block_job_txn_add_job().
69
- *
70
- * The transaction is automatically freed when the last job completes or is
71
- * cancelled.
72
- *
73
- * All jobs in the transaction either complete successfully or fail/cancel as a
74
- * group. Jobs wait for each other before completing. Cancelling one job
75
- * cancels all jobs in the transaction.
76
- */
77
-JobTxn *block_job_txn_new(void);
78
-
79
-/**
80
- * block_job_txn_unref:
81
- *
82
- * Release a reference that was previously acquired with block_job_txn_add_job
83
- * or block_job_txn_new. If it's the last reference to the object, it will be
84
- * freed.
85
- */
86
-void block_job_txn_unref(JobTxn *txn);
87
-
88
-/**
89
- * block_job_txn_add_job:
90
- * @txn: The transaction (may be NULL)
91
- * @job: Job to add to the transaction
92
- *
93
- * Add @job to the transaction. The @job must not already be in a transaction.
94
- * The caller must call either block_job_txn_unref() or block_job_completed()
95
- * to release the reference that is automatically grabbed here.
96
- */
97
-void block_job_txn_add_job(JobTxn *txn, BlockJob *job);
98
-
99
-/**
100
* block_job_is_internal:
101
* @job: The job to determine if it is user-visible or not.
102
*
103
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
104
index XXXXXXX..XXXXXXX 100644
105
--- a/include/block/blockjob_int.h
106
+++ b/include/block/blockjob_int.h
107
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
108
/** Generic JobDriver callbacks and settings */
109
JobDriver job_driver;
110
111
- /**
112
- * If the callback is not NULL, prepare will be invoked when all the jobs
113
- * belonging to the same transaction complete; or upon this job's completion
114
- * if it is not in a transaction.
115
- *
116
- * This callback will not be invoked if the job has already failed.
117
- * If it fails, abort and then clean will be called.
118
- */
119
- int (*prepare)(BlockJob *job);
120
-
121
/*
122
* If the callback is not NULL, it will be invoked before the job is
123
* resumed in a new AioContext. This is the place to move any resources
124
diff --git a/include/qemu/job.h b/include/qemu/job.h
125
index XXXXXXX..XXXXXXX 100644
126
--- a/include/qemu/job.h
127
+++ b/include/qemu/job.h
128
@@ -XXX,XX +XXX,XX @@
129
#include "block/aio.h"
130
131
typedef struct JobDriver JobDriver;
132
+typedef struct JobTxn JobTxn;
133
+
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
+ *
172
+ * The transaction is automatically freed when the last job completes or is
173
+ * cancelled.
174
+ *
175
+ * All jobs in the transaction either complete successfully or fail/cancel as a
176
+ * group. Jobs wait for each other before completing. Cancelling one job
177
+ * cancels all jobs in the transaction.
178
+ */
179
+JobTxn *job_txn_new(void);
180
+
181
+/**
182
+ * Release a reference that was previously acquired with job_txn_add_job or
183
+ * job_txn_new. If it's the last reference to the object, it will be freed.
184
+ */
185
+void job_txn_unref(JobTxn *txn);
186
+
187
+/**
188
+ * @txn: The transaction (may be NULL)
189
+ * @job: Job to add to the transaction
190
+ *
191
+ * Add @job to the transaction. The @job must not already be in a transaction.
192
+ * The caller must call either job_txn_unref() or block_job_completed() to
193
+ * release the reference that is automatically grabbed here.
194
+ *
195
+ * If @txn is NULL, the function does nothing.
196
+ */
197
+void job_txn_add_job(JobTxn *txn, Job *job);
198
199
/**
200
* Create a new long-running job and return it.
201
*
202
* @job_id: The id of the newly-created job, or %NULL for internal jobs
203
* @driver: The class object for the newly-created job.
204
+ * @txn: The transaction this job belongs to, if any. %NULL otherwise.
205
* @ctx: The AioContext to run the job coroutine in.
206
* @flags: Creation flags for the job. See @JobCreateFlags.
207
* @cb: Completion function for the job.
208
* @opaque: Opaque pointer value passed to @cb.
209
* @errp: Error object.
210
*/
211
-void *job_create(const char *job_id, const JobDriver *driver, AioContext *ctx,
212
- int flags, BlockCompletionFunc *cb, void *opaque, Error **errp);
213
+void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
214
+ AioContext *ctx, int flags, BlockCompletionFunc *cb,
215
+ void *opaque, Error **errp);
216
217
/**
218
* Add a reference to Job refcnt, it will be decreased with job_unref, and then
219
@@ -XXX,XX +XXX,XX @@ void job_event_cancelled(Job *job);
220
/** To be called when a successfully completed job is finalised. */
221
void job_event_completed(Job *job);
222
223
-/** To be called when the job transitions to PENDING */
224
-void job_event_pending(Job *job);
225
-
226
/**
227
* Conditionally enter the job coroutine if the job is ready to run, not
228
* already busy and fn() returns true. fn() is called while under the job_lock
229
@@ -XXX,XX +XXX,XX @@ void job_early_fail(Job *job);
230
/** Asynchronously complete the specified @job. */
231
void job_complete(Job *job, Error **errp);;
232
233
+/**
234
+ * For a @job that has finished its work and is pending awaiting explicit
235
+ * acknowledgement to commit its work, this will commit that work.
236
+ *
237
+ * FIXME: Make the below statement universally true:
238
+ * For jobs that support the manual workflow mode, all graph changes that occur
239
+ * as a result will occur after this command and before a successful reply.
240
+ */
241
+void job_finalize(Job *job, Error **errp);
242
+
243
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
244
245
/**
246
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_do_yield(Job *job, uint64_t ns);
247
bool job_should_pause(Job *job);
248
bool job_started(Job *job);
249
void job_do_dismiss(Job *job);
250
-int job_finalize_single(Job *job);
251
void job_update_rc(Job *job);
252
-
253
-typedef struct BlockJob BlockJob;
254
-void block_job_txn_del_job(BlockJob *job);
255
+void job_cancel_async(Job *job, bool force);
256
+void job_completed_txn_abort(Job *job);
257
+void job_completed_txn_success(Job *job);
258
259
#endif
260
diff --git a/blockdev.c b/blockdev.c
261
index XXXXXXX..XXXXXXX 100644
262
--- a/blockdev.c
263
+++ b/blockdev.c
264
@@ -XXX,XX +XXX,XX @@ void qmp_transaction(TransactionActionList *dev_list,
265
*/
266
props = get_transaction_properties(props);
267
if (props->completion_mode != ACTION_COMPLETION_MODE_INDIVIDUAL) {
268
- block_job_txn = block_job_txn_new();
269
+ block_job_txn = job_txn_new();
270
}
271
272
/* drain all i/o before any operations */
273
@@ -XXX,XX +XXX,XX @@ exit:
274
if (!has_props) {
275
qapi_free_TransactionProperties(props);
276
}
277
- block_job_txn_unref(block_job_txn);
278
+ job_txn_unref(block_job_txn);
279
}
280
281
void qmp_eject(bool has_device, const char *device,
282
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
283
}
284
285
trace_qmp_block_job_finalize(job);
286
- block_job_finalize(job, errp);
287
+ job_finalize(&job->job, errp);
288
aio_context_release(aio_context);
289
}
290
291
diff --git a/blockjob.c b/blockjob.c
292
index XXXXXXX..XXXXXXX 100644
293
--- a/blockjob.c
294
+++ b/blockjob.c
295
@@ -XXX,XX +XXX,XX @@
296
#include "qemu/coroutine.h"
297
#include "qemu/timer.h"
298
299
-/* Transactional group of block jobs */
300
-struct JobTxn {
301
-
302
- /* Is this txn being cancelled? */
303
- bool aborting;
304
-
305
- /* List of jobs */
306
- QLIST_HEAD(, Job) jobs;
307
-
308
- /* Reference count */
309
- int refcnt;
310
-};
311
-
312
/*
313
* The block job API is composed of two categories of functions.
314
*
315
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
316
}
317
}
318
319
-JobTxn *block_job_txn_new(void)
320
-{
321
- JobTxn *txn = g_new0(JobTxn, 1);
322
- QLIST_INIT(&txn->jobs);
323
- txn->refcnt = 1;
324
- return txn;
325
-}
326
-
327
-static void block_job_txn_ref(JobTxn *txn)
328
-{
329
- txn->refcnt++;
330
-}
331
-
332
-void block_job_txn_unref(JobTxn *txn)
333
-{
334
- if (txn && --txn->refcnt == 0) {
335
- g_free(txn);
336
- }
337
-}
338
-
339
-void block_job_txn_add_job(JobTxn *txn, BlockJob *job)
340
-{
341
- if (!txn) {
342
- return;
343
- }
344
-
345
- assert(!job->txn);
346
- job->txn = txn;
347
-
348
- QLIST_INSERT_HEAD(&txn->jobs, &job->job, txn_list);
349
- block_job_txn_ref(txn);
350
-}
351
-
352
-void block_job_txn_del_job(BlockJob *job)
353
-{
354
- if (job->txn) {
355
- QLIST_REMOVE(&job->job, txn_list);
356
- block_job_txn_unref(job->txn);
357
- job->txn = NULL;
358
- }
359
-}
360
-
361
static void block_job_attached_aio_context(AioContext *new_context,
362
void *opaque);
363
static void block_job_detach_aio_context(void *opaque);
364
@@ -XXX,XX +XXX,XX @@ void block_job_free(Job *job)
365
BlockJob *bjob = container_of(job, BlockJob, job);
366
BlockDriverState *bs = blk_bs(bjob->blk);
367
368
- assert(!bjob->txn);
369
-
370
bs->job = NULL;
371
block_job_remove_all_bdrv(bjob);
372
blk_remove_aio_context_notifier(bjob->blk,
373
@@ -XXX,XX +XXX,XX @@ const BlockJobDriver *block_job_driver(BlockJob *job)
374
return job->driver;
375
}
376
377
-static int block_job_prepare(BlockJob *job)
378
-{
379
- if (job->job.ret == 0 && job->driver->prepare) {
380
- job->job.ret = job->driver->prepare(job);
381
- }
382
- return job->job.ret;
383
-}
384
-
385
-static void job_cancel_async(Job *job, bool force)
386
-{
387
- if (job->user_paused) {
388
- /* Do not call job_enter here, the caller will handle it. */
389
- job->user_paused = false;
390
- if (job->driver->user_resume) {
391
- job->driver->user_resume(job);
392
- }
393
- assert(job->pause_count > 0);
394
- job->pause_count--;
395
- }
396
- job->cancelled = true;
397
- /* To prevent 'force == false' overriding a previous 'force == true' */
398
- job->force_cancel |= force;
399
-}
400
-
401
-static int block_job_txn_apply(JobTxn *txn, int fn(BlockJob *), bool lock)
402
-{
403
- AioContext *ctx;
404
- Job *job, *next;
405
- BlockJob *bjob;
406
- int rc = 0;
407
-
408
- QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
409
- assert(is_block_job(job));
410
- bjob = container_of(job, BlockJob, job);
411
-
412
- if (lock) {
413
- ctx = job->aio_context;
414
- aio_context_acquire(ctx);
415
- }
416
- rc = fn(bjob);
417
- if (lock) {
418
- aio_context_release(ctx);
419
- }
420
- if (rc) {
421
- break;
422
- }
423
- }
424
- return rc;
425
-}
426
-
427
-static void block_job_completed_txn_abort(BlockJob *job)
428
-{
429
- AioContext *ctx;
430
- JobTxn *txn = job->txn;
431
- Job *other_job;
432
-
433
- if (txn->aborting) {
434
- /*
435
- * We are cancelled by another job, which will handle everything.
436
- */
437
- return;
438
- }
439
- txn->aborting = true;
440
- block_job_txn_ref(txn);
441
-
442
- /* We are the first failed job. Cancel other jobs. */
443
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
444
- ctx = other_job->aio_context;
445
- aio_context_acquire(ctx);
446
- }
447
-
448
- /* Other jobs are effectively cancelled by us, set the status for
449
- * them; this job, however, may or may not be cancelled, depending
450
- * on the caller, so leave it. */
451
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
452
- if (other_job != &job->job) {
453
- job_cancel_async(other_job, false);
454
- }
455
- }
456
- while (!QLIST_EMPTY(&txn->jobs)) {
457
- other_job = QLIST_FIRST(&txn->jobs);
458
- ctx = other_job->aio_context;
459
- if (!job_is_completed(other_job)) {
460
- assert(job_is_cancelled(other_job));
461
- job_finish_sync(other_job, NULL, NULL);
462
- }
463
- job_finalize_single(other_job);
464
- aio_context_release(ctx);
465
- }
466
-
467
- block_job_txn_unref(txn);
468
-}
469
-
470
-static int block_job_needs_finalize(BlockJob *job)
471
-{
472
- return !job->job.auto_finalize;
473
-}
474
-
475
-static int block_job_finalize_single(BlockJob *job)
476
-{
477
- return job_finalize_single(&job->job);
478
-}
479
-
480
-static void block_job_do_finalize(BlockJob *job)
481
-{
482
- int rc;
483
- assert(job && job->txn);
484
-
485
- /* prepare the transaction to complete */
486
- rc = block_job_txn_apply(job->txn, block_job_prepare, true);
487
- if (rc) {
488
- block_job_completed_txn_abort(job);
489
- } else {
490
- block_job_txn_apply(job->txn, block_job_finalize_single, true);
491
- }
492
-}
493
-
494
-static int block_job_transition_to_pending(BlockJob *job)
495
-{
496
- job_state_transition(&job->job, JOB_STATUS_PENDING);
497
- if (!job->job.auto_finalize) {
498
- job_event_pending(&job->job);
499
- }
500
- return 0;
501
-}
502
-
503
-static void block_job_completed_txn_success(BlockJob *job)
504
-{
505
- JobTxn *txn = job->txn;
506
- Job *other_job;
507
-
508
- job_state_transition(&job->job, JOB_STATUS_WAITING);
509
-
510
- /*
511
- * Successful completion, see if there are other running jobs in this
512
- * txn.
513
- */
514
- QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
515
- if (!job_is_completed(other_job)) {
516
- return;
517
- }
518
- assert(other_job->ret == 0);
519
- }
520
-
521
- block_job_txn_apply(txn, block_job_transition_to_pending, false);
522
-
523
- /* If no jobs need manual finalization, automatically do so */
524
- if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
525
- block_job_do_finalize(job);
526
- }
527
-}
528
-
529
/* Assumes the job_mutex is held */
530
static bool job_timer_pending(Job *job)
531
{
532
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
533
return ratelimit_calculate_delay(&job->limit, n);
534
}
535
536
-void block_job_finalize(BlockJob *job, Error **errp)
537
-{
538
- assert(job && job->job.id);
539
- if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) {
540
- return;
541
- }
542
- block_job_do_finalize(job);
543
-}
544
-
545
void block_job_dismiss(BlockJob **jobptr, Error **errp)
546
{
547
BlockJob *job = *jobptr;
548
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job, bool force)
549
if (!job_started(&job->job)) {
550
block_job_completed(job, -ECANCELED);
551
} else if (job->job.deferred_to_main_loop) {
552
- block_job_completed_txn_abort(job);
553
+ job_completed_txn_abort(&job->job);
554
} else {
555
block_job_enter(job);
556
}
557
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
558
return NULL;
559
}
560
561
- job = job_create(job_id, &driver->job_driver, blk_get_aio_context(blk),
562
+ job = job_create(job_id, &driver->job_driver, txn, blk_get_aio_context(blk),
563
flags, cb, opaque, errp);
564
if (job == NULL) {
565
blk_unref(blk);
566
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
567
}
568
}
569
570
- /* Single jobs are modeled as single-job transactions for sake of
571
- * consolidating the job management logic */
572
- if (!txn) {
573
- txn = block_job_txn_new();
574
- block_job_txn_add_job(txn, job);
575
- block_job_txn_unref(txn);
576
- } else {
577
- block_job_txn_add_job(txn, job);
578
- }
579
-
580
return job;
581
}
582
583
void block_job_completed(BlockJob *job, int ret)
584
{
585
- assert(job && job->txn && !job_is_completed(&job->job));
586
+ assert(job && job->job.txn && !job_is_completed(&job->job));
587
assert(blk_bs(job->blk)->job == job);
588
job->job.ret = ret;
589
job_update_rc(&job->job);
590
trace_block_job_completed(job, ret, job->job.ret);
591
if (job->job.ret) {
592
- block_job_completed_txn_abort(job);
593
+ job_completed_txn_abort(&job->job);
594
} else {
595
- block_job_completed_txn_success(job);
596
+ job_completed_txn_success(&job->job);
597
}
598
}
599
600
diff --git a/job.c b/job.c
601
index XXXXXXX..XXXXXXX 100644
602
--- a/job.c
603
+++ b/job.c
604
@@ -XXX,XX +XXX,XX @@ bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
605
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
606
};
607
608
+/* Transactional group of jobs */
609
+struct JobTxn {
610
+
611
+ /* Is this txn being cancelled? */
612
+ bool aborting;
613
+
614
+ /* List of jobs */
615
+ QLIST_HEAD(, Job) jobs;
616
+
617
+ /* Reference count */
618
+ int refcnt;
619
+};
620
+
621
/* Right now, this mutex is only needed to synchronize accesses to job->busy
622
* and job->sleep_timer, such as concurrent calls to job_do_yield and
623
* job_enter. */
624
@@ -XXX,XX +XXX,XX @@ static void __attribute__((__constructor__)) job_init(void)
625
qemu_mutex_init(&job_mutex);
626
}
627
628
+JobTxn *job_txn_new(void)
629
+{
630
+ JobTxn *txn = g_new0(JobTxn, 1);
631
+ QLIST_INIT(&txn->jobs);
632
+ txn->refcnt = 1;
633
+ return txn;
634
+}
635
+
636
+static void job_txn_ref(JobTxn *txn)
637
+{
638
+ txn->refcnt++;
639
+}
640
+
641
+void job_txn_unref(JobTxn *txn)
642
+{
643
+ if (txn && --txn->refcnt == 0) {
644
+ g_free(txn);
645
+ }
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 {
719
+ job_txn_add_job(txn, job);
720
+ }
721
+
722
return job;
723
}
724
725
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
726
if (--job->refcnt == 0) {
727
assert(job->status == JOB_STATUS_NULL);
728
assert(!timer_pending(&job->sleep_timer));
729
+ assert(!job->txn);
730
731
if (job->driver->free) {
732
job->driver->free(job);
733
@@ -XXX,XX +XXX,XX @@ void job_event_completed(Job *job)
734
notifier_list_notify(&job->on_finalize_completed, job);
735
}
736
737
-void job_event_pending(Job *job)
738
+static void job_event_pending(Job *job)
739
{
740
notifier_list_notify(&job->on_pending, job);
741
}
742
@@ -XXX,XX +XXX,XX @@ void job_do_dismiss(Job *job)
743
job->paused = false;
744
job->deferred_to_main_loop = true;
745
746
- /* TODO Don't assume it's a BlockJob */
747
- block_job_txn_del_job((BlockJob*) job);
748
+ job_txn_del_job(job);
749
750
job_state_transition(job, JOB_STATUS_NULL);
751
job_unref(job);
752
@@ -XXX,XX +XXX,XX @@ static void job_clean(Job *job)
753
}
754
}
755
756
-int job_finalize_single(Job *job)
757
+static int job_finalize_single(Job *job)
758
{
759
assert(job_is_completed(job));
760
761
@@ -XXX,XX +XXX,XX @@ int job_finalize_single(Job *job)
762
}
763
}
764
765
- /* TODO Don't assume it's a BlockJob */
766
- block_job_txn_del_job((BlockJob*) job);
767
+ job_txn_del_job(job);
768
job_conclude(job);
769
return 0;
770
}
771
772
+void job_cancel_async(Job *job, bool force)
773
+{
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
--
977
2.13.6
978
979
diff view generated by jsdifflib
Deleted patch
1
Now that we cancel all jobs and not only block jobs on shutdown, doing
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
1
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
---
8
block.c | 4 +---
9
qemu-nbd.c | 8 +++++++-
10
vl.c | 1 +
11
3 files changed, 9 insertions(+), 4 deletions(-)
12
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
55
index XXXXXXX..XXXXXXX 100644
56
--- a/vl.c
57
+++ b/vl.c
58
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
59
/* No more vcpu or device emulation activity beyond this point */
60
vm_shutdown();
61
62
+ job_cancel_sync_all();
63
bdrv_close_all();
64
65
res_free();
66
--
67
2.13.6
68
69
diff view generated by jsdifflib
Deleted patch
1
This moves block_job_yield() to the Job layer.
2
1
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
6
include/block/blockjob_int.h | 8 --------
7
include/qemu/job.h | 9 +++++++--
8
block/backup.c | 2 +-
9
block/mirror.c | 2 +-
10
blockjob.c | 16 ----------------
11
job.c | 20 ++++++++++++++++++--
12
tests/test-blockjob-txn.c | 2 +-
13
7 files changed, 28 insertions(+), 31 deletions(-)
14
15
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
16
index XXXXXXX..XXXXXXX 100644
17
--- a/include/block/blockjob_int.h
18
+++ b/include/block/blockjob_int.h
19
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job);
20
void block_job_drain(Job *job);
21
22
/**
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
+
47
+/**
48
+ * @job: The job that calls the function.
49
* @ns: How many nanoseconds to stop for.
50
*
51
* Put the job to sleep (assuming that it wasn't canceled) for @ns
52
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
53
54
/* TODO To be removed from the public interface */
55
void job_state_transition(Job *job, JobStatus s1);
56
-void coroutine_fn job_do_yield(Job *job, uint64_t ns);
57
-bool job_should_pause(Job *job);
58
void job_do_dismiss(Job *job);
59
60
#endif
61
diff --git a/block/backup.c b/block/backup.c
62
index XXXXXXX..XXXXXXX 100644
63
--- a/block/backup.c
64
+++ b/block/backup.c
65
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
66
while (!job_is_cancelled(&job->common.job)) {
67
/* Yield until the job is cancelled. We just let our before_write
68
* notify callback service CoW requests. */
69
- block_job_yield(&job->common);
70
+ job_yield(&job->common.job);
71
}
72
} else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
73
ret = backup_run_incremental(job);
74
diff --git a/block/mirror.c b/block/mirror.c
75
index XXXXXXX..XXXXXXX 100644
76
--- a/block/mirror.c
77
+++ b/block/mirror.c
78
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
79
block_job_event_ready(&s->common);
80
s->synced = true;
81
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
82
- block_job_yield(&s->common);
83
+ job_yield(&s->common.job);
84
}
85
s->common.job.cancelled = false;
86
goto immediate_exit;
87
diff --git a/blockjob.c b/blockjob.c
88
index XXXXXXX..XXXXXXX 100644
89
--- a/blockjob.c
90
+++ b/blockjob.c
91
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
92
return job;
93
}
94
95
-void block_job_yield(BlockJob *job)
96
-{
97
- assert(job->job.busy);
98
-
99
- /* Check cancellation *before* setting busy = false, too! */
100
- if (job_is_cancelled(&job->job)) {
101
- return;
102
- }
103
-
104
- if (!job_should_pause(&job->job)) {
105
- job_do_yield(&job->job, -1);
106
- }
107
-
108
- job_pause_point(&job->job);
109
-}
110
-
111
void block_job_iostatus_reset(BlockJob *job)
112
{
113
if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
114
diff --git a/job.c b/job.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/job.c
117
+++ b/job.c
118
@@ -XXX,XX +XXX,XX @@ static bool job_started(Job *job)
119
return job->co;
120
}
121
122
-bool job_should_pause(Job *job)
123
+static bool job_should_pause(Job *job)
124
{
125
return job->pause_count > 0;
126
}
127
@@ -XXX,XX +XXX,XX @@ void job_enter(Job *job)
128
*
129
* If @ns is (uint64_t) -1, no timer is scheduled and job_enter() must be
130
* called explicitly. */
131
-void coroutine_fn job_do_yield(Job *job, uint64_t ns)
132
+static void coroutine_fn job_do_yield(Job *job, uint64_t ns)
133
{
134
job_lock();
135
if (ns != -1) {
136
@@ -XXX,XX +XXX,XX @@ void coroutine_fn job_pause_point(Job *job)
137
}
138
}
139
140
+void job_yield(Job *job)
141
+{
142
+ assert(job->busy);
143
+
144
+ /* Check cancellation *before* setting busy = false, too! */
145
+ if (job_is_cancelled(job)) {
146
+ return;
147
+ }
148
+
149
+ if (!job_should_pause(job)) {
150
+ job_do_yield(job, -1);
151
+ }
152
+
153
+ job_pause_point(job);
154
+}
155
+
156
void coroutine_fn job_sleep_ns(Job *job, int64_t ns)
157
{
158
assert(job->busy);
159
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
160
index XXXXXXX..XXXXXXX 100644
161
--- a/tests/test-blockjob-txn.c
162
+++ b/tests/test-blockjob-txn.c
163
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_block_job_run(void *opaque)
164
if (s->use_timer) {
165
job_sleep_ns(&job->job, 0);
166
} else {
167
- block_job_yield(job);
168
+ job_yield(&job->job);
169
}
170
171
if (job_is_cancelled(&job->job)) {
172
--
173
2.13.6
174
175
diff view generated by jsdifflib
Deleted patch
1
This moves block_job_dismiss() to the Job layer.
2
1
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
---
6
include/block/blockjob.h | 9 ---------
7
include/qemu/job.h | 7 ++++++-
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
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/block/blockjob.h
17
+++ b/include/block/blockjob.h
18
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
19
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
20
21
/**
22
- * block_job_dismiss:
23
- * @job: The job to be dismissed.
24
- * @errp: Error object.
25
- *
26
- * Remove a concluded job from the query list.
27
- */
28
-void block_job_dismiss(BlockJob **job, Error **errp);
29
-
30
-/**
31
* block_job_progress_update:
32
* @job: The job that has made progress
33
* @done: How much progress the job made
34
diff --git a/include/qemu/job.h b/include/qemu/job.h
35
index XXXXXXX..XXXXXXX 100644
36
--- a/include/qemu/job.h
37
+++ b/include/qemu/job.h
38
@@ -XXX,XX +XXX,XX @@ int job_complete_sync(Job *job, Error **errp);
39
*/
40
void job_finalize(Job *job, Error **errp);
41
42
+/**
43
+ * Remove the concluded @job from the query list and resets the passed pointer
44
+ * to %NULL. Returns an error if the job is not actually concluded.
45
+ */
46
+void job_dismiss(Job **job, Error **errp);
47
+
48
typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
49
50
/**
51
@@ -XXX,XX +XXX,XX @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
52
53
/* TODO To be removed from the public interface */
54
void job_state_transition(Job *job, JobStatus s1);
55
-void job_do_dismiss(Job *job);
56
57
#endif
58
diff --git a/blockdev.c b/blockdev.c
59
index XXXXXXX..XXXXXXX 100644
60
--- a/blockdev.c
61
+++ b/blockdev.c
62
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_finalize(const char *id, Error **errp)
63
void qmp_block_job_dismiss(const char *id, Error **errp)
64
{
65
AioContext *aio_context;
66
- BlockJob *job = find_block_job(id, &aio_context, errp);
67
+ BlockJob *bjob = find_block_job(id, &aio_context, errp);
68
+ Job *job;
69
70
- if (!job) {
71
+ if (!bjob) {
72
return;
73
}
74
75
- trace_qmp_block_job_dismiss(job);
76
- block_job_dismiss(&job, errp);
77
+ trace_qmp_block_job_dismiss(bjob);
78
+ job = &bjob->job;
79
+ job_dismiss(&job, errp);
80
aio_context_release(aio_context);
81
}
82
83
diff --git a/blockjob.c b/blockjob.c
84
index XXXXXXX..XXXXXXX 100644
85
--- a/blockjob.c
86
+++ b/blockjob.c
87
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
88
return ratelimit_calculate_delay(&job->limit, n);
89
}
90
91
-void block_job_dismiss(BlockJob **jobptr, Error **errp)
92
-{
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
--
156
2.13.6
157
158
diff view generated by jsdifflib
Deleted patch
1
Instead of having a 'bool ready' in BlockJob, add a function that
2
derives its value from the job status.
3
1
4
At the same time, this fixes the behaviour to match what the QAPI
5
documentation promises for query-block-job: 'true if the job may be
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
10
Job transactions and manual job finalisation were introduced only later.
11
With these changes, jobs may stay around even after having completed
12
(and they are not ready to be completed a second time), however their
13
patches forgot to reset the ready flag.
14
15
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
16
Reviewed-by: Max Reitz <mreitz@redhat.com>
17
---
18
include/block/blockjob.h | 5 -----
19
include/qemu/job.h | 3 +++
20
blockjob.c | 3 +--
21
job.c | 22 ++++++++++++++++++++++
22
qemu-img.c | 2 +-
23
tests/test-blockjob.c | 2 +-
24
6 files changed, 28 insertions(+), 9 deletions(-)
25
26
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
27
index XXXXXXX..XXXXXXX 100644
28
--- a/include/block/blockjob.h
29
+++ b/include/block/blockjob.h
30
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
31
/** The block device on which the job is operating. */
32
BlockBackend *blk;
33
34
- /**
35
- * Set to true when the job is ready to be completed.
36
- */
37
- bool ready;
38
-
39
/** Status that is published by the query-block-jobs QMP API */
40
BlockDeviceIoStatus iostatus;
41
42
diff --git a/include/qemu/job.h b/include/qemu/job.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/qemu/job.h
45
+++ b/include/qemu/job.h
46
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job);
47
/** Returns whether the job is in a completed state. */
48
bool job_is_completed(Job *job);
49
50
+/** Returns whether the job is ready to be completed. */
51
+bool job_is_ready(Job *job);
52
+
53
/**
54
* Request @job to pause at the next pause point. Must be paired with
55
* job_resume(). If the job is supposed to be resumed by user action, call
56
diff --git a/blockjob.c b/blockjob.c
57
index XXXXXXX..XXXXXXX 100644
58
--- a/blockjob.c
59
+++ b/blockjob.c
60
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
61
info->offset = job->offset;
62
info->speed = job->speed;
63
info->io_status = job->iostatus;
64
- info->ready = job->ready;
65
+ info->ready = job_is_ready(&job->job),
66
info->status = job->job.status;
67
info->auto_finalize = job->job.auto_finalize;
68
info->auto_dismiss = job->job.auto_dismiss;
69
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job)
70
void block_job_event_ready(BlockJob *job)
71
{
72
job_state_transition(&job->job, JOB_STATUS_READY);
73
- job->ready = true;
74
75
if (block_job_is_internal(job)) {
76
return;
77
diff --git a/job.c b/job.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/job.c
80
+++ b/job.c
81
@@ -XXX,XX +XXX,XX @@ bool job_is_cancelled(Job *job)
82
return job->cancelled;
83
}
84
85
+bool job_is_ready(Job *job)
86
+{
87
+ switch (job->status) {
88
+ case JOB_STATUS_UNDEFINED:
89
+ case JOB_STATUS_CREATED:
90
+ case JOB_STATUS_RUNNING:
91
+ case JOB_STATUS_PAUSED:
92
+ case JOB_STATUS_WAITING:
93
+ case JOB_STATUS_PENDING:
94
+ case JOB_STATUS_ABORTING:
95
+ case JOB_STATUS_CONCLUDED:
96
+ case JOB_STATUS_NULL:
97
+ return false;
98
+ case JOB_STATUS_READY:
99
+ case JOB_STATUS_STANDBY:
100
+ return true;
101
+ default:
102
+ g_assert_not_reached();
103
+ }
104
+ return false;
105
+}
106
+
107
bool job_is_completed(Job *job)
108
{
109
switch (job->status) {
110
diff --git a/qemu-img.c b/qemu-img.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/qemu-img.c
113
+++ b/qemu-img.c
114
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
115
aio_poll(aio_context, true);
116
qemu_progress_print(job->len ?
117
((float)job->offset / job->len * 100.f) : 0.0f, 0);
118
- } while (!job->ready && !job_is_completed(&job->job));
119
+ } while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
120
121
if (!job_is_completed(&job->job)) {
122
ret = job_complete_sync(&job->job, errp);
123
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
124
index XXXXXXX..XXXXXXX 100644
125
--- a/tests/test-blockjob.c
126
+++ b/tests/test-blockjob.c
127
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
128
goto defer;
129
}
130
131
- if (!s->common.ready && s->should_converge) {
132
+ if (!job_is_ready(&s->common.job) && s->should_converge) {
133
block_job_event_ready(&s->common);
134
}
135
136
--
137
2.13.6
138
139
diff view generated by jsdifflib
Deleted patch
1
The transition to the READY state was still performed in the BlockJob
2
layer, in the same function that sent the BLOCK_JOB_READY QMP event.
3
1
4
This patch brings the state transition to the Job layer and implements
5
the QMP event using a notifier called from the Job layer, like we
6
already do for other events related to state transitions.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/blockjob.h | 3 +++
12
include/block/blockjob_int.h | 8 --------
13
include/qemu/job.h | 9 ++++++---
14
block/mirror.c | 6 +++---
15
blockjob.c | 33 ++++++++++++++++++---------------
16
job.c | 16 +++++++++++++---
17
tests/test-bdrv-drain.c | 2 +-
18
tests/test-blockjob.c | 2 +-
19
8 files changed, 45 insertions(+), 34 deletions(-)
20
21
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
22
index XXXXXXX..XXXXXXX 100644
23
--- a/include/block/blockjob.h
24
+++ b/include/block/blockjob.h
25
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
26
/** Called when the job transitions to PENDING */
27
Notifier pending_notifier;
28
29
+ /** Called when the job transitions to READY */
30
+ Notifier ready_notifier;
31
+
32
/** BlockDriverStates that are involved in this block job */
33
GSList *nodes;
34
} BlockJob;
35
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
36
index XXXXXXX..XXXXXXX 100644
37
--- a/include/block/blockjob_int.h
38
+++ b/include/block/blockjob_int.h
39
@@ -XXX,XX +XXX,XX @@ void block_job_drain(Job *job);
40
int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n);
41
42
/**
43
- * block_job_event_ready:
44
- * @job: The job which is now ready to be completed.
45
- *
46
- * Send a BLOCK_JOB_READY event for the specified job.
47
- */
48
-void block_job_event_ready(BlockJob *job);
49
-
50
-/**
51
* block_job_error_action:
52
* @job: The job to signal an error for.
53
* @on_err: The error action setting.
54
diff --git a/include/qemu/job.h b/include/qemu/job.h
55
index XXXXXXX..XXXXXXX 100644
56
--- a/include/qemu/job.h
57
+++ b/include/qemu/job.h
58
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
59
/** Notifiers called when the job transitions to PENDING */
60
NotifierList on_pending;
61
62
+ /** Notifiers called when the job transitions to READY */
63
+ NotifierList on_ready;
64
+
65
/** Element of the list of jobs */
66
QLIST_ENTRY(Job) job_list;
67
68
@@ -XXX,XX +XXX,XX @@ int job_apply_verb(Job *job, JobVerb verb, Error **errp);
69
/** The @job could not be started, free it. */
70
void job_early_fail(Job *job);
71
72
+/** Moves the @job from RUNNING to READY */
73
+void job_transition_to_ready(Job *job);
74
+
75
/**
76
* @job: The job being completed.
77
* @ret: The status code.
78
@@ -XXX,XX +XXX,XX @@ void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
79
*/
80
int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp);
81
82
-/* TODO To be removed from the public interface */
83
-void job_state_transition(Job *job, JobStatus s1);
84
-
85
#endif
86
diff --git a/block/mirror.c b/block/mirror.c
87
index XXXXXXX..XXXXXXX 100644
88
--- a/block/mirror.c
89
+++ b/block/mirror.c
90
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
91
}
92
93
if (s->bdev_length == 0) {
94
- /* Report BLOCK_JOB_READY and wait for complete. */
95
- block_job_event_ready(&s->common);
96
+ /* Transition to the READY state and wait for complete. */
97
+ job_transition_to_ready(&s->common.job);
98
s->synced = true;
99
while (!job_is_cancelled(&s->common.job) && !s->should_complete) {
100
job_yield(&s->common.job);
101
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
102
* report completion. This way, block-job-cancel will leave
103
* the target in a consistent state.
104
*/
105
- block_job_event_ready(&s->common);
106
+ job_transition_to_ready(&s->common.job);
107
s->synced = true;
108
}
109
110
diff --git a/blockjob.c b/blockjob.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/blockjob.c
113
+++ b/blockjob.c
114
@@ -XXX,XX +XXX,XX @@ static void block_job_event_pending(Notifier *n, void *opaque)
115
&error_abort);
116
}
117
118
+static void block_job_event_ready(Notifier *n, void *opaque)
119
+{
120
+ BlockJob *job = opaque;
121
+
122
+ if (block_job_is_internal(job)) {
123
+ return;
124
+ }
125
+
126
+ qapi_event_send_block_job_ready(job_type(&job->job),
127
+ job->job.id,
128
+ job->len,
129
+ job->offset,
130
+ job->speed, &error_abort);
131
+}
132
+
133
+
134
/*
135
* API for block job drivers and the block layer. These functions are
136
* declared in blockjob_int.h.
137
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
138
job->finalize_cancelled_notifier.notify = block_job_event_cancelled;
139
job->finalize_completed_notifier.notify = block_job_event_completed;
140
job->pending_notifier.notify = block_job_event_pending;
141
+ job->ready_notifier.notify = block_job_event_ready;
142
143
notifier_list_add(&job->job.on_finalize_cancelled,
144
&job->finalize_cancelled_notifier);
145
notifier_list_add(&job->job.on_finalize_completed,
146
&job->finalize_completed_notifier);
147
notifier_list_add(&job->job.on_pending, &job->pending_notifier);
148
+ notifier_list_add(&job->job.on_ready, &job->ready_notifier);
149
150
error_setg(&job->blocker, "block device is in use by block job: %s",
151
job_type_str(&job->job));
152
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(Job *job)
153
block_job_iostatus_reset(bjob);
154
}
155
156
-void block_job_event_ready(BlockJob *job)
157
-{
158
- job_state_transition(&job->job, JOB_STATUS_READY);
159
-
160
- if (block_job_is_internal(job)) {
161
- return;
162
- }
163
-
164
- qapi_event_send_block_job_ready(job_type(&job->job),
165
- job->job.id,
166
- job->len,
167
- job->offset,
168
- job->speed, &error_abort);
169
-}
170
-
171
BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
172
int is_read, int error)
173
{
174
diff --git a/job.c b/job.c
175
index XXXXXXX..XXXXXXX 100644
176
--- a/job.c
177
+++ b/job.c
178
@@ -XXX,XX +XXX,XX @@ static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock)
179
return rc;
180
}
181
182
-
183
-/* TODO Make static once the whole state machine is in job.c */
184
-void job_state_transition(Job *job, JobStatus s1)
185
+static void job_state_transition(Job *job, JobStatus s1)
186
{
187
JobStatus s0 = job->status;
188
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
189
@@ -XXX,XX +XXX,XX @@ void *job_create(const char *job_id, const JobDriver *driver, JobTxn *txn,
190
notifier_list_init(&job->on_finalize_cancelled);
191
notifier_list_init(&job->on_finalize_completed);
192
notifier_list_init(&job->on_pending);
193
+ notifier_list_init(&job->on_ready);
194
195
job_state_transition(job, JOB_STATUS_CREATED);
196
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
197
@@ -XXX,XX +XXX,XX @@ static void job_event_pending(Job *job)
198
notifier_list_notify(&job->on_pending, job);
199
}
200
201
+static void job_event_ready(Job *job)
202
+{
203
+ notifier_list_notify(&job->on_ready, job);
204
+}
205
+
206
void job_enter_cond(Job *job, bool(*fn)(Job *job))
207
{
208
if (!job_started(job)) {
209
@@ -XXX,XX +XXX,XX @@ static int job_transition_to_pending(Job *job)
210
return 0;
211
}
212
213
+void job_transition_to_ready(Job *job)
214
+{
215
+ job_state_transition(job, JOB_STATUS_READY);
216
+ job_event_ready(job);
217
+}
218
+
219
static void job_completed_txn_success(Job *job)
220
{
221
JobTxn *txn = job->txn;
222
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
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
}
241
242
if (!job_is_ready(&s->common.job) && s->should_converge) {
243
- block_job_event_ready(&s->common);
244
+ job_transition_to_ready(&s->common.job);
245
}
246
247
job_sleep_ns(&s->common.job, 100000);
248
--
249
2.13.6
250
251
diff view generated by jsdifflib
Deleted patch
1
BlockJob has fields .offset and .len, which are actually misnomers today
2
because they are no longer tied to block device sizes, but just progress
3
counters. As such they make a lot of sense in generic Jobs.
4
1
5
This patch moves the fields to Job and renames them to .progress_current
6
and .progress_total to describe their function better.
7
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
---
11
include/block/blockjob.h | 25 -------------------------
12
include/qemu/job.h | 28 ++++++++++++++++++++++++++++
13
block/backup.c | 8 ++++----
14
block/commit.c | 4 ++--
15
block/mirror.c | 4 ++--
16
block/stream.c | 4 ++--
17
blockjob.c | 26 ++++++++------------------
18
job.c | 10 ++++++++++
19
qemu-img.c | 8 ++++++--
20
9 files changed, 62 insertions(+), 55 deletions(-)
21
22
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
23
index XXXXXXX..XXXXXXX 100644
24
--- a/include/block/blockjob.h
25
+++ b/include/block/blockjob.h
26
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
27
/** Status that is published by the query-block-jobs QMP API */
28
BlockDeviceIoStatus iostatus;
29
30
- /** Offset that is published by the query-block-jobs QMP API */
31
- int64_t offset;
32
-
33
- /** Length that is published by the query-block-jobs QMP API */
34
- int64_t len;
35
-
36
/** Speed that was set with @block_job_set_speed. */
37
int64_t speed;
38
39
@@ -XXX,XX +XXX,XX @@ void block_job_remove_all_bdrv(BlockJob *job);
40
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp);
41
42
/**
43
- * block_job_progress_update:
44
- * @job: The job that has made progress
45
- * @done: How much progress the job made
46
- *
47
- * Updates the progress counter of the job.
48
- */
49
-void block_job_progress_update(BlockJob *job, uint64_t done);
50
-
51
-/**
52
- * block_job_progress_set_remaining:
53
- * @job: The job whose expected progress end value is set
54
- * @remaining: Expected end value of the progress counter of the job
55
- *
56
- * Sets the expected end value of the progress counter of a job so that a
57
- * completion percentage can be calculated when the progress is updated.
58
- */
59
-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining);
60
-
61
-/**
62
* block_job_query:
63
* @job: The job to get information about.
64
*
65
diff --git a/include/qemu/job.h b/include/qemu/job.h
66
index XXXXXXX..XXXXXXX 100644
67
--- a/include/qemu/job.h
68
+++ b/include/qemu/job.h
69
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
70
/** True if this job should automatically dismiss itself */
71
bool auto_dismiss;
72
73
+ /**
74
+ * Current progress. The unit is arbitrary as long as the ratio between
75
+ * progress_current and progress_total represents the estimated percentage
76
+ * of work already done.
77
+ */
78
+ int64_t progress_current;
79
+
80
+ /** Estimated progress_current value at the completion of the job */
81
+ int64_t progress_total;
82
+
83
/** ret code passed to job_completed. */
84
int ret;
85
86
@@ -XXX,XX +XXX,XX @@ void job_ref(Job *job);
87
*/
88
void job_unref(Job *job);
89
90
+/**
91
+ * @job: The job that has made progress
92
+ * @done: How much progress the job made since the last call
93
+ *
94
+ * Updates the progress counter of the job.
95
+ */
96
+void job_progress_update(Job *job, uint64_t done);
97
+
98
+/**
99
+ * @job: The job whose expected progress end value is set
100
+ * @remaining: Missing progress (on top of the current progress counter value)
101
+ * until the new expected end value is reached
102
+ *
103
+ * Sets the expected end value of the progress counter of a job so that a
104
+ * completion percentage can be calculated when the progress is updated.
105
+ */
106
+void job_progress_set_remaining(Job *job, uint64_t remaining);
107
+
108
/** To be called when a cancelled job is finalised. */
109
void job_event_cancelled(Job *job);
110
111
diff --git a/block/backup.c b/block/backup.c
112
index XXXXXXX..XXXXXXX 100644
113
--- a/block/backup.c
114
+++ b/block/backup.c
115
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_do_cow(BackupBlockJob *job,
116
* offset field is an opaque progress value, it is not a disk offset.
117
*/
118
job->bytes_read += n;
119
- block_job_progress_update(&job->common, n);
120
+ job_progress_update(&job->common.job, n);
121
}
122
123
out:
124
@@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
125
bdrv_set_dirty_iter(dbi, next_cluster * job->cluster_size);
126
}
127
128
- /* TODO block_job_progress_set_remaining() would make more sense */
129
- block_job_progress_update(&job->common,
130
+ /* TODO job_progress_set_remaining() would make more sense */
131
+ job_progress_update(&job->common.job,
132
job->len - hbitmap_count(job->copy_bitmap) * job->cluster_size);
133
134
bdrv_dirty_iter_free(dbi);
135
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
136
qemu_co_rwlock_init(&job->flush_rwlock);
137
138
nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
139
- block_job_progress_set_remaining(&job->common, job->len);
140
+ job_progress_set_remaining(&job->common.job, job->len);
141
142
job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
143
if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
144
diff --git a/block/commit.c b/block/commit.c
145
index XXXXXXX..XXXXXXX 100644
146
--- a/block/commit.c
147
+++ b/block/commit.c
148
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
149
if (len < 0) {
150
goto out;
151
}
152
- block_job_progress_set_remaining(&s->common, len);
153
+ job_progress_set_remaining(&s->common.job, len);
154
155
ret = base_len = blk_getlength(s->base);
156
if (base_len < 0) {
157
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn commit_run(void *opaque)
158
}
159
}
160
/* Publish progress */
161
- block_job_progress_update(&s->common, n);
162
+ job_progress_update(&s->common.job, n);
163
164
if (copy) {
165
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
166
diff --git a/block/mirror.c b/block/mirror.c
167
index XXXXXXX..XXXXXXX 100644
168
--- a/block/mirror.c
169
+++ b/block/mirror.c
170
@@ -XXX,XX +XXX,XX @@ static void mirror_iteration_done(MirrorOp *op, int ret)
171
bitmap_set(s->cow_bitmap, chunk_num, nb_chunks);
172
}
173
if (!s->initial_zeroing_ongoing) {
174
- block_job_progress_update(&s->common, op->bytes);
175
+ job_progress_update(&s->common.job, op->bytes);
176
}
177
}
178
qemu_iovec_destroy(&op->qiov);
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
180
/* cnt is the number of dirty bytes remaining and s->bytes_in_flight is
181
* the number of bytes currently being processed; together those are
182
* the current remaining operation length */
183
- block_job_progress_set_remaining(&s->common, s->bytes_in_flight + cnt);
184
+ job_progress_set_remaining(&s->common.job, s->bytes_in_flight + cnt);
185
186
/* Note that even when no rate limit is applied we need to yield
187
* periodically with no pending I/O so that bdrv_drain_all() returns.
188
diff --git a/block/stream.c b/block/stream.c
189
index XXXXXXX..XXXXXXX 100644
190
--- a/block/stream.c
191
+++ b/block/stream.c
192
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
193
ret = len;
194
goto out;
195
}
196
- block_job_progress_set_remaining(&s->common, len);
197
+ job_progress_set_remaining(&s->common.job, len);
198
199
buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE);
200
201
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn stream_run(void *opaque)
202
ret = 0;
203
204
/* Publish progress */
205
- block_job_progress_update(&s->common, n);
206
+ job_progress_update(&s->common.job, n);
207
if (copy) {
208
delay_ns = block_job_ratelimit_get_delay(&s->common, n);
209
} else {
210
diff --git a/blockjob.c b/blockjob.c
211
index XXXXXXX..XXXXXXX 100644
212
--- a/blockjob.c
213
+++ b/blockjob.c
214
@@ -XXX,XX +XXX,XX @@ int64_t block_job_ratelimit_get_delay(BlockJob *job, uint64_t n)
215
return ratelimit_calculate_delay(&job->limit, n);
216
}
217
218
-void block_job_progress_update(BlockJob *job, uint64_t done)
219
-{
220
- job->offset += done;
221
-}
222
-
223
-void block_job_progress_set_remaining(BlockJob *job, uint64_t remaining)
224
-{
225
- job->len = job->offset + remaining;
226
-}
227
-
228
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
229
{
230
BlockJobInfo *info;
231
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
232
info = g_new0(BlockJobInfo, 1);
233
info->type = g_strdup(job_type_str(&job->job));
234
info->device = g_strdup(job->job.id);
235
- info->len = job->len;
236
info->busy = atomic_read(&job->job.busy);
237
info->paused = job->job.pause_count > 0;
238
- info->offset = job->offset;
239
+ info->offset = job->job.progress_current;
240
+ info->len = job->job.progress_total;
241
info->speed = job->speed;
242
info->io_status = job->iostatus;
243
info->ready = job_is_ready(&job->job),
244
@@ -XXX,XX +XXX,XX @@ static void block_job_event_cancelled(Notifier *n, void *opaque)
245
246
qapi_event_send_block_job_cancelled(job_type(&job->job),
247
job->job.id,
248
- job->len,
249
- job->offset,
250
+ job->job.progress_total,
251
+ job->job.progress_current,
252
job->speed,
253
&error_abort);
254
}
255
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(Notifier *n, void *opaque)
256
257
qapi_event_send_block_job_completed(job_type(&job->job),
258
job->job.id,
259
- job->len,
260
- job->offset,
261
+ job->job.progress_total,
262
+ job->job.progress_current,
263
job->speed,
264
!!msg,
265
msg,
266
@@ -XXX,XX +XXX,XX @@ static void block_job_event_ready(Notifier *n, void *opaque)
267
268
qapi_event_send_block_job_ready(job_type(&job->job),
269
job->job.id,
270
- job->len,
271
- job->offset,
272
+ job->job.progress_total,
273
+ job->job.progress_current,
274
job->speed, &error_abort);
275
}
276
277
diff --git a/job.c b/job.c
278
index XXXXXXX..XXXXXXX 100644
279
--- a/job.c
280
+++ b/job.c
281
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
282
}
283
}
284
285
+void job_progress_update(Job *job, uint64_t done)
286
+{
287
+ job->progress_current += done;
288
+}
289
+
290
+void job_progress_set_remaining(Job *job, uint64_t remaining)
291
+{
292
+ job->progress_total = job->progress_current + remaining;
293
+}
294
+
295
void job_event_cancelled(Job *job)
296
{
297
notifier_list_notify(&job->on_finalize_cancelled, job);
298
diff --git a/qemu-img.c b/qemu-img.c
299
index XXXXXXX..XXXXXXX 100644
300
--- a/qemu-img.c
301
+++ b/qemu-img.c
302
@@ -XXX,XX +XXX,XX @@ static void run_block_job(BlockJob *job, Error **errp)
303
aio_context_acquire(aio_context);
304
job_ref(&job->job);
305
do {
306
+ float progress = 0.0f;
307
aio_poll(aio_context, true);
308
- qemu_progress_print(job->len ?
309
- ((float)job->offset / job->len * 100.f) : 0.0f, 0);
310
+ if (job->job.progress_total) {
311
+ progress = (float)job->job.progress_current /
312
+ job->job.progress_total * 100.f;
313
+ }
314
+ qemu_progress_print(progress, 0);
315
} while (!job_is_ready(&job->job) && !job_is_completed(&job->job));
316
317
if (!job_is_completed(&job->job)) {
318
--
319
2.13.6
320
321
diff view generated by jsdifflib
Deleted patch
1
This adds a separate schema file for all job-related definitions that
2
aren't tied to the block layer.
3
1
4
For a start, move the enums JobType, JobStatus and JobVerb.
5
6
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
---
9
qapi/block-core.json | 90 +-----------------------------------------------
10
qapi/job.json | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++
11
qapi/qapi-schema.json | 1 +
12
MAINTAINERS | 1 +
13
Makefile | 9 +++++
14
Makefile.objs | 4 +++
15
6 files changed, 110 insertions(+), 89 deletions(-)
16
create mode 100644 qapi/job.json
17
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
19
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
21
+++ b/qapi/block-core.json
22
@@ -XXX,XX +XXX,XX @@
23
24
{ 'include': 'common.json' }
25
{ 'include': 'crypto.json' }
26
+{ 'include': 'job.json' }
27
{ 'include': 'sockets.json' }
28
29
##
30
@@ -XXX,XX +XXX,XX @@
31
'data': ['top', 'full', 'none', 'incremental'] }
32
33
##
34
-# @JobType:
35
-#
36
-# Type of a background job.
37
-#
38
-# @commit: block commit job type, see "block-commit"
39
-#
40
-# @stream: block stream job type, see "block-stream"
41
-#
42
-# @mirror: drive mirror job type, see "drive-mirror"
43
-#
44
-# @backup: drive backup job type, see "drive-backup"
45
-#
46
-# Since: 1.7
47
-##
48
-{ 'enum': 'JobType',
49
- 'data': ['commit', 'stream', 'mirror', 'backup'] }
50
-
51
-##
52
-# @JobVerb:
53
-#
54
-# Represents command verbs that can be applied to a job.
55
-#
56
-# @cancel: see @block-job-cancel
57
-#
58
-# @pause: see @block-job-pause
59
-#
60
-# @resume: see @block-job-resume
61
-#
62
-# @set-speed: see @block-job-set-speed
63
-#
64
-# @complete: see @block-job-complete
65
-#
66
-# @dismiss: see @block-job-dismiss
67
-#
68
-# @finalize: see @block-job-finalize
69
-#
70
-# Since: 2.12
71
-##
72
-{ 'enum': 'JobVerb',
73
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
74
- 'finalize' ] }
75
-
76
-##
77
-# @JobStatus:
78
-#
79
-# Indicates the present state of a given job in its lifetime.
80
-#
81
-# @undefined: Erroneous, default state. Should not ever be visible.
82
-#
83
-# @created: The job has been created, but not yet started.
84
-#
85
-# @running: The job is currently running.
86
-#
87
-# @paused: The job is running, but paused. The pause may be requested by
88
-# either the QMP user or by internal processes.
89
-#
90
-# @ready: The job is running, but is ready for the user to signal completion.
91
-# This is used for long-running jobs like mirror that are designed to
92
-# run indefinitely.
93
-#
94
-# @standby: The job is ready, but paused. This is nearly identical to @paused.
95
-# The job may return to @ready or otherwise be canceled.
96
-#
97
-# @waiting: The job is waiting for other jobs in the transaction to converge
98
-# to the waiting state. This status will likely not be visible for
99
-# the last job in a transaction.
100
-#
101
-# @pending: The job has finished its work, but has finalization steps that it
102
-# needs to make prior to completing. These changes may require
103
-# manual intervention by the management process if manual was set
104
-# to true. These changes may still fail.
105
-#
106
-# @aborting: The job is in the process of being aborted, and will finish with
107
-# an error. The job will afterwards report that it is @concluded.
108
-# This status may not be visible to the management process.
109
-#
110
-# @concluded: The job has finished all work. If manual was set to true, the job
111
-# will remain in the query list until it is dismissed.
112
-#
113
-# @null: The job is in the process of being dismantled. This state should not
114
-# ever be visible externally.
115
-#
116
-# Since: 2.12
117
-##
118
-{ 'enum': 'JobStatus',
119
- 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
120
- 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
121
-
122
-##
123
# @BlockJobInfo:
124
#
125
# Information about a long-running block device operation.
126
diff --git a/qapi/job.json b/qapi/job.json
127
new file mode 100644
128
index XXXXXXX..XXXXXXX
129
--- /dev/null
130
+++ b/qapi/job.json
131
@@ -XXX,XX +XXX,XX @@
132
+# -*- Mode: Python -*-
133
+
134
+##
135
+# == Background jobs
136
+##
137
+
138
+##
139
+# @JobType:
140
+#
141
+# Type of a background job.
142
+#
143
+# @commit: block commit job type, see "block-commit"
144
+#
145
+# @stream: block stream job type, see "block-stream"
146
+#
147
+# @mirror: drive mirror job type, see "drive-mirror"
148
+#
149
+# @backup: drive backup job type, see "drive-backup"
150
+#
151
+# Since: 1.7
152
+##
153
+{ 'enum': 'JobType',
154
+ 'data': ['commit', 'stream', 'mirror', 'backup'] }
155
+
156
+##
157
+# @JobStatus:
158
+#
159
+# Indicates the present state of a given job in its lifetime.
160
+#
161
+# @undefined: Erroneous, default state. Should not ever be visible.
162
+#
163
+# @created: The job has been created, but not yet started.
164
+#
165
+# @running: The job is currently running.
166
+#
167
+# @paused: The job is running, but paused. The pause may be requested by
168
+# either the QMP user or by internal processes.
169
+#
170
+# @ready: The job is running, but is ready for the user to signal completion.
171
+# This is used for long-running jobs like mirror that are designed to
172
+# run indefinitely.
173
+#
174
+# @standby: The job is ready, but paused. This is nearly identical to @paused.
175
+# The job may return to @ready or otherwise be canceled.
176
+#
177
+# @waiting: The job is waiting for other jobs in the transaction to converge
178
+# to the waiting state. This status will likely not be visible for
179
+# the last job in a transaction.
180
+#
181
+# @pending: The job has finished its work, but has finalization steps that it
182
+# needs to make prior to completing. These changes may require
183
+# manual intervention by the management process if manual was set
184
+# to true. These changes may still fail.
185
+#
186
+# @aborting: The job is in the process of being aborted, and will finish with
187
+# an error. The job will afterwards report that it is @concluded.
188
+# This status may not be visible to the management process.
189
+#
190
+# @concluded: The job has finished all work. If manual was set to true, the job
191
+# will remain in the query list until it is dismissed.
192
+#
193
+# @null: The job is in the process of being dismantled. This state should not
194
+# ever be visible externally.
195
+#
196
+# Since: 2.12
197
+##
198
+{ 'enum': 'JobStatus',
199
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
200
+ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
201
+
202
+##
203
+# @JobVerb:
204
+#
205
+# Represents command verbs that can be applied to a job.
206
+#
207
+# @cancel: see @block-job-cancel
208
+#
209
+# @pause: see @block-job-pause
210
+#
211
+# @resume: see @block-job-resume
212
+#
213
+# @set-speed: see @block-job-set-speed
214
+#
215
+# @complete: see @block-job-complete
216
+#
217
+# @dismiss: see @block-job-dismiss
218
+#
219
+# @finalize: see @block-job-finalize
220
+#
221
+# Since: 2.12
222
+##
223
+{ 'enum': 'JobVerb',
224
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
225
+ 'finalize' ] }
226
diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json
227
index XXXXXXX..XXXXXXX 100644
228
--- a/qapi/qapi-schema.json
229
+++ b/qapi/qapi-schema.json
230
@@ -XXX,XX +XXX,XX @@
231
{ 'include': 'crypto.json' }
232
{ 'include': 'block.json' }
233
{ 'include': 'char.json' }
234
+{ 'include': 'job.json' }
235
{ 'include': 'net.json' }
236
{ 'include': 'rocker.json' }
237
{ 'include': 'tpm.json' }
238
diff --git a/MAINTAINERS b/MAINTAINERS
239
index XXXXXXX..XXXXXXX 100644
240
--- a/MAINTAINERS
241
+++ b/MAINTAINERS
242
@@ -XXX,XX +XXX,XX @@ F: block/backup.c
243
F: block/commit.c
244
F: block/stream.c
245
F: block/mirror.c
246
+F: qapi/job.json
247
T: git git://github.com/codyprime/qemu-kvm-jtc.git block
248
249
Block QAPI, monitor, command line
250
diff --git a/Makefile b/Makefile
251
index XXXXXXX..XXXXXXX 100644
252
--- a/Makefile
253
+++ b/Makefile
254
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c
255
GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c
256
GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c
257
GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c
258
+GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c
259
GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c
260
GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c
261
GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c
262
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c
263
GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c
264
GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c
265
GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c
266
+GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c
267
GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c
268
GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c
269
GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c
270
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c
271
GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c
272
GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c
273
GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c
274
+GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c
275
GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c
276
GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c
277
GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c
278
@@ -XXX,XX +XXX,XX @@ GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c
279
GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c
280
GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c
281
GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c
282
+GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c
283
GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c
284
GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c
285
GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c
286
@@ -XXX,XX +XXX,XX @@ qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \
287
$(SRC_PATH)/qapi/char.json \
288
$(SRC_PATH)/qapi/crypto.json \
289
$(SRC_PATH)/qapi/introspect.json \
290
+ $(SRC_PATH)/qapi/job.json \
291
$(SRC_PATH)/qapi/migration.json \
292
$(SRC_PATH)/qapi/misc.json \
293
$(SRC_PATH)/qapi/net.json \
294
@@ -XXX,XX +XXX,XX @@ qapi/qapi-types-char.c qapi/qapi-types-char.h \
295
qapi/qapi-types-common.c qapi/qapi-types-common.h \
296
qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \
297
qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \
298
+qapi/qapi-types-job.c qapi/qapi-types-job.h \
299
qapi/qapi-types-migration.c qapi/qapi-types-migration.h \
300
qapi/qapi-types-misc.c qapi/qapi-types-misc.h \
301
qapi/qapi-types-net.c qapi/qapi-types-net.h \
302
@@ -XXX,XX +XXX,XX @@ qapi/qapi-visit-char.c qapi/qapi-visit-char.h \
303
qapi/qapi-visit-common.c qapi/qapi-visit-common.h \
304
qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \
305
qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \
306
+qapi/qapi-visit-job.c qapi/qapi-visit-job.h \
307
qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \
308
qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \
309
qapi/qapi-visit-net.c qapi/qapi-visit-net.h \
310
@@ -XXX,XX +XXX,XX @@ qapi/qapi-commands-char.c qapi/qapi-commands-char.h \
311
qapi/qapi-commands-common.c qapi/qapi-commands-common.h \
312
qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \
313
qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \
314
+qapi/qapi-commands-job.c qapi/qapi-commands-job.h \
315
qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \
316
qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \
317
qapi/qapi-commands-net.c qapi/qapi-commands-net.h \
318
@@ -XXX,XX +XXX,XX @@ qapi/qapi-events-char.c qapi/qapi-events-char.h \
319
qapi/qapi-events-common.c qapi/qapi-events-common.h \
320
qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \
321
qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \
322
+qapi/qapi-events-job.c qapi/qapi-events-job.h \
323
qapi/qapi-events-migration.c qapi/qapi-events-migration.h \
324
qapi/qapi-events-misc.c qapi/qapi-events-misc.h \
325
qapi/qapi-events-net.c qapi/qapi-events-net.h \
326
diff --git a/Makefile.objs b/Makefile.objs
327
index XXXXXXX..XXXXXXX 100644
328
--- a/Makefile.objs
329
+++ b/Makefile.objs
330
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-types-char.o
331
util-obj-y += qapi/qapi-types-common.o
332
util-obj-y += qapi/qapi-types-crypto.o
333
util-obj-y += qapi/qapi-types-introspect.o
334
+util-obj-y += qapi/qapi-types-job.o
335
util-obj-y += qapi/qapi-types-migration.o
336
util-obj-y += qapi/qapi-types-misc.o
337
util-obj-y += qapi/qapi-types-net.o
338
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-visit-char.o
339
util-obj-y += qapi/qapi-visit-common.o
340
util-obj-y += qapi/qapi-visit-crypto.o
341
util-obj-y += qapi/qapi-visit-introspect.o
342
+util-obj-y += qapi/qapi-visit-job.o
343
util-obj-y += qapi/qapi-visit-migration.o
344
util-obj-y += qapi/qapi-visit-misc.o
345
util-obj-y += qapi/qapi-visit-net.o
346
@@ -XXX,XX +XXX,XX @@ util-obj-y += qapi/qapi-events-char.o
347
util-obj-y += qapi/qapi-events-common.o
348
util-obj-y += qapi/qapi-events-crypto.o
349
util-obj-y += qapi/qapi-events-introspect.o
350
+util-obj-y += qapi/qapi-events-job.o
351
util-obj-y += qapi/qapi-events-migration.o
352
util-obj-y += qapi/qapi-events-misc.o
353
util-obj-y += qapi/qapi-events-net.o
354
@@ -XXX,XX +XXX,XX @@ common-obj-y += qapi/qapi-commands-char.o
355
common-obj-y += qapi/qapi-commands-common.o
356
common-obj-y += qapi/qapi-commands-crypto.o
357
common-obj-y += qapi/qapi-commands-introspect.o
358
+common-obj-y += qapi/qapi-commands-job.o
359
common-obj-y += qapi/qapi-commands-migration.o
360
common-obj-y += qapi/qapi-commands-misc.o
361
common-obj-y += qapi/qapi-commands-net.o
362
--
363
2.13.6
364
365
diff view generated by jsdifflib
Deleted patch
1
This adds a QMP event that is emitted whenever a job transitions from
2
one status to another.
3
1
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
---
7
qapi/job.json | 14 ++++
8
job.c | 10 +++
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
32
diff --git a/qapi/job.json b/qapi/job.json
33
index XXXXXXX..XXXXXXX 100644
34
--- a/qapi/job.json
35
+++ b/qapi/job.json
36
@@ -XXX,XX +XXX,XX @@
37
{ 'enum': 'JobVerb',
38
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
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
}
1145
}
1146
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1147
"seconds": TIMESTAMP,
1148
"microseconds": TIMESTAMP
1149
},
1150
+ "event": "JOB_STATUS_CHANGE",
1151
+ "data": {
1152
+ "status": "waiting",
1153
+ "id": "commit0"
1154
+ }
1155
+}
1156
+{
1157
+ "timestamp": {
1158
+ "seconds": TIMESTAMP,
1159
+ "microseconds": TIMESTAMP
1160
+ },
1161
+ "event": "JOB_STATUS_CHANGE",
1162
+ "data": {
1163
+ "status": "pending",
1164
+ "id": "commit0"
1165
+ }
1166
+}
1167
+{
1168
+ "timestamp": {
1169
+ "seconds": TIMESTAMP,
1170
+ "microseconds": TIMESTAMP
1171
+ },
1172
"event": "BLOCK_JOB_COMPLETED",
1173
"data": {
1174
"device": "commit0",
1175
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1176
"type": "commit"
1177
}
1178
}
1179
+{
1180
+ "timestamp": {
1181
+ "seconds": TIMESTAMP,
1182
+ "microseconds": TIMESTAMP
1183
+ },
1184
+ "event": "JOB_STATUS_CHANGE",
1185
+ "data": {
1186
+ "status": "concluded",
1187
+ "id": "commit0"
1188
+ }
1189
+}
1190
+{
1191
+ "timestamp": {
1192
+ "seconds": TIMESTAMP,
1193
+ "microseconds": TIMESTAMP
1194
+ },
1195
+ "event": "JOB_STATUS_CHANGE",
1196
+ "data": {
1197
+ "status": "null",
1198
+ "id": "commit0"
1199
+ }
1200
+}
1201
1202
=== Check that both top and top2 point to base now ===
1203
1204
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1205
=== Perform commit job ===
1206
1207
{
1208
+ "timestamp": {
1209
+ "seconds": TIMESTAMP,
1210
+ "microseconds": TIMESTAMP
1211
+ },
1212
+ "event": "JOB_STATUS_CHANGE",
1213
+ "data": {
1214
+ "status": "created",
1215
+ "id": "commit0"
1216
+ }
1217
+}
1218
+{
1219
+ "timestamp": {
1220
+ "seconds": TIMESTAMP,
1221
+ "microseconds": TIMESTAMP
1222
+ },
1223
+ "event": "JOB_STATUS_CHANGE",
1224
+ "data": {
1225
+ "status": "running",
1226
+ "id": "commit0"
1227
+ }
1228
+}
1229
+{
1230
"return": {
1231
}
1232
}
1233
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1234
"seconds": TIMESTAMP,
1235
"microseconds": TIMESTAMP
1236
},
1237
+ "event": "JOB_STATUS_CHANGE",
1238
+ "data": {
1239
+ "status": "waiting",
1240
+ "id": "commit0"
1241
+ }
1242
+}
1243
+{
1244
+ "timestamp": {
1245
+ "seconds": TIMESTAMP,
1246
+ "microseconds": TIMESTAMP
1247
+ },
1248
+ "event": "JOB_STATUS_CHANGE",
1249
+ "data": {
1250
+ "status": "pending",
1251
+ "id": "commit0"
1252
+ }
1253
+}
1254
+{
1255
+ "timestamp": {
1256
+ "seconds": TIMESTAMP,
1257
+ "microseconds": TIMESTAMP
1258
+ },
1259
"event": "BLOCK_JOB_COMPLETED",
1260
"data": {
1261
"device": "commit0",
1262
@@ -XXX,XX +XXX,XX @@ wrote 65536/65536 bytes at offset 1048576
1263
"type": "commit"
1264
}
1265
}
1266
+{
1267
+ "timestamp": {
1268
+ "seconds": TIMESTAMP,
1269
+ "microseconds": TIMESTAMP
1270
+ },
1271
+ "event": "JOB_STATUS_CHANGE",
1272
+ "data": {
1273
+ "status": "concluded",
1274
+ "id": "commit0"
1275
+ }
1276
+}
1277
+{
1278
+ "timestamp": {
1279
+ "seconds": TIMESTAMP,
1280
+ "microseconds": TIMESTAMP
1281
+ },
1282
+ "event": "JOB_STATUS_CHANGE",
1283
+ "data": {
1284
+ "status": "null",
1285
+ "id": "commit0"
1286
+ }
1287
+}
1288
1289
=== Check that both top and top2 point to base now ===
1290
1291
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
1292
index XXXXXXX..XXXXXXX 100644
1293
--- a/tests/qemu-iotests/iotests.py
1294
+++ b/tests/qemu-iotests/iotests.py
1295
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1296
self.assert_qmp(event, 'data/device', drive)
1297
result = event
1298
cancelled = True
1299
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1300
+ self.assert_qmp(event, 'data/id', drive)
1301
+
1302
1303
self.assert_no_active_block_jobs()
1304
return result
1305
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
1306
self.assert_qmp(event, 'data/offset', event['data']['len'])
1307
self.assert_no_active_block_jobs()
1308
return event
1309
+ elif event['event'] == 'JOB_STATUS_CHANGE':
1310
+ self.assert_qmp(event, 'data/id', drive)
1311
1312
def wait_ready(self, drive='drive0'):
1313
'''Wait until a block job BLOCK_JOB_READY event'''
1314
--
1315
2.13.6
1316
1317
diff view generated by jsdifflib
Deleted patch
1
qmp_to_opts() used to be a method of QMPTestCase, but recently we
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
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Max Reitz <mreitz@redhat.com>
7
---
8
tests/qemu-iotests/041 | 6 +++---
9
tests/qemu-iotests/155 | 2 +-
10
tests/qemu-iotests/iotests.py | 45 ++++++++++++++++++++++---------------------
11
3 files changed, 27 insertions(+), 26 deletions(-)
12
13
diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041
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
45
--- a/tests/qemu-iotests/iotests.py
46
+++ b/tests/qemu-iotests/iotests.py
47
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
48
return self.qmp('human-monitor-command',
49
command_line='qemu-io %s "%s"' % (drive, cmd))
50
51
+ def flatten_qmp_object(self, obj, output=None, basestr=''):
52
+ if output is None:
53
+ output = dict()
54
+ if isinstance(obj, list):
55
+ for i in range(len(obj)):
56
+ self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
57
+ elif isinstance(obj, dict):
58
+ for key in obj:
59
+ self.flatten_qmp_object(obj[key], output, basestr + key + '.')
60
+ else:
61
+ output[basestr[:-1]] = obj # Strip trailing '.'
62
+ return output
63
+
64
+ def qmp_to_opts(self, obj):
65
+ obj = self.flatten_qmp_object(obj)
66
+ output_list = list()
67
+ for key in obj:
68
+ output_list += [key + '=' + obj[key]]
69
+ return ','.join(output_list)
70
+
71
+
72
73
index_re = re.compile(r'([^\[]+)\[([^\]]+)\]')
74
75
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
76
self.fail('invalid index "%s" in path "%s" in "%s"' % (idx, path, str(d)))
77
return d
78
79
- def flatten_qmp_object(self, obj, output=None, basestr=''):
80
- if output is None:
81
- output = dict()
82
- if isinstance(obj, list):
83
- for i in range(len(obj)):
84
- self.flatten_qmp_object(obj[i], output, basestr + str(i) + '.')
85
- elif isinstance(obj, dict):
86
- for key in obj:
87
- self.flatten_qmp_object(obj[key], output, basestr + key + '.')
88
- else:
89
- output[basestr[:-1]] = obj # Strip trailing '.'
90
- return output
91
-
92
- def qmp_to_opts(self, obj):
93
- obj = self.flatten_qmp_object(obj)
94
- output_list = list()
95
- for key in obj:
96
- output_list += [key + '=' + obj[key]]
97
- return ','.join(output_list)
98
-
99
def assert_qmp_absent(self, d, path):
100
try:
101
result = self.dictpath(d, path)
102
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
103
'''Asserts that the given filename is a json: filename and that its
104
content is equal to the given reference object'''
105
self.assertEqual(json_filename[:5], 'json:')
106
- self.assertEqual(self.flatten_qmp_object(json.loads(json_filename[5:])),
107
- self.flatten_qmp_object(reference))
108
+ self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
109
+ self.vm.flatten_qmp_object(reference))
110
111
def cancel_and_wait(self, drive='drive0', force=False, resume=False):
112
'''Cancel a block job and wait for it to finish, returning the event'''
113
--
114
2.13.6
115
116
diff view generated by jsdifflib