1
The following changes since commit 22ef7ba8e8ce7fef297549b3defcac333742b804:
1
The following changes since commit 281f327487c9c9b1599f93c589a408bbf4a651b8:
2
2
3
Merge remote-tracking branch 'remotes/famz/tags/staging-pull-request' into staging (2018-03-13 11:42:45 +0000)
3
Merge remote-tracking branch 'remotes/vivier/tags/m68k-for-2.12-pull-request' into staging (2017-12-22 00:11:36 +0000)
4
4
5
are available in the git repository at:
5
are available in the git repository at:
6
6
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
7
git://repo.or.cz/qemu/kevin.git tags/for-upstream
8
8
9
for you to fetch changes up to be6c885842efded81a20f4ca24f0d4e123a80c00:
9
for you to fetch changes up to 1a63a907507fbbcfaee3f622907ec244b7eabda8:
10
10
11
block/mirror: change the semantic of 'force' of block-job-cancel (2018-03-13 16:54:47 +0100)
11
block: Keep nodes drained between reopen_queue/multiple (2017-12-22 15:05:32 +0100)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block layer patches
14
Block layer patches
15
15
16
----------------------------------------------------------------
16
----------------------------------------------------------------
17
Doug Gale (1):
18
nvme: Add tracing
19
20
Edgar Kaziakhmedov (1):
21
qcow2: get rid of qcow2_backing_read1 routine
22
17
Fam Zheng (2):
23
Fam Zheng (2):
18
block: Fix flags in reopen queue
24
block: Open backing image in force share mode for size probe
19
iotests: Add regression test for commit base locking
25
block: Remove unused bdrv_requests_pending
20
26
21
John Snow (21):
27
John Snow (1):
22
blockjobs: fix set-speed kick
28
iotests: fix 197 for vpc
23
blockjobs: model single jobs as transactions
24
Blockjobs: documentation touchup
25
blockjobs: add status enum
26
blockjobs: add state transition table
27
iotests: add pause_wait
28
blockjobs: add block_job_verb permission table
29
blockjobs: add ABORTING state
30
blockjobs: add CONCLUDED state
31
blockjobs: add NULL state
32
blockjobs: add block_job_dismiss
33
blockjobs: ensure abort is called for cancelled jobs
34
blockjobs: add commit, abort, clean helpers
35
blockjobs: add block_job_txn_apply function
36
blockjobs: add prepare callback
37
blockjobs: add waiting status
38
blockjobs: add PENDING status and event
39
blockjobs: add block-job-finalize
40
blockjobs: Expose manual property
41
iotests: test manual job dismissal
42
tests/test-blockjob: test cancellations
43
29
44
Kevin Wolf (14):
30
Kevin Wolf (27):
45
luks: Separate image file creation from formatting
31
block: Formats don't need CONSISTENT_READ with NO_IO
46
luks: Create block_crypto_co_create_generic()
32
block: Make bdrv_drain_invoke() recursive
47
luks: Support .bdrv_co_create
33
block: Call .drain_begin only once in bdrv_drain_all_begin()
48
luks: Turn invalid assertion into check
34
test-bdrv-drain: Test BlockDriver callbacks for drain
49
luks: Catch integer overflow for huge sizes
35
block: bdrv_drain_recurse(): Remove unused begin parameter
50
qemu-iotests: Test luks QMP image creation
36
block: Don't wait for requests in bdrv_drain*_end()
51
parallels: Support .bdrv_co_create
37
block: Unify order in drain functions
52
qemu-iotests: Enable write tests for parallels
38
block: Don't acquire AioContext in hmp_qemu_io()
53
qcow: Support .bdrv_co_create
39
block: Document that x-blockdev-change breaks quorum children list
54
qed: Support .bdrv_co_create
40
block: Assert drain_all is only called from main AioContext
55
vdi: Make comments consistent with other drivers
41
block: Make bdrv_drain() driver callbacks non-recursive
56
vhdx: Support .bdrv_co_create
42
test-bdrv-drain: Test callback for bdrv_drain
57
vpc: Support .bdrv_co_create
43
test-bdrv-drain: Test bs->quiesce_counter
58
vpc: Require aligned size in .bdrv_co_create
44
blockjob: Pause job on draining any job BDS
45
test-bdrv-drain: Test drain vs. block jobs
46
block: Don't block_job_pause_all() in bdrv_drain_all()
47
block: Nested drain_end must still call callbacks
48
test-bdrv-drain: Test nested drain sections
49
block: Don't notify parents in drain call chain
50
block: Add bdrv_subtree_drained_begin/end()
51
test-bdrv-drain: Tests for bdrv_subtree_drain
52
test-bdrv-drain: Test behaviour in coroutine context
53
test-bdrv-drain: Recursive draining with multiple parents
54
block: Allow graph changes in subtree drained section
55
test-bdrv-drain: Test graph changes in drained section
56
commit: Simplify reopen of base
57
block: Keep nodes drained between reopen_queue/multiple
59
58
60
Liang Li (1):
59
Thomas Huth (3):
61
block/mirror: change the semantic of 'force' of block-job-cancel
60
block: Remove the obsolete -drive boot=on|off parameter
61
block: Remove the deprecated -hdachs option
62
block: Mention -drive cyls/heads/secs/trans/serial/addr in deprecation chapter
62
63
63
Max Reitz (3):
64
qapi/block-core.json | 4 +
64
vdi: Pull option parsing from vdi_co_create
65
block/qcow2.h | 3 -
65
vdi: Move file creation to vdi_co_create_opts
66
include/block/block.h | 15 +-
66
vdi: Implement .bdrv_co_create
67
include/block/block_int.h | 6 +-
68
block.c | 75 ++++-
69
block/commit.c | 8 +-
70
block/io.c | 164 +++++++---
71
block/qcow2.c | 51 +--
72
block/replication.c | 6 +
73
blockdev.c | 11 -
74
blockjob.c | 22 +-
75
hmp.c | 6 -
76
hw/block/nvme.c | 349 +++++++++++++++++----
77
qemu-io-cmds.c | 3 +
78
tests/test-bdrv-drain.c | 651 +++++++++++++++++++++++++++++++++++++++
79
vl.c | 86 +-----
80
hw/block/trace-events | 93 ++++++
81
qemu-doc.texi | 29 +-
82
qemu-options.hx | 19 +-
83
tests/Makefile.include | 2 +
84
tests/qemu-iotests/197 | 4 +
85
tests/qemu-iotests/common.filter | 3 +-
86
22 files changed, 1294 insertions(+), 316 deletions(-)
87
create mode 100644 tests/test-bdrv-drain.c
67
88
68
qapi/block-core.json | 363 ++++++++++++++++++++++++++++++++++++++++--
69
include/block/blockjob.h | 71 ++++++++-
70
include/block/blockjob_int.h | 17 +-
71
block.c | 8 +
72
block/backup.c | 5 +-
73
block/commit.c | 2 +-
74
block/crypto.c | 150 ++++++++++++-----
75
block/mirror.c | 12 +-
76
block/parallels.c | 199 +++++++++++++++++------
77
block/qcow.c | 196 +++++++++++++++--------
78
block/qed.c | 204 ++++++++++++++++--------
79
block/stream.c | 2 +-
80
block/vdi.c | 147 +++++++++++++----
81
block/vhdx.c | 216 +++++++++++++++++++------
82
block/vpc.c | 241 +++++++++++++++++++++-------
83
blockdev.c | 71 +++++++--
84
blockjob.c | 358 +++++++++++++++++++++++++++++++++++------
85
tests/test-bdrv-drain.c | 5 +-
86
tests/test-blockjob-txn.c | 27 ++--
87
tests/test-blockjob.c | 233 ++++++++++++++++++++++++++-
88
block/trace-events | 7 +
89
hmp-commands.hx | 3 +-
90
tests/qemu-iotests/030 | 6 +-
91
tests/qemu-iotests/055 | 17 +-
92
tests/qemu-iotests/056 | 187 ++++++++++++++++++++++
93
tests/qemu-iotests/056.out | 4 +-
94
tests/qemu-iotests/109.out | 24 +--
95
tests/qemu-iotests/153 | 12 ++
96
tests/qemu-iotests/153.out | 5 +
97
tests/qemu-iotests/181 | 2 +-
98
tests/qemu-iotests/209 | 210 ++++++++++++++++++++++++
99
tests/qemu-iotests/209.out | 136 ++++++++++++++++
100
tests/qemu-iotests/check | 1 -
101
tests/qemu-iotests/common.rc | 2 +-
102
tests/qemu-iotests/group | 1 +
103
tests/qemu-iotests/iotests.py | 12 +-
104
36 files changed, 2642 insertions(+), 514 deletions(-)
105
create mode 100755 tests/qemu-iotests/209
106
create mode 100644 tests/qemu-iotests/209.out
107
diff view generated by jsdifflib
1
This makes the .bdrv_co_create(_opts) implementation of vdi look more
1
Commit 1f4ad7d fixed 'qemu-img info' for raw images that are currently
2
like the other recently converted block drivers.
2
in use as a mirror target. It is not enough for image formats, though,
3
as these still unconditionally request BLK_PERM_CONSISTENT_READ.
4
5
As this permission is geared towards whether the guest-visible data is
6
consistent, and has no impact on whether the metadata is sane, and
7
'qemu-img info' does not read guest-visible data (except for the raw
8
format), it makes sense to not require BLK_PERM_CONSISTENT_READ if there
9
is not going to be any guest I/O performed, regardless of image format.
3
10
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
---
12
---
7
block/vdi.c | 12 +++++++++---
13
block.c | 6 +++++-
8
1 file changed, 9 insertions(+), 3 deletions(-)
14
1 file changed, 5 insertions(+), 1 deletion(-)
9
15
10
diff --git a/block/vdi.c b/block/vdi.c
16
diff --git a/block.c b/block.c
11
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
12
--- a/block/vdi.c
18
--- a/block.c
13
+++ b/block/vdi.c
19
+++ b/block.c
14
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
20
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
15
21
assert(role == &child_backing || role == &child_file);
16
logout("\n");
22
17
23
if (!backing) {
18
- /* Read out options. */
24
+ int flags = bdrv_reopen_get_flags(reopen_queue, bs);
19
+ /* Validate options and set default values */
25
+
20
bytes = vdi_opts->size;
26
/* Apart from the modifications below, the same permissions are
21
if (vdi_opts->q_static) {
27
* forwarded and left alone as for filters */
22
image_type = VDI_TYPE_STATIC;
28
bdrv_filter_default_perms(bs, c, role, reopen_queue, perm, shared,
23
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
29
@@ -XXX,XX +XXX,XX @@ void bdrv_format_default_perms(BlockDriverState *bs, BdrvChild *c,
24
goto exit;
30
25
}
31
/* bs->file always needs to be consistent because of the metadata. We
26
32
* can never allow other users to resize or write to it. */
27
+ /* Create BlockBackend to write to the image */
33
- perm |= BLK_PERM_CONSISTENT_READ;
28
bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
34
+ if (!(flags & BDRV_O_NO_IO)) {
29
if (!bs_file) {
35
+ perm |= BLK_PERM_CONSISTENT_READ;
30
ret = -EIO;
36
+ }
31
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
37
shared &= ~(BLK_PERM_WRITE | BLK_PERM_RESIZE);
32
Error *local_err = NULL;
38
} else {
33
int ret;
39
/* We want consistent read from backing files if the parent needs it.
34
35
- /* Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
36
+ /* Parse options and convert legacy syntax.
37
+ *
38
+ * Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
39
* cluster-size is not part of the QAPI schema; therefore we have
40
* to parse it before creating the QAPI object. */
41
#if defined(CONFIG_VDI_BLOCK_SIZE)
42
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
43
44
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
45
46
+ /* Create and open the file (protocol layer) */
47
ret = bdrv_create_file(filename, opts, errp);
48
if (ret < 0) {
49
goto done;
50
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
51
goto done;
52
}
53
54
+ /* Silently round up size */
55
assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
56
create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
57
BDRV_SECTOR_SIZE);
58
59
+ /* Create the vdi image (format layer) */
60
ret = vdi_co_do_create(create_options, block_size, errp);
61
done:
62
QDECREF(qdict);
63
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = {
64
.bdrv_close = vdi_close,
65
.bdrv_reopen_prepare = vdi_reopen_prepare,
66
.bdrv_child_perm = bdrv_format_default_perms,
67
- .bdrv_co_create_opts = vdi_co_create_opts,
68
.bdrv_co_create = vdi_co_create,
69
+ .bdrv_co_create_opts = vdi_co_create_opts,
70
.bdrv_has_zero_init = bdrv_has_zero_init_1,
71
.bdrv_co_block_status = vdi_co_block_status,
72
.bdrv_make_empty = vdi_make_empty,
73
--
40
--
74
2.13.6
41
2.13.6
75
42
76
43
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
Presently, even if a job is canceled post-completion as a result of
3
VPC has some difficulty creating geometries of particular size.
4
a failing peer in a transaction, it will still call .commit because
4
However, we can indeed force it to use a literal one, so let's
5
nothing has updated or changed its return code.
5
do that for the sake of test 197, which is testing some specific
6
6
offsets.
7
The reason why this does not cause problems currently is because
8
backup's implementation of .commit checks for cancellation itself.
9
10
I'd like to simplify this contract:
11
12
(1) Abort is called if the job/transaction fails
13
(2) Commit is called if the job/transaction succeeds
14
15
To this end: A job's return code, if 0, will be forcibly set as
16
-ECANCELED if that job has already concluded. Remove the now
17
redundant check in the backup job implementation.
18
19
We need to check for cancellation in both block_job_completed
20
AND block_job_completed_single, because jobs may be cancelled between
21
those two calls; for instance in transactions. This also necessitates
22
an ABORTING -> ABORTING transition to be allowed.
23
24
The check in block_job_completed could be removed, but there's no
25
point in starting to attempt to succeed a transaction that we know
26
in advance will fail.
27
28
This does NOT affect mirror jobs that are "canceled" during their
29
synchronous phase. The mirror job itself forcibly sets the canceled
30
property to false prior to ceding control, so such cases will invoke
31
the "commit" callback.
32
7
33
Signed-off-by: John Snow <jsnow@redhat.com>
8
Signed-off-by: John Snow <jsnow@redhat.com>
34
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Eric Blake <eblake@redhat.com>
35
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
36
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Lukáš Doktor <ldoktor@redhat.com>
37
---
13
---
38
block/backup.c | 2 +-
14
tests/qemu-iotests/197 | 4 ++++
39
blockjob.c | 21 ++++++++++++++++-----
15
tests/qemu-iotests/common.filter | 3 ++-
40
block/trace-events | 1 +
16
2 files changed, 6 insertions(+), 1 deletion(-)
41
3 files changed, 18 insertions(+), 6 deletions(-)
42
17
43
diff --git a/block/backup.c b/block/backup.c
18
diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197
19
index XXXXXXX..XXXXXXX 100755
20
--- a/tests/qemu-iotests/197
21
+++ b/tests/qemu-iotests/197
22
@@ -XXX,XX +XXX,XX @@ echo '=== Copy-on-read ==='
23
echo
24
25
# Prep the images
26
+# VPC rounds image sizes to a specific geometry, force a specific size.
27
+if [ "$IMGFMT" = "vpc" ]; then
28
+ IMGOPTS=$(_optstr_add "$IMGOPTS" "force_size")
29
+fi
30
_make_test_img 4G
31
$QEMU_IO -c "write -P 55 3G 1k" "$TEST_IMG" | _filter_qemu_io
32
IMGPROTO=file IMGFMT=qcow2 IMGOPTS= TEST_IMG_FILE="$TEST_WRAP" \
33
diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter
44
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
45
--- a/block/backup.c
35
--- a/tests/qemu-iotests/common.filter
46
+++ b/block/backup.c
36
+++ b/tests/qemu-iotests/common.filter
47
@@ -XXX,XX +XXX,XX @@ static void backup_cleanup_sync_bitmap(BackupBlockJob *job, int ret)
37
@@ -XXX,XX +XXX,XX @@ _filter_img_create()
48
BdrvDirtyBitmap *bm;
38
-e "s# log_size=[0-9]\\+##g" \
49
BlockDriverState *bs = blk_bs(job->common.blk);
39
-e "s# refcount_bits=[0-9]\\+##g" \
50
40
-e "s# key-secret=[a-zA-Z0-9]\\+##g" \
51
- if (ret < 0 || block_job_is_cancelled(&job->common)) {
41
- -e "s# iter-time=[0-9]\\+##g"
52
+ if (ret < 0) {
42
+ -e "s# iter-time=[0-9]\\+##g" \
53
/* Merge the successor back into the parent, delete nothing. */
43
+ -e "s# force_size=\\(on\\|off\\)##g"
54
bm = bdrv_reclaim_dirty_bitmap(bs, job->sync_bitmap, NULL);
55
assert(bm);
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 @@ bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
61
/* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0},
62
/* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1, 0},
63
/* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
64
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1, 0},
65
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 1, 1, 0},
66
/* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 1},
67
/* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0},
68
};
69
@@ -XXX,XX +XXX,XX @@ static void block_job_conclude(BlockJob *job)
70
}
71
}
44
}
72
45
73
+static void block_job_update_rc(BlockJob *job)
46
_filter_img_info()
74
+{
75
+ if (!job->ret && block_job_is_cancelled(job)) {
76
+ job->ret = -ECANCELED;
77
+ }
78
+ if (job->ret) {
79
+ block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
80
+ }
81
+}
82
+
83
static void block_job_completed_single(BlockJob *job)
84
{
85
assert(job->completed);
86
87
- if (job->ret || block_job_is_cancelled(job)) {
88
- block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
89
- }
90
+ /* Ensure abort is called for late-transactional failures */
91
+ block_job_update_rc(job);
92
93
if (!job->ret) {
94
if (job->driver->commit) {
95
@@ -XXX,XX +XXX,XX @@ void block_job_completed(BlockJob *job, int ret)
96
assert(blk_bs(job->blk)->job == job);
97
job->completed = true;
98
job->ret = ret;
99
- if (ret < 0 || block_job_is_cancelled(job)) {
100
+ block_job_update_rc(job);
101
+ trace_block_job_completed(job, ret, job->ret);
102
+ if (job->ret) {
103
block_job_completed_txn_abort(job);
104
} else {
105
block_job_completed_txn_success(job);
106
diff --git a/block/trace-events b/block/trace-events
107
index XXXXXXX..XXXXXXX 100644
108
--- a/block/trace-events
109
+++ b/block/trace-events
110
@@ -XXX,XX +XXX,XX @@ bdrv_open_common(void *bs, const char *filename, int flags, const char *format_n
111
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
112
113
# blockjob.c
114
+block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
115
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)"
116
block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
117
118
--
47
--
119
2.13.6
48
2.13.6
120
49
121
50
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
This change separates bdrv_drain_invoke(), which calls the BlockDriver
2
drain callbacks, from bdrv_drain_recurse(). Instead, the function
3
performs its own recursion now.
2
4
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
One reason for this is that bdrv_drain_recurse() can be called multiple
6
times by bdrv_drain_all_begin(), but the callbacks may only be called
7
once. The separation is necessary to fix this bug.
8
9
The other reason is that we intend to go to a model where we call all
10
driver callbacks first, and only then start polling. This is not fully
11
achieved yet with this patch, as bdrv_drain_invoke() contains a
12
BDRV_POLL_WHILE() loop for the block driver callbacks, which can still
13
call callbacks for any unrelated event. It's a step in this direction
14
anyway.
15
16
Cc: qemu-stable@nongnu.org
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
5
---
19
---
6
qapi/block-core.json | 2 +-
20
block/io.c | 14 +++++++++++---
7
block/vdi.c | 24 +++++++++++++++++++-----
21
1 file changed, 11 insertions(+), 3 deletions(-)
8
2 files changed, 20 insertions(+), 6 deletions(-)
9
22
10
diff --git a/qapi/block-core.json b/qapi/block-core.json
23
diff --git a/block/io.c b/block/io.c
11
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
12
--- a/qapi/block-core.json
25
--- a/block/io.c
13
+++ b/qapi/block-core.json
26
+++ b/block/io.c
14
@@ -XXX,XX +XXX,XX @@
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
15
'sheepdog': 'BlockdevCreateOptionsSheepdog',
28
bdrv_wakeup(bs);
16
'ssh': 'BlockdevCreateOptionsSsh',
17
'throttle': 'BlockdevCreateNotSupported',
18
- 'vdi': 'BlockdevCreateNotSupported',
19
+ 'vdi': 'BlockdevCreateOptionsVdi',
20
'vhdx': 'BlockdevCreateNotSupported',
21
'vmdk': 'BlockdevCreateNotSupported',
22
'vpc': 'BlockdevCreateNotSupported',
23
diff --git a/block/vdi.c b/block/vdi.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/block/vdi.c
26
+++ b/block/vdi.c
27
@@ -XXX,XX +XXX,XX @@ nonallocating_write:
28
return ret;
29
}
29
}
30
30
31
-static int coroutine_fn vdi_co_do_create(BlockdevCreateOptionsVdi *vdi_opts,
31
+/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
32
+static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options,
32
static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
33
size_t block_size, Error **errp)
34
{
33
{
35
+ BlockdevCreateOptionsVdi *vdi_opts;
34
+ BdrvChild *child, *tmp;
36
int ret = 0;
35
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
37
uint64_t bytes = 0;
36
38
uint32_t blocks;
37
if (!bs->drv || (begin && !bs->drv->bdrv_co_drain_begin) ||
39
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptionsVdi *vdi_opts,
38
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
40
BlockBackend *blk = NULL;
39
data.co = qemu_coroutine_create(bdrv_drain_invoke_entry, &data);
41
uint32_t *bmap = NULL;
40
bdrv_coroutine_enter(bs, data.co);
42
41
BDRV_POLL_WHILE(bs, !data.done);
43
+ assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
44
+ vdi_opts = &create_options->u.vdi;
45
+
42
+
46
logout("\n");
43
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
47
44
+ bdrv_drain_invoke(child->bs, begin);
48
/* Read out options. */
45
+ }
49
@@ -XXX,XX +XXX,XX @@ exit:
50
return ret;
51
}
46
}
52
47
53
+static int coroutine_fn vdi_co_create(BlockdevCreateOptions *create_options,
48
static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
54
+ Error **errp)
49
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
55
+{
50
BdrvChild *child, *tmp;
56
+ return vdi_co_do_create(create_options, DEFAULT_CLUSTER_SIZE, errp);
51
bool waited;
57
+}
52
58
+
53
- /* Ensure any pending metadata writes are submitted to bs->file. */
59
static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
54
- bdrv_drain_invoke(bs, begin);
60
Error **errp)
55
-
61
{
56
/* Wait for drained requests to finish */
62
QDict *qdict = NULL;
57
waited = BDRV_POLL_WHILE(bs, atomic_read(&bs->in_flight) > 0);
63
- BlockdevCreateOptionsVdi *create_options = NULL;
58
64
+ BlockdevCreateOptions *create_options = NULL;
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
65
BlockDriverState *bs_file = NULL;
60
bdrv_parent_drained_begin(bs);
66
uint64_t block_size = DEFAULT_CLUSTER_SIZE;
67
Visitor *v;
68
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
69
goto done;
70
}
61
}
71
62
72
+ qdict_put_str(qdict, "driver", "vdi");
63
+ bdrv_drain_invoke(bs, true);
73
qdict_put_str(qdict, "file", bs_file->node_name);
64
bdrv_drain_recurse(bs, true);
74
65
}
75
/* Get the QAPI object */
66
76
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
67
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
77
- visit_type_BlockdevCreateOptionsVdi(v, NULL, &create_options, &local_err);
78
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
79
visit_free(v);
80
81
if (local_err) {
82
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
83
goto done;
84
}
68
}
85
69
86
- create_options->size = ROUND_UP(create_options->size, BDRV_SECTOR_SIZE);
70
bdrv_parent_drained_end(bs);
87
+ assert(create_options->driver == BLOCKDEV_DRIVER_VDI);
71
+ bdrv_drain_invoke(bs, false);
88
+ create_options->u.vdi.size = ROUND_UP(create_options->u.vdi.size,
72
bdrv_drain_recurse(bs, false);
89
+ BDRV_SECTOR_SIZE);
73
aio_enable_external(bdrv_get_aio_context(bs));
90
91
ret = vdi_co_do_create(create_options, block_size, errp);
92
done:
93
QDECREF(qdict);
94
- qapi_free_BlockdevCreateOptionsVdi(create_options);
95
+ qapi_free_BlockdevCreateOptions(create_options);
96
bdrv_unref(bs_file);
97
return ret;
98
}
74
}
99
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vdi = {
75
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
100
.bdrv_reopen_prepare = vdi_reopen_prepare,
76
aio_context_acquire(aio_context);
101
.bdrv_child_perm = bdrv_format_default_perms,
77
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
102
.bdrv_co_create_opts = vdi_co_create_opts,
78
if (aio_context == bdrv_get_aio_context(bs)) {
103
+ .bdrv_co_create = vdi_co_create,
79
+ /* FIXME Calling this multiple times is wrong */
104
.bdrv_has_zero_init = bdrv_has_zero_init_1,
80
+ bdrv_drain_invoke(bs, true);
105
.bdrv_co_block_status = vdi_co_block_status,
81
waited |= bdrv_drain_recurse(bs, true);
106
.bdrv_make_empty = vdi_make_empty,
82
}
83
}
84
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
85
aio_context_acquire(aio_context);
86
aio_enable_external(aio_context);
87
bdrv_parent_drained_end(bs);
88
+ bdrv_drain_invoke(bs, false);
89
bdrv_drain_recurse(bs, false);
90
aio_context_release(aio_context);
91
}
107
--
92
--
108
2.13.6
93
2.13.6
109
94
110
95
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to qcow, which
1
bdrv_drain_all_begin() used to call the .bdrv_co_drain_begin() driver
2
enables image creation over QMP.
2
callback inside its polling loop. This means that how many times it got
3
called for each node depended on long it had to poll the event loop.
3
4
5
This is obviously not right and results in nodes that stay drained even
6
after bdrv_drain_all_end(), which calls .bdrv_co_drain_begin() once per
7
node.
8
9
Fix bdrv_drain_all_begin() to call the callback only once, too.
10
11
Cc: qemu-stable@nongnu.org
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
Reviewed-by: Jeff Cody <jcody@redhat.com>
7
---
14
---
8
qapi/block-core.json | 21 +++++-
15
block/io.c | 3 +--
9
block/qcow.c | 196 ++++++++++++++++++++++++++++++++++-----------------
16
1 file changed, 1 insertion(+), 2 deletions(-)
10
2 files changed, 150 insertions(+), 67 deletions(-)
11
17
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
18
diff --git a/block/io.c b/block/io.c
13
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
20
--- a/block/io.c
15
+++ b/qapi/block-core.json
21
+++ b/block/io.c
16
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
17
'*cluster-size': 'size' } }
23
aio_context_acquire(aio_context);
18
24
bdrv_parent_drained_begin(bs);
19
##
25
aio_disable_external(aio_context);
20
+# @BlockdevCreateOptionsQcow:
26
+ bdrv_drain_invoke(bs, true);
21
+#
27
aio_context_release(aio_context);
22
+# Driver specific image creation options for qcow.
28
23
+#
29
if (!g_slist_find(aio_ctxs, aio_context)) {
24
+# @file Node to create the image format on
30
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
25
+# @size Size of the virtual disk in bytes
31
aio_context_acquire(aio_context);
26
+# @backing-file File name of the backing file if a backing file
32
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
27
+# should be used
33
if (aio_context == bdrv_get_aio_context(bs)) {
28
+# @encrypt Encryption options if the image should be encrypted
34
- /* FIXME Calling this multiple times is wrong */
29
+#
35
- bdrv_drain_invoke(bs, true);
30
+# Since: 2.12
36
waited |= bdrv_drain_recurse(bs, true);
31
+##
37
}
32
+{ 'struct': 'BlockdevCreateOptionsQcow',
38
}
33
+ 'data': { 'file': 'BlockdevRef',
34
+ 'size': 'size',
35
+ '*backing-file': 'str',
36
+ '*encrypt': 'QCryptoBlockCreateOptions' } }
37
+
38
+##
39
# @BlockdevQcow2Version:
40
#
41
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
42
@@ -XXX,XX +XXX,XX @@
43
'null-co': 'BlockdevCreateNotSupported',
44
'nvme': 'BlockdevCreateNotSupported',
45
'parallels': 'BlockdevCreateOptionsParallels',
46
+ 'qcow': 'BlockdevCreateOptionsQcow',
47
'qcow2': 'BlockdevCreateOptionsQcow2',
48
- 'qcow': 'BlockdevCreateNotSupported',
49
'qed': 'BlockdevCreateNotSupported',
50
'quorum': 'BlockdevCreateNotSupported',
51
'raw': 'BlockdevCreateNotSupported',
52
diff --git a/block/qcow.c b/block/qcow.c
53
index XXXXXXX..XXXXXXX 100644
54
--- a/block/qcow.c
55
+++ b/block/qcow.c
56
@@ -XXX,XX +XXX,XX @@
57
#include <zlib.h>
58
#include "qapi/qmp/qdict.h"
59
#include "qapi/qmp/qstring.h"
60
+#include "qapi/qobject-input-visitor.h"
61
+#include "qapi/qapi-visit-block-core.h"
62
#include "crypto/block.h"
63
#include "migration/blocker.h"
64
#include "block/crypto.h"
65
@@ -XXX,XX +XXX,XX @@ typedef struct BDRVQcowState {
66
Error *migration_blocker;
67
} BDRVQcowState;
68
69
+static QemuOptsList qcow_create_opts;
70
+
71
static int decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset);
72
73
static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)
74
@@ -XXX,XX +XXX,XX @@ static void qcow_close(BlockDriverState *bs)
75
error_free(s->migration_blocker);
76
}
77
78
-static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts,
79
- Error **errp)
80
+static int coroutine_fn qcow_co_create(BlockdevCreateOptions *opts,
81
+ Error **errp)
82
{
83
+ BlockdevCreateOptionsQcow *qcow_opts;
84
int header_size, backing_filename_len, l1_size, shift, i;
85
QCowHeader header;
86
uint8_t *tmp;
87
int64_t total_size = 0;
88
- char *backing_file = NULL;
89
- Error *local_err = NULL;
90
int ret;
91
+ BlockDriverState *bs;
92
BlockBackend *qcow_blk;
93
- char *encryptfmt = NULL;
94
- QDict *options;
95
- QDict *encryptopts = NULL;
96
- QCryptoBlockCreateOptions *crypto_opts = NULL;
97
QCryptoBlock *crypto = NULL;
98
99
- /* Read out options */
100
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
101
- BDRV_SECTOR_SIZE);
102
+ assert(opts->driver == BLOCKDEV_DRIVER_QCOW);
103
+ qcow_opts = &opts->u.qcow;
104
+
105
+ /* Sanity checks */
106
+ total_size = qcow_opts->size;
107
if (total_size == 0) {
108
error_setg(errp, "Image size is too small, cannot be zero length");
109
- ret = -EINVAL;
110
- goto cleanup;
111
+ return -EINVAL;
112
}
113
114
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
115
- encryptfmt = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT);
116
- if (encryptfmt) {
117
- if (qemu_opt_get(opts, BLOCK_OPT_ENCRYPT)) {
118
- error_setg(errp, "Options " BLOCK_OPT_ENCRYPT " and "
119
- BLOCK_OPT_ENCRYPT_FORMAT " are mutually exclusive");
120
- ret = -EINVAL;
121
- goto cleanup;
122
- }
123
- } else if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ENCRYPT, false)) {
124
- encryptfmt = g_strdup("aes");
125
+ if (qcow_opts->has_encrypt &&
126
+ qcow_opts->encrypt->format != Q_CRYPTO_BLOCK_FORMAT_QCOW)
127
+ {
128
+ error_setg(errp, "Unsupported encryption format");
129
+ return -EINVAL;
130
}
131
132
- ret = bdrv_create_file(filename, opts, &local_err);
133
- if (ret < 0) {
134
- error_propagate(errp, local_err);
135
- goto cleanup;
136
+ /* Create BlockBackend to write to the image */
137
+ bs = bdrv_open_blockdev_ref(qcow_opts->file, errp);
138
+ if (bs == NULL) {
139
+ return -EIO;
140
}
141
142
- qcow_blk = blk_new_open(filename, NULL, NULL,
143
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
144
- &local_err);
145
- if (qcow_blk == NULL) {
146
- error_propagate(errp, local_err);
147
- ret = -EIO;
148
- goto cleanup;
149
+ qcow_blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
150
+ ret = blk_insert_bs(qcow_blk, bs, errp);
151
+ if (ret < 0) {
152
+ goto exit;
153
}
154
-
155
blk_set_allow_write_beyond_eof(qcow_blk, true);
156
157
+ /* Create image format */
158
ret = blk_truncate(qcow_blk, 0, PREALLOC_MODE_OFF, errp);
159
if (ret < 0) {
160
goto exit;
161
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
162
header.size = cpu_to_be64(total_size);
163
header_size = sizeof(header);
164
backing_filename_len = 0;
165
- if (backing_file) {
166
- if (strcmp(backing_file, "fat:")) {
167
+ if (qcow_opts->has_backing_file) {
168
+ if (strcmp(qcow_opts->backing_file, "fat:")) {
169
header.backing_file_offset = cpu_to_be64(header_size);
170
- backing_filename_len = strlen(backing_file);
171
+ backing_filename_len = strlen(qcow_opts->backing_file);
172
header.backing_file_size = cpu_to_be32(backing_filename_len);
173
header_size += backing_filename_len;
174
} else {
175
/* special backing file for vvfat */
176
- g_free(backing_file);
177
- backing_file = NULL;
178
+ qcow_opts->has_backing_file = false;
179
}
180
header.cluster_bits = 9; /* 512 byte cluster to avoid copying
181
unmodified sectors */
182
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
183
184
header.l1_table_offset = cpu_to_be64(header_size);
185
186
- options = qemu_opts_to_qdict(opts, NULL);
187
- qdict_extract_subqdict(options, &encryptopts, "encrypt.");
188
- QDECREF(options);
189
- if (encryptfmt) {
190
- if (!g_str_equal(encryptfmt, "aes")) {
191
- error_setg(errp, "Unknown encryption format '%s', expected 'aes'",
192
- encryptfmt);
193
- ret = -EINVAL;
194
- goto exit;
195
- }
196
+ if (qcow_opts->has_encrypt) {
197
header.crypt_method = cpu_to_be32(QCOW_CRYPT_AES);
198
199
- crypto_opts = block_crypto_create_opts_init(
200
- Q_CRYPTO_BLOCK_FORMAT_QCOW, encryptopts, errp);
201
- if (!crypto_opts) {
202
- ret = -EINVAL;
203
- goto exit;
204
- }
205
-
206
- crypto = qcrypto_block_create(crypto_opts, "encrypt.",
207
+ crypto = qcrypto_block_create(qcow_opts->encrypt, "encrypt.",
208
NULL, NULL, NULL, errp);
209
if (!crypto) {
210
ret = -EINVAL;
211
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
212
goto exit;
213
}
214
215
- if (backing_file) {
216
+ if (qcow_opts->has_backing_file) {
217
ret = blk_pwrite(qcow_blk, sizeof(header),
218
- backing_file, backing_filename_len, 0);
219
+ qcow_opts->backing_file, backing_filename_len, 0);
220
if (ret != backing_filename_len) {
221
goto exit;
222
}
223
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn qcow_co_create_opts(const char *filename, QemuOpts *opts
224
ret = 0;
225
exit:
226
blk_unref(qcow_blk);
227
-cleanup:
228
- QDECREF(encryptopts);
229
- g_free(encryptfmt);
230
qcrypto_block_free(crypto);
231
- qapi_free_QCryptoBlockCreateOptions(crypto_opts);
232
- g_free(backing_file);
233
+ return ret;
234
+}
235
+
236
+static int coroutine_fn qcow_co_create_opts(const char *filename,
237
+ QemuOpts *opts, Error **errp)
238
+{
239
+ BlockdevCreateOptions *create_options = NULL;
240
+ BlockDriverState *bs = NULL;
241
+ QDict *qdict = NULL;
242
+ QObject *qobj;
243
+ Visitor *v;
244
+ const char *val;
245
+ Error *local_err = NULL;
246
+ int ret;
247
+
248
+ static const QDictRenames opt_renames[] = {
249
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
250
+ { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT },
251
+ { NULL, NULL },
252
+ };
253
+
254
+ /* Parse options and convert legacy syntax */
255
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qcow_create_opts, true);
256
+
257
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT);
258
+ if (val && !strcmp(val, "on")) {
259
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT, "qcow");
260
+ } else if (val && !strcmp(val, "off")) {
261
+ qdict_del(qdict, BLOCK_OPT_ENCRYPT);
262
+ }
263
+
264
+ val = qdict_get_try_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT);
265
+ if (val && !strcmp(val, "aes")) {
266
+ qdict_put_str(qdict, BLOCK_OPT_ENCRYPT_FORMAT, "qcow");
267
+ }
268
+
269
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
270
+ ret = -EINVAL;
271
+ goto fail;
272
+ }
273
+
274
+ /* Create and open the file (protocol layer) */
275
+ ret = bdrv_create_file(filename, opts, &local_err);
276
+ if (ret < 0) {
277
+ error_propagate(errp, local_err);
278
+ goto fail;
279
+ }
280
+
281
+ bs = bdrv_open(filename, NULL, NULL,
282
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
283
+ if (bs == NULL) {
284
+ ret = -EIO;
285
+ goto fail;
286
+ }
287
+
288
+ /* Now get the QAPI type BlockdevCreateOptions */
289
+ qdict_put_str(qdict, "driver", "qcow");
290
+ qdict_put_str(qdict, "file", bs->node_name);
291
+
292
+ qobj = qdict_crumple(qdict, errp);
293
+ QDECREF(qdict);
294
+ qdict = qobject_to_qdict(qobj);
295
+ if (qdict == NULL) {
296
+ ret = -EINVAL;
297
+ goto fail;
298
+ }
299
+
300
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
301
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
302
+ visit_free(v);
303
+
304
+ if (local_err) {
305
+ error_propagate(errp, local_err);
306
+ ret = -EINVAL;
307
+ goto fail;
308
+ }
309
+
310
+ /* Silently round up size */
311
+ assert(create_options->driver == BLOCKDEV_DRIVER_QCOW);
312
+ create_options->u.qcow.size =
313
+ ROUND_UP(create_options->u.qcow.size, BDRV_SECTOR_SIZE);
314
+
315
+ /* Create the qcow image (format layer) */
316
+ ret = qcow_co_create(create_options, errp);
317
+ if (ret < 0) {
318
+ goto fail;
319
+ }
320
+
321
+ ret = 0;
322
+fail:
323
+ QDECREF(qdict);
324
+ bdrv_unref(bs);
325
+ qapi_free_BlockdevCreateOptions(create_options);
326
return ret;
327
}
328
329
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qcow = {
330
.bdrv_close        = qcow_close,
331
.bdrv_child_perm = bdrv_format_default_perms,
332
.bdrv_reopen_prepare = qcow_reopen_prepare,
333
+ .bdrv_co_create = qcow_co_create,
334
.bdrv_co_create_opts = qcow_co_create_opts,
335
.bdrv_has_zero_init = bdrv_has_zero_init_1,
336
.supports_backing = true,
337
--
39
--
338
2.13.6
40
2.13.6
339
41
340
42
diff view generated by jsdifflib
1
Perform the rounding to match a CHS geometry only in the legacy code
1
This adds a test case that the BlockDriver callbacks for drain are
2
path in .bdrv_co_create_opts. QMP now requires that the user already
2
called in bdrv_drained_all_begin/end(), and that both of them are called
3
passes a CHS aligned image size, unless force-size=true is given.
3
exactly once.
4
5
CHS alignment is required to make the image compatible with Virtual PC,
6
but not for use with newer Microsoft hypervisors.
7
4
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
10
---
8
---
11
block/vpc.c | 113 +++++++++++++++++++++++++++++++++++++++++++-----------------
9
tests/test-bdrv-drain.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++
12
1 file changed, 82 insertions(+), 31 deletions(-)
10
tests/Makefile.include | 2 +
11
2 files changed, 139 insertions(+)
12
create mode 100644 tests/test-bdrv-drain.c
13
13
14
diff --git a/block/vpc.c b/block/vpc.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100644
15
new file mode 100644
16
--- a/block/vpc.c
16
index XXXXXXX..XXXXXXX
17
+++ b/block/vpc.c
17
--- /dev/null
18
@@ -XXX,XX +XXX,XX @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
18
+++ b/tests/test-bdrv-drain.c
19
return ret;
19
@@ -XXX,XX +XXX,XX @@
20
}
20
+/*
21
21
+ * Block node draining tests
22
+static int calculate_rounded_image_size(BlockdevCreateOptionsVpc *vpc_opts,
22
+ *
23
+ uint16_t *out_cyls,
23
+ * Copyright (c) 2017 Kevin Wolf <kwolf@redhat.com>
24
+ uint8_t *out_heads,
24
+ *
25
+ uint8_t *out_secs_per_cyl,
25
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
26
+ int64_t *out_total_sectors,
26
+ * of this software and associated documentation files (the "Software"), to deal
27
+ Error **errp)
27
+ * in the Software without restriction, including without limitation the rights
28
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
29
+ * copies of the Software, and to permit persons to whom the Software is
30
+ * furnished to do so, subject to the following conditions:
31
+ *
32
+ * The above copyright notice and this permission notice shall be included in
33
+ * all copies or substantial portions of the Software.
34
+ *
35
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
36
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
37
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
38
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
39
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
40
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
41
+ * THE SOFTWARE.
42
+ */
43
+
44
+#include "qemu/osdep.h"
45
+#include "block/block.h"
46
+#include "sysemu/block-backend.h"
47
+#include "qapi/error.h"
48
+
49
+typedef struct BDRVTestState {
50
+ int drain_count;
51
+} BDRVTestState;
52
+
53
+static void coroutine_fn bdrv_test_co_drain_begin(BlockDriverState *bs)
28
+{
54
+{
29
+ int64_t total_size = vpc_opts->size;
55
+ BDRVTestState *s = bs->opaque;
30
+ uint16_t cyls = 0;
56
+ s->drain_count++;
31
+ uint8_t heads = 0;
57
+}
32
+ uint8_t secs_per_cyl = 0;
33
+ int64_t total_sectors;
34
+ int i;
35
+
58
+
36
+ /*
59
+static void coroutine_fn bdrv_test_co_drain_end(BlockDriverState *bs)
37
+ * Calculate matching total_size and geometry. Increase the number of
60
+{
38
+ * sectors requested until we get enough (or fail). This ensures that
61
+ BDRVTestState *s = bs->opaque;
39
+ * qemu-img convert doesn't truncate images, but rather rounds up.
62
+ s->drain_count--;
40
+ *
63
+}
41
+ * If the image size can't be represented by a spec conformant CHS geometry,
42
+ * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
43
+ * the image size from the VHD footer to calculate total_sectors.
44
+ */
45
+ if (vpc_opts->force_size) {
46
+ /* This will force the use of total_size for sector count, below */
47
+ cyls = VHD_CHS_MAX_C;
48
+ heads = VHD_CHS_MAX_H;
49
+ secs_per_cyl = VHD_CHS_MAX_S;
50
+ } else {
51
+ total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
52
+ for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
53
+ calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
54
+ }
55
+ }
56
+
64
+
57
+ if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
65
+static void bdrv_test_close(BlockDriverState *bs)
58
+ total_sectors = total_size / BDRV_SECTOR_SIZE;
66
+{
59
+ /* Allow a maximum disk size of 2040 GiB */
67
+ BDRVTestState *s = bs->opaque;
60
+ if (total_sectors > VHD_MAX_SECTORS) {
68
+ g_assert_cmpint(s->drain_count, >, 0);
61
+ error_setg(errp, "Disk size is too large, max size is 2040 GiB");
69
+}
62
+ return -EFBIG;
63
+ }
64
+ } else {
65
+ total_sectors = (int64_t) cyls * heads * secs_per_cyl;
66
+ }
67
+
70
+
68
+ *out_total_sectors = total_sectors;
71
+static int coroutine_fn bdrv_test_co_preadv(BlockDriverState *bs,
69
+ if (out_cyls) {
72
+ uint64_t offset, uint64_t bytes,
70
+ *out_cyls = cyls;
73
+ QEMUIOVector *qiov, int flags)
71
+ *out_heads = heads;
74
+{
72
+ *out_secs_per_cyl = secs_per_cyl;
75
+ /* We want this request to stay until the polling loop in drain waits for
73
+ }
76
+ * it to complete. We need to sleep a while as bdrv_drain_invoke() comes
77
+ * first and polls its result, too, but it shouldn't accidentally complete
78
+ * this request yet. */
79
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000);
74
+
80
+
75
+ return 0;
81
+ return 0;
76
+}
82
+}
77
+
83
+
78
static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
84
+static BlockDriver bdrv_test = {
79
Error **errp)
85
+ .format_name = "test",
80
{
86
+ .instance_size = sizeof(BDRVTestState),
81
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
82
83
uint8_t buf[1024];
84
VHDFooter *footer = (VHDFooter *) buf;
85
- int i;
86
uint16_t cyls = 0;
87
uint8_t heads = 0;
88
uint8_t secs_per_cyl = 0;
89
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
90
}
91
blk_set_allow_write_beyond_eof(blk, true);
92
93
- /*
94
- * Calculate matching total_size and geometry. Increase the number of
95
- * sectors requested until we get enough (or fail). This ensures that
96
- * qemu-img convert doesn't truncate images, but rather rounds up.
97
- *
98
- * If the image size can't be represented by a spec conformant CHS geometry,
99
- * we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
100
- * the image size from the VHD footer to calculate total_sectors.
101
- */
102
- if (vpc_opts->force_size) {
103
- /* This will force the use of total_size for sector count, below */
104
- cyls = VHD_CHS_MAX_C;
105
- heads = VHD_CHS_MAX_H;
106
- secs_per_cyl = VHD_CHS_MAX_S;
107
- } else {
108
- total_sectors = MIN(VHD_MAX_GEOMETRY, total_size / BDRV_SECTOR_SIZE);
109
- for (i = 0; total_sectors > (int64_t)cyls * heads * secs_per_cyl; i++) {
110
- calculate_geometry(total_sectors + i, &cyls, &heads, &secs_per_cyl);
111
- }
112
+ /* Get geometry and check that it matches the image size*/
113
+ ret = calculate_rounded_image_size(vpc_opts, &cyls, &heads, &secs_per_cyl,
114
+ &total_sectors, errp);
115
+ if (ret < 0) {
116
+ goto out;
117
}
118
119
- if ((int64_t)cyls * heads * secs_per_cyl == VHD_MAX_GEOMETRY) {
120
- total_sectors = total_size / BDRV_SECTOR_SIZE;
121
- /* Allow a maximum disk size of 2040 GiB */
122
- if (total_sectors > VHD_MAX_SECTORS) {
123
- error_setg(errp, "Disk size is too large, max size is 2040 GiB");
124
- ret = -EFBIG;
125
- goto out;
126
- }
127
- } else {
128
- total_sectors = (int64_t)cyls * heads * secs_per_cyl;
129
- total_size = total_sectors * BDRV_SECTOR_SIZE;
130
+ if (total_size != total_sectors * BDRV_SECTOR_SIZE) {
131
+ error_setg(errp, "The requested image size cannot be represented in "
132
+ "CHS geometry");
133
+ error_append_hint(errp, "Try size=%llu or force-size=on (the "
134
+ "latter makes the image incompatible with "
135
+ "Virtual PC)",
136
+ total_sectors * BDRV_SECTOR_SIZE);
137
+ ret = -EINVAL;
138
+ goto out;
139
}
140
141
/* Prepare the Hard Disk Footer */
142
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename,
143
create_options->u.vpc.size =
144
ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
145
146
+ if (!create_options->u.vpc.force_size) {
147
+ int64_t total_sectors;
148
+ ret = calculate_rounded_image_size(&create_options->u.vpc, NULL, NULL,
149
+ NULL, &total_sectors, errp);
150
+ if (ret < 0) {
151
+ goto fail;
152
+ }
153
+
87
+
154
+ create_options->u.vpc.size = total_sectors * BDRV_SECTOR_SIZE;
88
+ .bdrv_close = bdrv_test_close,
155
+ }
89
+ .bdrv_co_preadv = bdrv_test_co_preadv,
156
+
90
+
91
+ .bdrv_co_drain_begin = bdrv_test_co_drain_begin,
92
+ .bdrv_co_drain_end = bdrv_test_co_drain_end,
93
+};
157
+
94
+
158
/* Create the vpc image (format layer) */
95
+static void aio_ret_cb(void *opaque, int ret)
159
ret = vpc_co_create(create_options, errp);
96
+{
160
97
+ int *aio_ret = opaque;
98
+ *aio_ret = ret;
99
+}
100
+
101
+static void test_drv_cb_drain_all(void)
102
+{
103
+ BlockBackend *blk;
104
+ BlockDriverState *bs;
105
+ BDRVTestState *s;
106
+ BlockAIOCB *acb;
107
+ int aio_ret;
108
+
109
+ QEMUIOVector qiov;
110
+ struct iovec iov = {
111
+ .iov_base = NULL,
112
+ .iov_len = 0,
113
+ };
114
+ qemu_iovec_init_external(&qiov, &iov, 1);
115
+
116
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
117
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
118
+ &error_abort);
119
+ s = bs->opaque;
120
+ blk_insert_bs(blk, bs, &error_abort);
121
+
122
+ /* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
123
+ g_assert_cmpint(s->drain_count, ==, 0);
124
+ bdrv_drain_all_begin();
125
+ g_assert_cmpint(s->drain_count, ==, 1);
126
+ bdrv_drain_all_end();
127
+ g_assert_cmpint(s->drain_count, ==, 0);
128
+
129
+ /* Now do the same while a request is pending */
130
+ aio_ret = -EINPROGRESS;
131
+ acb = blk_aio_preadv(blk, 0, &qiov, 0, aio_ret_cb, &aio_ret);
132
+ g_assert(acb != NULL);
133
+ g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
134
+
135
+ g_assert_cmpint(s->drain_count, ==, 0);
136
+ bdrv_drain_all_begin();
137
+ g_assert_cmpint(aio_ret, ==, 0);
138
+ g_assert_cmpint(s->drain_count, ==, 1);
139
+ bdrv_drain_all_end();
140
+ g_assert_cmpint(s->drain_count, ==, 0);
141
+
142
+ bdrv_unref(bs);
143
+ blk_unref(blk);
144
+}
145
+
146
+int main(int argc, char **argv)
147
+{
148
+ bdrv_init();
149
+ qemu_init_main_loop(&error_abort);
150
+
151
+ g_test_init(&argc, &argv, NULL);
152
+
153
+ g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
154
+
155
+ return g_test_run();
156
+}
157
diff --git a/tests/Makefile.include b/tests/Makefile.include
158
index XXXXXXX..XXXXXXX 100644
159
--- a/tests/Makefile.include
160
+++ b/tests/Makefile.include
161
@@ -XXX,XX +XXX,XX @@ gcov-files-test-thread-pool-y = thread-pool.c
162
gcov-files-test-hbitmap-y = util/hbitmap.c
163
check-unit-y += tests/test-hbitmap$(EXESUF)
164
gcov-files-test-hbitmap-y = blockjob.c
165
+check-unit-y += tests/test-bdrv-drain$(EXESUF)
166
check-unit-y += tests/test-blockjob$(EXESUF)
167
check-unit-y += tests/test-blockjob-txn$(EXESUF)
168
check-unit-y += tests/test-x86-cpuid$(EXESUF)
169
@@ -XXX,XX +XXX,XX @@ tests/test-coroutine$(EXESUF): tests/test-coroutine.o $(test-block-obj-y)
170
tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y)
171
tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y)
172
tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y)
173
+tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y)
174
tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y)
175
tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y)
176
tests/test-thread-pool$(EXESUF): tests/test-thread-pool.o $(test-block-obj-y)
161
--
177
--
162
2.13.6
178
2.13.6
163
179
164
180
diff view generated by jsdifflib
1
Everything that refers to the protocol layer or QemuOpts is moved out of
1
Now that the bdrv_drain_invoke() calls are pulled up to the callers of
2
block_crypto_create_generic(), so that the remaining function is
2
bdrv_drain_recurse(), the 'begin' parameter isn't needed any more.
3
suitable to be called by a .bdrv_co_create implementation.
4
5
LUKS is the only driver that actually implements the old interface, and
6
we don't intend to use it in any new drivers, so put the moved out code
7
directly into a LUKS function rather than creating a generic
8
intermediate one.
9
3
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
---
6
---
14
block/crypto.c | 95 +++++++++++++++++++++++++++++++++++++---------------------
7
block/io.c | 12 ++++++------
15
1 file changed, 61 insertions(+), 34 deletions(-)
8
1 file changed, 6 insertions(+), 6 deletions(-)
16
9
17
diff --git a/block/crypto.c b/block/crypto.c
10
diff --git a/block/io.c b/block/io.c
18
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
19
--- a/block/crypto.c
12
--- a/block/io.c
20
+++ b/block/crypto.c
13
+++ b/block/io.c
21
@@ -XXX,XX +XXX,XX @@ static int block_crypto_open_generic(QCryptoBlockFormat format,
14
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
15
}
22
}
16
}
23
17
24
18
-static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
25
-static int block_crypto_create_generic(QCryptoBlockFormat format,
19
+static bool bdrv_drain_recurse(BlockDriverState *bs)
26
- const char *filename,
27
- QemuOpts *opts,
28
- Error **errp)
29
+static int block_crypto_co_create_generic(BlockDriverState *bs,
30
+ int64_t size,
31
+ QCryptoBlockCreateOptions *opts,
32
+ Error **errp)
33
{
20
{
34
- int ret = -EINVAL;
21
BdrvChild *child, *tmp;
35
- QCryptoBlockCreateOptions *create_opts = NULL;
22
bool waited;
36
+ int ret;
23
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs, bool begin)
37
+ BlockBackend *blk;
24
*/
38
QCryptoBlock *crypto = NULL;
25
bdrv_ref(bs);
39
- struct BlockCryptoCreateData data = {
26
}
40
- .size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
27
- waited |= bdrv_drain_recurse(bs, begin);
41
- BDRV_SECTOR_SIZE),
28
+ waited |= bdrv_drain_recurse(bs);
42
- };
29
if (in_main_loop) {
43
- QDict *cryptoopts;
30
bdrv_unref(bs);
44
-
31
}
45
- /* Parse options */
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
46
- cryptoopts = qemu_opts_to_qdict(opts, NULL);
47
+ struct BlockCryptoCreateData data;
48
49
- create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
50
- if (!create_opts) {
51
- return -1;
52
- }
53
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
54
55
- /* Create protocol layer */
56
- ret = bdrv_create_file(filename, opts, errp);
57
+ ret = blk_insert_bs(blk, bs, errp);
58
if (ret < 0) {
59
- return ret;
60
+ goto cleanup;
61
}
33
}
62
34
63
- data.blk = blk_new_open(filename, NULL, NULL,
35
bdrv_drain_invoke(bs, true);
64
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
36
- bdrv_drain_recurse(bs, true);
65
- errp);
37
+ bdrv_drain_recurse(bs);
66
- if (!data.blk) {
67
- return -EINVAL;
68
- }
69
+ data = (struct BlockCryptoCreateData) {
70
+ .blk = blk,
71
+ .size = size,
72
+ };
73
74
- /* Create format layer */
75
- crypto = qcrypto_block_create(create_opts, NULL,
76
+ crypto = qcrypto_block_create(opts, NULL,
77
block_crypto_init_func,
78
block_crypto_write_func,
79
&data,
80
@@ -XXX,XX +XXX,XX @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
81
82
ret = 0;
83
cleanup:
84
- QDECREF(cryptoopts);
85
qcrypto_block_free(crypto);
86
- blk_unref(data.blk);
87
- qapi_free_QCryptoBlockCreateOptions(create_opts);
88
+ blk_unref(blk);
89
return ret;
90
}
38
}
91
39
92
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
40
void bdrv_drained_end(BlockDriverState *bs)
93
QemuOpts *opts,
41
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
94
Error **errp)
42
95
{
43
bdrv_parent_drained_end(bs);
96
- return block_crypto_create_generic(Q_CRYPTO_BLOCK_FORMAT_LUKS,
44
bdrv_drain_invoke(bs, false);
97
- filename, opts, errp);
45
- bdrv_drain_recurse(bs, false);
98
+ QCryptoBlockCreateOptions *create_opts = NULL;
46
+ bdrv_drain_recurse(bs);
99
+ BlockDriverState *bs = NULL;
47
aio_enable_external(bdrv_get_aio_context(bs));
100
+ QDict *cryptoopts;
101
+ int64_t size;
102
+ int ret;
103
+
104
+ /* Parse options */
105
+ size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0);
106
+
107
+ cryptoopts = qemu_opts_to_qdict_filtered(opts, NULL,
108
+ &block_crypto_create_opts_luks,
109
+ true);
110
+
111
+ create_opts = block_crypto_create_opts_init(Q_CRYPTO_BLOCK_FORMAT_LUKS,
112
+ cryptoopts, errp);
113
+ if (!create_opts) {
114
+ ret = -EINVAL;
115
+ goto fail;
116
+ }
117
+
118
+ /* Create protocol layer */
119
+ ret = bdrv_create_file(filename, opts, errp);
120
+ if (ret < 0) {
121
+ return ret;
122
+ }
123
+
124
+ bs = bdrv_open(filename, NULL, NULL,
125
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
126
+ if (!bs) {
127
+ ret = -EINVAL;
128
+ goto fail;
129
+ }
130
+
131
+ /* Create format layer */
132
+ ret = block_crypto_co_create_generic(bs, size, create_opts, errp);
133
+ if (ret < 0) {
134
+ goto fail;
135
+ }
136
+
137
+ ret = 0;
138
+fail:
139
+ bdrv_unref(bs);
140
+ qapi_free_QCryptoBlockCreateOptions(create_opts);
141
+ QDECREF(cryptoopts);
142
+ return ret;
143
}
48
}
144
49
145
static int block_crypto_get_info_luks(BlockDriverState *bs,
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
51
aio_context_acquire(aio_context);
52
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
53
if (aio_context == bdrv_get_aio_context(bs)) {
54
- waited |= bdrv_drain_recurse(bs, true);
55
+ waited |= bdrv_drain_recurse(bs);
56
}
57
}
58
aio_context_release(aio_context);
59
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
60
aio_enable_external(aio_context);
61
bdrv_parent_drained_end(bs);
62
bdrv_drain_invoke(bs, false);
63
- bdrv_drain_recurse(bs, false);
64
+ bdrv_drain_recurse(bs);
65
aio_context_release(aio_context);
66
}
67
146
--
68
--
147
2.13.6
69
2.13.6
148
70
149
71
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to parallels, which
1
The device is drained, so there is no point in waiting for requests at
2
enables image creation over QMP.
2
the end of the drained section. Remove the bdrv_drain_recurse() calls
3
there.
4
5
The bdrv_drain_recurse() calls were introduced in commit 481cad48e5e
6
in order to call the .bdrv_co_drain_end() driver callback. This is now
7
done by a separate bdrv_drain_invoke() call.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
6
Reviewed-by: Jeff Cody <jcody@redhat.com>
11
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
12
---
8
qapi/block-core.json | 18 ++++-
13
block/io.c | 2 --
9
block/parallels.c | 199 ++++++++++++++++++++++++++++++++++++++-------------
14
1 file changed, 2 deletions(-)
10
2 files changed, 168 insertions(+), 49 deletions(-)
11
15
12
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
diff --git a/block/io.c b/block/io.c
13
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
14
--- a/qapi/block-core.json
18
--- a/block/io.c
15
+++ b/qapi/block-core.json
19
+++ b/block/io.c
16
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
17
'size': 'size' } }
21
18
22
bdrv_parent_drained_end(bs);
19
##
23
bdrv_drain_invoke(bs, false);
20
+# @BlockdevCreateOptionsParallels:
24
- bdrv_drain_recurse(bs);
21
+#
25
aio_enable_external(bdrv_get_aio_context(bs));
22
+# Driver specific image creation options for parallels.
23
+#
24
+# @file Node to create the image format on
25
+# @size Size of the virtual disk in bytes
26
+# @cluster-size Cluster size in bytes (default: 1 MB)
27
+#
28
+# Since: 2.12
29
+##
30
+{ 'struct': 'BlockdevCreateOptionsParallels',
31
+ 'data': { 'file': 'BlockdevRef',
32
+ 'size': 'size',
33
+ '*cluster-size': 'size' } }
34
+
35
+##
36
# @BlockdevQcow2Version:
37
#
38
# @v2: The original QCOW2 format as introduced in qemu 0.10 (version 2)
39
@@ -XXX,XX +XXX,XX @@
40
'null-aio': 'BlockdevCreateNotSupported',
41
'null-co': 'BlockdevCreateNotSupported',
42
'nvme': 'BlockdevCreateNotSupported',
43
- 'parallels': 'BlockdevCreateNotSupported',
44
+ 'parallels': 'BlockdevCreateOptionsParallels',
45
'qcow2': 'BlockdevCreateOptionsQcow2',
46
'qcow': 'BlockdevCreateNotSupported',
47
'qed': 'BlockdevCreateNotSupported',
48
diff --git a/block/parallels.c b/block/parallels.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/block/parallels.c
51
+++ b/block/parallels.c
52
@@ -XXX,XX +XXX,XX @@
53
#include "sysemu/block-backend.h"
54
#include "qemu/module.h"
55
#include "qemu/option.h"
56
+#include "qapi/qmp/qdict.h"
57
+#include "qapi/qobject-input-visitor.h"
58
+#include "qapi/qapi-visit-block-core.h"
59
#include "qemu/bswap.h"
60
#include "qemu/bitmap.h"
61
#include "migration/blocker.h"
62
@@ -XXX,XX +XXX,XX @@ static QemuOptsList parallels_runtime_opts = {
63
},
64
};
65
66
+static QemuOptsList parallels_create_opts = {
67
+ .name = "parallels-create-opts",
68
+ .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
69
+ .desc = {
70
+ {
71
+ .name = BLOCK_OPT_SIZE,
72
+ .type = QEMU_OPT_SIZE,
73
+ .help = "Virtual disk size",
74
+ },
75
+ {
76
+ .name = BLOCK_OPT_CLUSTER_SIZE,
77
+ .type = QEMU_OPT_SIZE,
78
+ .help = "Parallels image cluster size",
79
+ .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
80
+ },
81
+ { /* end of list */ }
82
+ }
83
+};
84
+
85
86
static int64_t bat2sect(BDRVParallelsState *s, uint32_t idx)
87
{
88
@@ -XXX,XX +XXX,XX @@ out:
89
}
26
}
90
27
91
28
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
92
-static int coroutine_fn parallels_co_create_opts(const char *filename,
29
aio_enable_external(aio_context);
93
- QemuOpts *opts,
30
bdrv_parent_drained_end(bs);
94
- Error **errp)
31
bdrv_drain_invoke(bs, false);
95
+static int coroutine_fn parallels_co_create(BlockdevCreateOptions* opts,
32
- bdrv_drain_recurse(bs);
96
+ Error **errp)
33
aio_context_release(aio_context);
97
{
98
+ BlockdevCreateOptionsParallels *parallels_opts;
99
+ BlockDriverState *bs;
100
+ BlockBackend *blk;
101
int64_t total_size, cl_size;
102
- uint8_t tmp[BDRV_SECTOR_SIZE];
103
- Error *local_err = NULL;
104
- BlockBackend *file;
105
uint32_t bat_entries, bat_sectors;
106
ParallelsHeader header;
107
+ uint8_t tmp[BDRV_SECTOR_SIZE];
108
int ret;
109
110
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
111
- BDRV_SECTOR_SIZE);
112
- cl_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_CLUSTER_SIZE,
113
- DEFAULT_CLUSTER_SIZE), BDRV_SECTOR_SIZE);
114
+ assert(opts->driver == BLOCKDEV_DRIVER_PARALLELS);
115
+ parallels_opts = &opts->u.parallels;
116
+
117
+ /* Sanity checks */
118
+ total_size = parallels_opts->size;
119
+
120
+ if (parallels_opts->has_cluster_size) {
121
+ cl_size = parallels_opts->cluster_size;
122
+ } else {
123
+ cl_size = DEFAULT_CLUSTER_SIZE;
124
+ }
125
+
126
if (total_size >= MAX_PARALLELS_IMAGE_FACTOR * cl_size) {
127
- error_propagate(errp, local_err);
128
+ error_setg(errp, "Image size is too large for this cluster size");
129
return -E2BIG;
130
}
34
}
131
35
132
- ret = bdrv_create_file(filename, opts, &local_err);
133
- if (ret < 0) {
134
- error_propagate(errp, local_err);
135
- return ret;
136
+ if (!QEMU_IS_ALIGNED(total_size, BDRV_SECTOR_SIZE)) {
137
+ error_setg(errp, "Image size must be a multiple of 512 bytes");
138
+ return -EINVAL;
139
}
140
141
- file = blk_new_open(filename, NULL, NULL,
142
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
143
- &local_err);
144
- if (file == NULL) {
145
- error_propagate(errp, local_err);
146
+ if (!QEMU_IS_ALIGNED(cl_size, BDRV_SECTOR_SIZE)) {
147
+ error_setg(errp, "Cluster size must be a multiple of 512 bytes");
148
+ return -EINVAL;
149
+ }
150
+
151
+ /* Create BlockBackend to write to the image */
152
+ bs = bdrv_open_blockdev_ref(parallels_opts->file, errp);
153
+ if (bs == NULL) {
154
return -EIO;
155
}
156
157
- blk_set_allow_write_beyond_eof(file, true);
158
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
159
+ ret = blk_insert_bs(blk, bs, errp);
160
+ if (ret < 0) {
161
+ goto out;
162
+ }
163
+ blk_set_allow_write_beyond_eof(blk, true);
164
165
- ret = blk_truncate(file, 0, PREALLOC_MODE_OFF, errp);
166
+ /* Create image format */
167
+ ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
168
if (ret < 0) {
169
- goto exit;
170
+ goto out;
171
}
172
173
bat_entries = DIV_ROUND_UP(total_size, cl_size);
174
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn parallels_co_create_opts(const char *filename,
175
memset(tmp, 0, sizeof(tmp));
176
memcpy(tmp, &header, sizeof(header));
177
178
- ret = blk_pwrite(file, 0, tmp, BDRV_SECTOR_SIZE, 0);
179
+ ret = blk_pwrite(blk, 0, tmp, BDRV_SECTOR_SIZE, 0);
180
if (ret < 0) {
181
goto exit;
182
}
183
- ret = blk_pwrite_zeroes(file, BDRV_SECTOR_SIZE,
184
+ ret = blk_pwrite_zeroes(blk, BDRV_SECTOR_SIZE,
185
(bat_sectors - 1) << BDRV_SECTOR_BITS, 0);
186
if (ret < 0) {
187
goto exit;
188
}
189
- ret = 0;
190
191
-done:
192
- blk_unref(file);
193
+ ret = 0;
194
+out:
195
+ blk_unref(blk);
196
+ bdrv_unref(bs);
197
return ret;
198
199
exit:
200
error_setg_errno(errp, -ret, "Failed to create Parallels image");
201
- goto done;
202
+ goto out;
203
+}
204
+
205
+static int coroutine_fn parallels_co_create_opts(const char *filename,
206
+ QemuOpts *opts,
207
+ Error **errp)
208
+{
209
+ BlockdevCreateOptions *create_options = NULL;
210
+ Error *local_err = NULL;
211
+ BlockDriverState *bs = NULL;
212
+ QDict *qdict = NULL;
213
+ QObject *qobj;
214
+ Visitor *v;
215
+ int ret;
216
+
217
+ static const QDictRenames opt_renames[] = {
218
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
219
+ { NULL, NULL },
220
+ };
221
+
222
+ /* Parse options and convert legacy syntax */
223
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &parallels_create_opts,
224
+ true);
225
+
226
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
227
+ ret = -EINVAL;
228
+ goto done;
229
+ }
230
+
231
+ /* Create and open the file (protocol layer) */
232
+ ret = bdrv_create_file(filename, opts, &local_err);
233
+ if (ret < 0) {
234
+ error_propagate(errp, local_err);
235
+ goto done;
236
+ }
237
+
238
+ bs = bdrv_open(filename, NULL, NULL,
239
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
240
+ if (bs == NULL) {
241
+ ret = -EIO;
242
+ goto done;
243
+ }
244
+
245
+ /* Now get the QAPI type BlockdevCreateOptions */
246
+ qdict_put_str(qdict, "driver", "parallels");
247
+ qdict_put_str(qdict, "file", bs->node_name);
248
+
249
+ qobj = qdict_crumple(qdict, errp);
250
+ QDECREF(qdict);
251
+ qdict = qobject_to_qdict(qobj);
252
+ if (qdict == NULL) {
253
+ ret = -EINVAL;
254
+ goto done;
255
+ }
256
+
257
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
258
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
259
+ visit_free(v);
260
+
261
+ if (local_err) {
262
+ error_propagate(errp, local_err);
263
+ ret = -EINVAL;
264
+ goto done;
265
+ }
266
+
267
+ /* Silently round up sizes */
268
+ create_options->u.parallels.size =
269
+ ROUND_UP(create_options->u.parallels.size, BDRV_SECTOR_SIZE);
270
+ create_options->u.parallels.cluster_size =
271
+ ROUND_UP(create_options->u.parallels.cluster_size, BDRV_SECTOR_SIZE);
272
+
273
+ /* Create the Parallels image (format layer) */
274
+ ret = parallels_co_create(create_options, errp);
275
+ if (ret < 0) {
276
+ goto done;
277
+ }
278
+ ret = 0;
279
+
280
+done:
281
+ QDECREF(qdict);
282
+ bdrv_unref(bs);
283
+ qapi_free_BlockdevCreateOptions(create_options);
284
+ return ret;
285
}
286
287
288
@@ -XXX,XX +XXX,XX @@ static void parallels_close(BlockDriverState *bs)
289
error_free(s->migration_blocker);
290
}
291
292
-static QemuOptsList parallels_create_opts = {
293
- .name = "parallels-create-opts",
294
- .head = QTAILQ_HEAD_INITIALIZER(parallels_create_opts.head),
295
- .desc = {
296
- {
297
- .name = BLOCK_OPT_SIZE,
298
- .type = QEMU_OPT_SIZE,
299
- .help = "Virtual disk size",
300
- },
301
- {
302
- .name = BLOCK_OPT_CLUSTER_SIZE,
303
- .type = QEMU_OPT_SIZE,
304
- .help = "Parallels image cluster size",
305
- .def_value_str = stringify(DEFAULT_CLUSTER_SIZE),
306
- },
307
- { /* end of list */ }
308
- }
309
-};
310
-
311
static BlockDriver bdrv_parallels = {
312
.format_name    = "parallels",
313
.instance_size    = sizeof(BDRVParallelsState),
314
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_parallels = {
315
.bdrv_co_readv = parallels_co_readv,
316
.bdrv_co_writev = parallels_co_writev,
317
.supports_backing = true,
318
+ .bdrv_co_create = parallels_co_create,
319
.bdrv_co_create_opts = parallels_co_create_opts,
320
.bdrv_co_check = parallels_co_check,
321
.create_opts = &parallels_create_opts,
322
--
36
--
323
2.13.6
37
2.13.6
324
38
325
39
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to vhdx, which
1
Drain requests are propagated to child nodes, parent nodes and directly
2
enables image creation over QMP.
2
to the AioContext. The order in which this happened was different
3
between all combinations of drain/drain_all and begin/end.
4
5
The correct order is to keep children only drained when their parents
6
are also drained. This means that at the start of a drained section, the
7
AioContext needs to be drained first, the parents second and only then
8
the children. The correct order for the end of a drained section is the
9
opposite.
10
11
This patch changes the three other functions to follow the example of
12
bdrv_drained_begin(), which is the only one that got it right.
3
13
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
16
---
7
qapi/block-core.json | 40 +++++++++-
17
block/io.c | 12 ++++++++----
8
block/vhdx.c | 216 ++++++++++++++++++++++++++++++++++++++-------------
18
1 file changed, 8 insertions(+), 4 deletions(-)
9
2 files changed, 203 insertions(+), 53 deletions(-)
10
19
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
20
diff --git a/block/io.c b/block/io.c
12
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
22
--- a/block/io.c
14
+++ b/qapi/block-core.json
23
+++ b/block/io.c
15
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
16
'*static': 'bool' } }
25
return;
17
18
##
19
+# @BlockdevVhdxSubformat:
20
+#
21
+# @dynamic: Growing image file
22
+# @fixed: Preallocated fixed-size image file
23
+#
24
+# Since: 2.12
25
+##
26
+{ 'enum': 'BlockdevVhdxSubformat',
27
+ 'data': [ 'dynamic', 'fixed' ] }
28
+
29
+##
30
+# @BlockdevCreateOptionsVhdx:
31
+#
32
+# Driver specific image creation options for vhdx.
33
+#
34
+# @file Node to create the image format on
35
+# @size Size of the virtual disk in bytes
36
+# @log-size Log size in bytes, must be a multiple of 1 MB
37
+# (default: 1 MB)
38
+# @block-size Block size in bytes, must be a multiple of 1 MB and not
39
+# larger than 256 MB (default: automatically choose a block
40
+# size depending on the image size)
41
+# @subformat vhdx subformat (default: dynamic)
42
+# @block-state-zero Force use of payload blocks of type 'ZERO'. Non-standard,
43
+# but default. Do not set to 'off' when using 'qemu-img
44
+# convert' with subformat=dynamic.
45
+#
46
+# Since: 2.12
47
+##
48
+{ 'struct': 'BlockdevCreateOptionsVhdx',
49
+ 'data': { 'file': 'BlockdevRef',
50
+ 'size': 'size',
51
+ '*log-size': 'size',
52
+ '*block-size': 'size',
53
+ '*subformat': 'BlockdevVhdxSubformat',
54
+ '*block-state-zero': 'bool' } }
55
+
56
+##
57
# @BlockdevCreateNotSupported:
58
#
59
# This is used for all drivers that don't support creating images.
60
@@ -XXX,XX +XXX,XX @@
61
'ssh': 'BlockdevCreateOptionsSsh',
62
'throttle': 'BlockdevCreateNotSupported',
63
'vdi': 'BlockdevCreateOptionsVdi',
64
- 'vhdx': 'BlockdevCreateNotSupported',
65
+ 'vhdx': 'BlockdevCreateOptionsVhdx',
66
'vmdk': 'BlockdevCreateNotSupported',
67
'vpc': 'BlockdevCreateNotSupported',
68
'vvfat': 'BlockdevCreateNotSupported',
69
diff --git a/block/vhdx.c b/block/vhdx.c
70
index XXXXXXX..XXXXXXX 100644
71
--- a/block/vhdx.c
72
+++ b/block/vhdx.c
73
@@ -XXX,XX +XXX,XX @@
74
#include "block/vhdx.h"
75
#include "migration/blocker.h"
76
#include "qemu/uuid.h"
77
+#include "qapi/qmp/qdict.h"
78
+#include "qapi/qobject-input-visitor.h"
79
+#include "qapi/qapi-visit-block-core.h"
80
81
/* Options for VHDX creation */
82
83
@@ -XXX,XX +XXX,XX @@ typedef enum VHDXImageType {
84
VHDX_TYPE_DIFFERENCING, /* Currently unsupported */
85
} VHDXImageType;
86
87
+static QemuOptsList vhdx_create_opts;
88
+
89
/* Several metadata and region table data entries are identified by
90
* guids in a MS-specific GUID format. */
91
92
@@ -XXX,XX +XXX,XX @@ exit:
93
* .---- ~ ----------- ~ ------------ ~ ---------------- ~ -----------.
94
* 1MB
95
*/
96
-static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts,
97
- Error **errp)
98
+static int coroutine_fn vhdx_co_create(BlockdevCreateOptions *opts,
99
+ Error **errp)
100
{
101
+ BlockdevCreateOptionsVhdx *vhdx_opts;
102
+ BlockBackend *blk = NULL;
103
+ BlockDriverState *bs = NULL;
104
+
105
int ret = 0;
106
- uint64_t image_size = (uint64_t) 2 * GiB;
107
- uint32_t log_size = 1 * MiB;
108
- uint32_t block_size = 0;
109
+ uint64_t image_size;
110
+ uint32_t log_size;
111
+ uint32_t block_size;
112
uint64_t signature;
113
uint64_t metadata_offset;
114
bool use_zero_blocks = false;
115
116
gunichar2 *creator = NULL;
117
glong creator_items;
118
- BlockBackend *blk;
119
- char *type = NULL;
120
VHDXImageType image_type;
121
- Error *local_err = NULL;
122
123
- image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
124
- BDRV_SECTOR_SIZE);
125
- log_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_LOG_SIZE, 0);
126
- block_size = qemu_opt_get_size_del(opts, VHDX_BLOCK_OPT_BLOCK_SIZE, 0);
127
- type = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
128
- use_zero_blocks = qemu_opt_get_bool_del(opts, VHDX_BLOCK_OPT_ZERO, true);
129
+ assert(opts->driver == BLOCKDEV_DRIVER_VHDX);
130
+ vhdx_opts = &opts->u.vhdx;
131
132
+ /* Validate options and set default values */
133
+ image_size = vhdx_opts->size;
134
if (image_size > VHDX_MAX_IMAGE_SIZE) {
135
error_setg_errno(errp, EINVAL, "Image size too large; max of 64TB");
136
- ret = -EINVAL;
137
- goto exit;
138
+ return -EINVAL;
139
}
26
}
140
27
141
- if (type == NULL) {
28
+ /* Stop things in parent-to-child order */
142
- type = g_strdup("dynamic");
29
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
143
+ if (!vhdx_opts->has_log_size) {
30
aio_disable_external(bdrv_get_aio_context(bs));
144
+ log_size = DEFAULT_LOG_SIZE;
31
bdrv_parent_drained_begin(bs);
145
+ } else {
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
146
+ log_size = vhdx_opts->log_size;
33
return;
147
+ }
148
+ if (log_size < MiB || (log_size % MiB) != 0) {
149
+ error_setg_errno(errp, EINVAL, "Log size must be a multiple of 1 MB");
150
+ return -EINVAL;
151
}
34
}
152
35
153
- if (!strcmp(type, "dynamic")) {
36
- bdrv_parent_drained_end(bs);
154
+ if (!vhdx_opts->has_block_state_zero) {
37
+ /* Re-enable things in child-to-parent order */
155
+ use_zero_blocks = true;
38
bdrv_drain_invoke(bs, false);
156
+ } else {
39
+ bdrv_parent_drained_end(bs);
157
+ use_zero_blocks = vhdx_opts->block_state_zero;
40
aio_enable_external(bdrv_get_aio_context(bs));
158
+ }
41
}
159
+
42
160
+ if (!vhdx_opts->has_subformat) {
43
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
161
+ vhdx_opts->subformat = BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC;
44
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
162
+ }
45
AioContext *aio_context = bdrv_get_aio_context(bs);
163
+
46
164
+ switch (vhdx_opts->subformat) {
47
+ /* Stop things in parent-to-child order */
165
+ case BLOCKDEV_VHDX_SUBFORMAT_DYNAMIC:
48
aio_context_acquire(aio_context);
166
image_type = VHDX_TYPE_DYNAMIC;
49
- bdrv_parent_drained_begin(bs);
167
- } else if (!strcmp(type, "fixed")) {
50
aio_disable_external(aio_context);
168
+ break;
51
+ bdrv_parent_drained_begin(bs);
169
+ case BLOCKDEV_VHDX_SUBFORMAT_FIXED:
52
bdrv_drain_invoke(bs, true);
170
image_type = VHDX_TYPE_FIXED;
53
aio_context_release(aio_context);
171
- } else if (!strcmp(type, "differencing")) {
54
172
- error_setg_errno(errp, ENOTSUP,
55
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
173
- "Differencing files not yet supported");
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
174
- ret = -ENOTSUP;
57
AioContext *aio_context = bdrv_get_aio_context(bs);
175
- goto exit;
58
176
- } else {
59
+ /* Re-enable things in child-to-parent order */
177
- error_setg(errp, "Invalid subformat '%s'", type);
60
aio_context_acquire(aio_context);
178
- ret = -EINVAL;
61
- aio_enable_external(aio_context);
179
- goto exit;
62
- bdrv_parent_drained_end(bs);
180
+ break;
63
bdrv_drain_invoke(bs, false);
181
+ default:
64
+ bdrv_parent_drained_end(bs);
182
+ g_assert_not_reached();
65
+ aio_enable_external(aio_context);
66
aio_context_release(aio_context);
183
}
67
}
184
68
185
/* These are pretty arbitrary, and mainly designed to keep the BAT
186
* size reasonable to load into RAM */
187
- if (block_size == 0) {
188
+ if (vhdx_opts->has_block_size) {
189
+ block_size = vhdx_opts->block_size;
190
+ } else {
191
if (image_size > 32 * TiB) {
192
block_size = 64 * MiB;
193
} else if (image_size > (uint64_t) 100 * GiB) {
194
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts
195
}
196
}
197
198
-
199
- /* make the log size close to what was specified, but must be
200
- * min 1MB, and multiple of 1MB */
201
- log_size = ROUND_UP(log_size, MiB);
202
-
203
- block_size = ROUND_UP(block_size, MiB);
204
- block_size = block_size > VHDX_BLOCK_SIZE_MAX ? VHDX_BLOCK_SIZE_MAX :
205
- block_size;
206
-
207
- ret = bdrv_create_file(filename, opts, &local_err);
208
- if (ret < 0) {
209
- error_propagate(errp, local_err);
210
- goto exit;
211
+ if (block_size < MiB || (block_size % MiB) != 0) {
212
+ error_setg_errno(errp, EINVAL, "Block size must be a multiple of 1 MB");
213
+ return -EINVAL;
214
+ }
215
+ if (block_size > VHDX_BLOCK_SIZE_MAX) {
216
+ error_setg_errno(errp, EINVAL, "Block size must not exceed %d",
217
+ VHDX_BLOCK_SIZE_MAX);
218
+ return -EINVAL;
219
}
220
221
- blk = blk_new_open(filename, NULL, NULL,
222
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
223
- &local_err);
224
- if (blk == NULL) {
225
- error_propagate(errp, local_err);
226
- ret = -EIO;
227
- goto exit;
228
+ /* Create BlockBackend to write to the image */
229
+ bs = bdrv_open_blockdev_ref(vhdx_opts->file, errp);
230
+ if (bs == NULL) {
231
+ return -EIO;
232
}
233
234
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
235
+ ret = blk_insert_bs(blk, bs, errp);
236
+ if (ret < 0) {
237
+ goto delete_and_exit;
238
+ }
239
blk_set_allow_write_beyond_eof(blk, true);
240
241
/* Create (A) */
242
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vhdx_co_create_opts(const char *filename, QemuOpts *opts
243
244
delete_and_exit:
245
blk_unref(blk);
246
-exit:
247
- g_free(type);
248
+ bdrv_unref(bs);
249
g_free(creator);
250
return ret;
251
}
252
253
+static int coroutine_fn vhdx_co_create_opts(const char *filename,
254
+ QemuOpts *opts,
255
+ Error **errp)
256
+{
257
+ BlockdevCreateOptions *create_options = NULL;
258
+ QDict *qdict = NULL;
259
+ QObject *qobj;
260
+ Visitor *v;
261
+ BlockDriverState *bs = NULL;
262
+ Error *local_err = NULL;
263
+ int ret;
264
+
265
+ static const QDictRenames opt_renames[] = {
266
+ { VHDX_BLOCK_OPT_LOG_SIZE, "log-size" },
267
+ { VHDX_BLOCK_OPT_BLOCK_SIZE, "block-size" },
268
+ { VHDX_BLOCK_OPT_ZERO, "block-state-zero" },
269
+ { NULL, NULL },
270
+ };
271
+
272
+ /* Parse options and convert legacy syntax */
273
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vhdx_create_opts, true);
274
+
275
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
276
+ ret = -EINVAL;
277
+ goto fail;
278
+ }
279
+
280
+ /* Create and open the file (protocol layer) */
281
+ ret = bdrv_create_file(filename, opts, &local_err);
282
+ if (ret < 0) {
283
+ error_propagate(errp, local_err);
284
+ goto fail;
285
+ }
286
+
287
+ bs = bdrv_open(filename, NULL, NULL,
288
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
289
+ if (bs == NULL) {
290
+ ret = -EIO;
291
+ goto fail;
292
+ }
293
+
294
+ /* Now get the QAPI type BlockdevCreateOptions */
295
+ qdict_put_str(qdict, "driver", "vhdx");
296
+ qdict_put_str(qdict, "file", bs->node_name);
297
+
298
+ qobj = qdict_crumple(qdict, errp);
299
+ QDECREF(qdict);
300
+ qdict = qobject_to_qdict(qobj);
301
+ if (qdict == NULL) {
302
+ ret = -EINVAL;
303
+ goto fail;
304
+ }
305
+
306
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
307
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
308
+ visit_free(v);
309
+
310
+ if (local_err) {
311
+ error_propagate(errp, local_err);
312
+ ret = -EINVAL;
313
+ goto fail;
314
+ }
315
+
316
+ /* Silently round up sizes:
317
+ * The image size is rounded to 512 bytes. Make the block and log size
318
+ * close to what was specified, but must be at least 1MB, and a multiple of
319
+ * 1 MB. Also respect VHDX_BLOCK_SIZE_MAX for block sizes. block_size = 0
320
+ * means auto, which is represented by a missing key in QAPI. */
321
+ assert(create_options->driver == BLOCKDEV_DRIVER_VHDX);
322
+ create_options->u.vhdx.size =
323
+ ROUND_UP(create_options->u.vhdx.size, BDRV_SECTOR_SIZE);
324
+
325
+ if (create_options->u.vhdx.has_log_size) {
326
+ create_options->u.vhdx.log_size =
327
+ ROUND_UP(create_options->u.vhdx.log_size, MiB);
328
+ }
329
+ if (create_options->u.vhdx.has_block_size) {
330
+ create_options->u.vhdx.block_size =
331
+ ROUND_UP(create_options->u.vhdx.block_size, MiB);
332
+
333
+ if (create_options->u.vhdx.block_size == 0) {
334
+ create_options->u.vhdx.has_block_size = false;
335
+ }
336
+ if (create_options->u.vhdx.block_size > VHDX_BLOCK_SIZE_MAX) {
337
+ create_options->u.vhdx.block_size = VHDX_BLOCK_SIZE_MAX;
338
+ }
339
+ }
340
+
341
+ /* Create the vhdx image (format layer) */
342
+ ret = vhdx_co_create(create_options, errp);
343
+
344
+fail:
345
+ QDECREF(qdict);
346
+ bdrv_unref(bs);
347
+ qapi_free_BlockdevCreateOptions(create_options);
348
+ return ret;
349
+}
350
+
351
/* If opened r/w, the VHDX driver will automatically replay the log,
352
* if one is present, inside the vhdx_open() call.
353
*
354
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vhdx = {
355
.bdrv_child_perm = bdrv_format_default_perms,
356
.bdrv_co_readv = vhdx_co_readv,
357
.bdrv_co_writev = vhdx_co_writev,
358
+ .bdrv_co_create = vhdx_co_create,
359
.bdrv_co_create_opts = vhdx_co_create_opts,
360
.bdrv_get_info = vhdx_get_info,
361
.bdrv_co_check = vhdx_co_check,
362
--
69
--
363
2.13.6
70
2.13.6
364
71
365
72
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to qed, which
1
Commit 15afd94a047 added code to acquire and release the AioContext in
2
enables image creation over QMP.
2
qemuio_command(). This means that the lock is taken twice now in the
3
call path from hmp_qemu_io(). This causes BDRV_POLL_WHILE() to hang for
4
any requests issued to nodes in a non-mainloop AioContext.
5
6
Dropping the first locking from hmp_qemu_io() fixes the problem.
3
7
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
6
---
10
---
7
qapi/block-core.json | 25 ++++++-
11
hmp.c | 6 ------
8
block/qed.c | 204 ++++++++++++++++++++++++++++++++++-----------------
12
1 file changed, 6 deletions(-)
9
2 files changed, 162 insertions(+), 67 deletions(-)
10
13
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
14
diff --git a/hmp.c b/hmp.c
12
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
16
--- a/hmp.c
14
+++ b/qapi/block-core.json
17
+++ b/hmp.c
15
@@ -XXX,XX +XXX,XX @@
18
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
16
'*refcount-bits': 'int' } }
17
18
##
19
+# @BlockdevCreateOptionsQed:
20
+#
21
+# Driver specific image creation options for qed.
22
+#
23
+# @file Node to create the image format on
24
+# @size Size of the virtual disk in bytes
25
+# @backing-file File name of the backing file if a backing file
26
+# should be used
27
+# @backing-fmt Name of the block driver to use for the backing file
28
+# @cluster-size Cluster size in bytes (default: 65536)
29
+# @table-size L1/L2 table size (in clusters)
30
+#
31
+# Since: 2.12
32
+##
33
+{ 'struct': 'BlockdevCreateOptionsQed',
34
+ 'data': { 'file': 'BlockdevRef',
35
+ 'size': 'size',
36
+ '*backing-file': 'str',
37
+ '*backing-fmt': 'BlockdevDriver',
38
+ '*cluster-size': 'size',
39
+ '*table-size': 'int' } }
40
+
41
+##
42
# @BlockdevCreateOptionsRbd:
43
#
44
# Driver specific image creation options for rbd/Ceph.
45
@@ -XXX,XX +XXX,XX @@
46
'parallels': 'BlockdevCreateOptionsParallels',
47
'qcow': 'BlockdevCreateOptionsQcow',
48
'qcow2': 'BlockdevCreateOptionsQcow2',
49
- 'qed': 'BlockdevCreateNotSupported',
50
+ 'qed': 'BlockdevCreateOptionsQed',
51
'quorum': 'BlockdevCreateNotSupported',
52
'raw': 'BlockdevCreateNotSupported',
53
'rbd': 'BlockdevCreateOptionsRbd',
54
diff --git a/block/qed.c b/block/qed.c
55
index XXXXXXX..XXXXXXX 100644
56
--- a/block/qed.c
57
+++ b/block/qed.c
58
@@ -XXX,XX +XXX,XX @@
59
#include "trace.h"
60
#include "qed.h"
61
#include "sysemu/block-backend.h"
62
+#include "qapi/qmp/qdict.h"
63
+#include "qapi/qobject-input-visitor.h"
64
+#include "qapi/qapi-visit-block-core.h"
65
+
66
+static QemuOptsList qed_create_opts;
67
68
static int bdrv_qed_probe(const uint8_t *buf, int buf_size,
69
const char *filename)
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_qed_close(BlockDriverState *bs)
71
qemu_vfree(s->l1_table);
72
}
73
74
-static int qed_create(const char *filename, uint32_t cluster_size,
75
- uint64_t image_size, uint32_t table_size,
76
- const char *backing_file, const char *backing_fmt,
77
- QemuOpts *opts, Error **errp)
78
+static int coroutine_fn bdrv_qed_co_create(BlockdevCreateOptions *opts,
79
+ Error **errp)
80
{
19
{
81
- QEDHeader header = {
20
BlockBackend *blk;
82
- .magic = QED_MAGIC,
21
BlockBackend *local_blk = NULL;
83
- .cluster_size = cluster_size,
22
- AioContext *aio_context;
84
- .table_size = table_size,
23
const char* device = qdict_get_str(qdict, "device");
85
- .header_size = 1,
24
const char* command = qdict_get_str(qdict, "command");
86
- .features = 0,
25
Error *err = NULL;
87
- .compat_features = 0,
26
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
88
- .l1_table_offset = cluster_size,
89
- .image_size = image_size,
90
- };
91
+ BlockdevCreateOptionsQed *qed_opts;
92
+ BlockBackend *blk = NULL;
93
+ BlockDriverState *bs = NULL;
94
+
95
+ QEDHeader header;
96
QEDHeader le_header;
97
uint8_t *l1_table = NULL;
98
- size_t l1_size = header.cluster_size * header.table_size;
99
- Error *local_err = NULL;
100
+ size_t l1_size;
101
int ret = 0;
102
- BlockBackend *blk;
103
104
- ret = bdrv_create_file(filename, opts, &local_err);
105
- if (ret < 0) {
106
- error_propagate(errp, local_err);
107
- return ret;
108
+ assert(opts->driver == BLOCKDEV_DRIVER_QED);
109
+ qed_opts = &opts->u.qed;
110
+
111
+ /* Validate options and set default values */
112
+ if (!qed_opts->has_cluster_size) {
113
+ qed_opts->cluster_size = QED_DEFAULT_CLUSTER_SIZE;
114
+ }
115
+ if (!qed_opts->has_table_size) {
116
+ qed_opts->table_size = QED_DEFAULT_TABLE_SIZE;
117
}
118
119
- blk = blk_new_open(filename, NULL, NULL,
120
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
121
- &local_err);
122
- if (blk == NULL) {
123
- error_propagate(errp, local_err);
124
+ if (!qed_is_cluster_size_valid(qed_opts->cluster_size)) {
125
+ error_setg(errp, "QED cluster size must be within range [%u, %u] "
126
+ "and power of 2",
127
+ QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
128
+ return -EINVAL;
129
+ }
130
+ if (!qed_is_table_size_valid(qed_opts->table_size)) {
131
+ error_setg(errp, "QED table size must be within range [%u, %u] "
132
+ "and power of 2",
133
+ QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
134
+ return -EINVAL;
135
+ }
136
+ if (!qed_is_image_size_valid(qed_opts->size, qed_opts->cluster_size,
137
+ qed_opts->table_size))
138
+ {
139
+ error_setg(errp, "QED image size must be a non-zero multiple of "
140
+ "cluster size and less than %" PRIu64 " bytes",
141
+ qed_max_image_size(qed_opts->cluster_size,
142
+ qed_opts->table_size));
143
+ return -EINVAL;
144
+ }
145
+
146
+ /* Create BlockBackend to write to the image */
147
+ bs = bdrv_open_blockdev_ref(qed_opts->file, errp);
148
+ if (bs == NULL) {
149
return -EIO;
150
}
151
152
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
153
+ ret = blk_insert_bs(blk, bs, errp);
154
+ if (ret < 0) {
155
+ goto out;
156
+ }
157
blk_set_allow_write_beyond_eof(blk, true);
158
159
+ /* Prepare image format */
160
+ header = (QEDHeader) {
161
+ .magic = QED_MAGIC,
162
+ .cluster_size = qed_opts->cluster_size,
163
+ .table_size = qed_opts->table_size,
164
+ .header_size = 1,
165
+ .features = 0,
166
+ .compat_features = 0,
167
+ .l1_table_offset = qed_opts->cluster_size,
168
+ .image_size = qed_opts->size,
169
+ };
170
+
171
+ l1_size = header.cluster_size * header.table_size;
172
+
173
/* File must start empty and grow, check truncate is supported */
174
ret = blk_truncate(blk, 0, PREALLOC_MODE_OFF, errp);
175
if (ret < 0) {
176
goto out;
177
}
178
179
- if (backing_file) {
180
+ if (qed_opts->has_backing_file) {
181
header.features |= QED_F_BACKING_FILE;
182
header.backing_filename_offset = sizeof(le_header);
183
- header.backing_filename_size = strlen(backing_file);
184
+ header.backing_filename_size = strlen(qed_opts->backing_file);
185
186
- if (qed_fmt_is_raw(backing_fmt)) {
187
- header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
188
+ if (qed_opts->has_backing_fmt) {
189
+ const char *backing_fmt = BlockdevDriver_str(qed_opts->backing_fmt);
190
+ if (qed_fmt_is_raw(backing_fmt)) {
191
+ header.features |= QED_F_BACKING_FORMAT_NO_PROBE;
192
+ }
193
}
27
}
194
}
28
}
195
29
196
@@ -XXX,XX +XXX,XX @@ static int qed_create(const char *filename, uint32_t cluster_size,
30
- aio_context = blk_get_aio_context(blk);
197
if (ret < 0) {
31
- aio_context_acquire(aio_context);
198
goto out;
199
}
200
- ret = blk_pwrite(blk, sizeof(le_header), backing_file,
201
+ ret = blk_pwrite(blk, sizeof(le_header), qed_opts->backing_file,
202
header.backing_filename_size, 0);
203
if (ret < 0) {
204
goto out;
205
@@ -XXX,XX +XXX,XX @@ static int qed_create(const char *filename, uint32_t cluster_size,
206
out:
207
g_free(l1_table);
208
blk_unref(blk);
209
+ bdrv_unref(bs);
210
return ret;
211
}
212
213
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn bdrv_qed_co_create_opts(const char *filename,
214
QemuOpts *opts,
215
Error **errp)
216
{
217
- uint64_t image_size = 0;
218
- uint32_t cluster_size = QED_DEFAULT_CLUSTER_SIZE;
219
- uint32_t table_size = QED_DEFAULT_TABLE_SIZE;
220
- char *backing_file = NULL;
221
- char *backing_fmt = NULL;
222
+ BlockdevCreateOptions *create_options = NULL;
223
+ QDict *qdict = NULL;
224
+ QObject *qobj;
225
+ Visitor *v;
226
+ BlockDriverState *bs = NULL;
227
+ Error *local_err = NULL;
228
int ret;
229
230
- image_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
231
- BDRV_SECTOR_SIZE);
232
- backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE);
233
- backing_fmt = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FMT);
234
- cluster_size = qemu_opt_get_size_del(opts,
235
- BLOCK_OPT_CLUSTER_SIZE,
236
- QED_DEFAULT_CLUSTER_SIZE);
237
- table_size = qemu_opt_get_size_del(opts, BLOCK_OPT_TABLE_SIZE,
238
- QED_DEFAULT_TABLE_SIZE);
239
-
32
-
240
- if (!qed_is_cluster_size_valid(cluster_size)) {
33
/*
241
- error_setg(errp, "QED cluster size must be within range [%u, %u] "
34
* Notably absent: Proper permission management. This is sad, but it seems
242
- "and power of 2",
35
* almost impossible to achieve without changing the semantics and thereby
243
- QED_MIN_CLUSTER_SIZE, QED_MAX_CLUSTER_SIZE);
36
@@ -XXX,XX +XXX,XX @@ void hmp_qemu_io(Monitor *mon, const QDict *qdict)
244
+ static const QDictRenames opt_renames[] = {
37
*/
245
+ { BLOCK_OPT_BACKING_FILE, "backing-file" },
38
qemuio_command(blk, command);
246
+ { BLOCK_OPT_BACKING_FMT, "backing-fmt" },
39
247
+ { BLOCK_OPT_CLUSTER_SIZE, "cluster-size" },
40
- aio_context_release(aio_context);
248
+ { BLOCK_OPT_TABLE_SIZE, "table-size" },
41
-
249
+ { NULL, NULL },
42
fail:
250
+ };
43
blk_unref(local_blk);
251
+
44
hmp_handle_error(mon, &err);
252
+ /* Parse options and convert legacy syntax */
253
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &qed_create_opts, true);
254
+
255
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
256
ret = -EINVAL;
257
- goto finish;
258
+ goto fail;
259
}
260
- if (!qed_is_table_size_valid(table_size)) {
261
- error_setg(errp, "QED table size must be within range [%u, %u] "
262
- "and power of 2",
263
- QED_MIN_TABLE_SIZE, QED_MAX_TABLE_SIZE);
264
+
265
+ /* Create and open the file (protocol layer) */
266
+ ret = bdrv_create_file(filename, opts, &local_err);
267
+ if (ret < 0) {
268
+ error_propagate(errp, local_err);
269
+ goto fail;
270
+ }
271
+
272
+ bs = bdrv_open(filename, NULL, NULL,
273
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
274
+ if (bs == NULL) {
275
+ ret = -EIO;
276
+ goto fail;
277
+ }
278
+
279
+ /* Now get the QAPI type BlockdevCreateOptions */
280
+ qdict_put_str(qdict, "driver", "qed");
281
+ qdict_put_str(qdict, "file", bs->node_name);
282
+
283
+ qobj = qdict_crumple(qdict, errp);
284
+ QDECREF(qdict);
285
+ qdict = qobject_to_qdict(qobj);
286
+ if (qdict == NULL) {
287
ret = -EINVAL;
288
- goto finish;
289
+ goto fail;
290
}
291
- if (!qed_is_image_size_valid(image_size, cluster_size, table_size)) {
292
- error_setg(errp, "QED image size must be a non-zero multiple of "
293
- "cluster size and less than %" PRIu64 " bytes",
294
- qed_max_image_size(cluster_size, table_size));
295
+
296
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
297
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
298
+ visit_free(v);
299
+
300
+ if (local_err) {
301
+ error_propagate(errp, local_err);
302
ret = -EINVAL;
303
- goto finish;
304
+ goto fail;
305
}
306
307
- ret = qed_create(filename, cluster_size, image_size, table_size,
308
- backing_file, backing_fmt, opts, errp);
309
+ /* Silently round up size */
310
+ assert(create_options->driver == BLOCKDEV_DRIVER_QED);
311
+ create_options->u.qed.size =
312
+ ROUND_UP(create_options->u.qed.size, BDRV_SECTOR_SIZE);
313
+
314
+ /* Create the qed image (format layer) */
315
+ ret = bdrv_qed_co_create(create_options, errp);
316
317
-finish:
318
- g_free(backing_file);
319
- g_free(backing_fmt);
320
+fail:
321
+ QDECREF(qdict);
322
+ bdrv_unref(bs);
323
+ qapi_free_BlockdevCreateOptions(create_options);
324
return ret;
325
}
326
327
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_qed = {
328
.bdrv_close = bdrv_qed_close,
329
.bdrv_reopen_prepare = bdrv_qed_reopen_prepare,
330
.bdrv_child_perm = bdrv_format_default_perms,
331
+ .bdrv_co_create = bdrv_qed_co_create,
332
.bdrv_co_create_opts = bdrv_qed_co_create_opts,
333
.bdrv_has_zero_init = bdrv_has_zero_init_1,
334
.bdrv_co_block_status = bdrv_qed_co_block_status,
335
--
45
--
336
2.13.6
46
2.13.6
337
47
338
48
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
1
From: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
2
2
3
In preparation of QAPI-fying VDI image creation, we have to create a
3
Since bdrv_co_preadv does all neccessary checks including
4
BlockdevCreateOptionsVdi type which is received by a (future)
4
reading after the end of the backing file, avoid duplication
5
vdi_co_create().
5
of verification before bdrv_co_preadv call.
6
6
7
vdi_co_create_opts() now converts the QemuOpts object into such a
7
Signed-off-by: Edgar Kaziakhmedov <edgar.kaziakhmedov@virtuozzo.com>
8
BlockdevCreateOptionsVdi object. The protocol-layer file is still
8
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
created in vdi_co_do_create() (and BlockdevCreateOptionsVdi.file is set
9
Reviewed-by: Eric Blake <eblake@redhat.com>
10
to an empty string), but that will be addressed by a follow-up patch.
11
12
Note that cluster-size is not part of the QAPI schema because it is not
13
supported by default.
14
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
11
---
18
qapi/block-core.json | 18 +++++++++++
12
block/qcow2.h | 3 ---
19
block/vdi.c | 91 ++++++++++++++++++++++++++++++++++++++++++++--------
13
block/qcow2.c | 51 ++++++++-------------------------------------------
20
2 files changed, 95 insertions(+), 14 deletions(-)
14
2 files changed, 8 insertions(+), 46 deletions(-)
21
15
22
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
diff --git a/block/qcow2.h b/block/qcow2.h
23
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
24
--- a/qapi/block-core.json
18
--- a/block/qcow2.h
25
+++ b/qapi/block-core.json
19
+++ b/block/qcow2.h
26
@@ -XXX,XX +XXX,XX @@
20
@@ -XXX,XX +XXX,XX @@ uint32_t offset_to_reftable_index(BDRVQcow2State *s, uint64_t offset)
27
'size': 'size' } }
21
}
28
22
29
##
23
/* qcow2.c functions */
30
+# @BlockdevCreateOptionsVdi:
24
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
31
+#
25
- int64_t sector_num, int nb_sectors);
32
+# Driver specific image creation options for VDI.
26
-
33
+#
27
int64_t qcow2_refcount_metadata_size(int64_t clusters, size_t cluster_size,
34
+# @file Node to create the image format on
28
int refcount_order, bool generous_increase,
35
+# @size Size of the virtual disk in bytes
29
uint64_t *refblock_count);
36
+# @static Whether to create a statically (true) or
30
diff --git a/block/qcow2.c b/block/qcow2.c
37
+# dynamically (false) allocated image
38
+# (default: false, i.e. dynamic)
39
+#
40
+# Since: 2.12
41
+##
42
+{ 'struct': 'BlockdevCreateOptionsVdi',
43
+ 'data': { 'file': 'BlockdevRef',
44
+ 'size': 'size',
45
+ '*static': 'bool' } }
46
+
47
+##
48
# @BlockdevCreateNotSupported:
49
#
50
# This is used for all drivers that don't support creating images.
51
diff --git a/block/vdi.c b/block/vdi.c
52
index XXXXXXX..XXXXXXX 100644
31
index XXXXXXX..XXXXXXX 100644
53
--- a/block/vdi.c
32
--- a/block/qcow2.c
54
+++ b/block/vdi.c
33
+++ b/block/qcow2.c
55
@@ -XXX,XX +XXX,XX @@
34
@@ -XXX,XX +XXX,XX @@ static int64_t coroutine_fn qcow2_co_get_block_status(BlockDriverState *bs,
56
35
return status;
57
#include "qemu/osdep.h"
58
#include "qapi/error.h"
59
+#include "qapi/qmp/qdict.h"
60
+#include "qapi/qobject-input-visitor.h"
61
+#include "qapi/qapi-visit-block-core.h"
62
#include "block/block_int.h"
63
#include "sysemu/block-backend.h"
64
#include "qemu/module.h"
65
@@ -XXX,XX +XXX,XX @@
66
#define VDI_DISK_SIZE_MAX ((uint64_t)VDI_BLOCKS_IN_IMAGE_MAX * \
67
(uint64_t)DEFAULT_CLUSTER_SIZE)
68
69
+static QemuOptsList vdi_create_opts;
70
+
71
typedef struct {
72
char text[0x40];
73
uint32_t signature;
74
@@ -XXX,XX +XXX,XX @@ nonallocating_write:
75
return ret;
76
}
36
}
77
37
78
-static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
38
-/* handle reading after the end of the backing file */
79
- Error **errp)
39
-int qcow2_backing_read1(BlockDriverState *bs, QEMUIOVector *qiov,
80
+static int coroutine_fn vdi_co_do_create(const char *filename,
40
- int64_t offset, int bytes)
81
+ QemuOpts *file_opts,
41
-{
82
+ BlockdevCreateOptionsVdi *vdi_opts,
42
- uint64_t bs_size = bs->total_sectors * BDRV_SECTOR_SIZE;
83
+ size_t block_size, Error **errp)
43
- int n1;
44
-
45
- if ((offset + bytes) <= bs_size) {
46
- return bytes;
47
- }
48
-
49
- if (offset >= bs_size) {
50
- n1 = 0;
51
- } else {
52
- n1 = bs_size - offset;
53
- }
54
-
55
- qemu_iovec_memset(qiov, n1, 0, bytes - n1);
56
-
57
- return n1;
58
-}
59
-
60
static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
61
uint64_t bytes, QEMUIOVector *qiov,
62
int flags)
84
{
63
{
85
int ret = 0;
64
BDRVQcow2State *s = bs->opaque;
86
uint64_t bytes = 0;
65
- int offset_in_cluster, n1;
87
uint32_t blocks;
66
+ int offset_in_cluster;
88
- size_t block_size = DEFAULT_CLUSTER_SIZE;
67
int ret;
89
uint32_t image_type = VDI_TYPE_DYNAMIC;
68
unsigned int cur_bytes; /* number of bytes in current iteration */
90
VdiHeader header;
69
uint64_t cluster_offset = 0;
91
size_t i;
70
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset,
92
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
71
case QCOW2_CLUSTER_UNALLOCATED:
93
logout("\n");
72
94
73
if (bs->backing) {
95
/* Read out options. */
74
- /* read from the base image */
96
- bytes = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
75
- n1 = qcow2_backing_read1(bs->backing->bs, &hd_qiov,
97
- BDRV_SECTOR_SIZE);
76
- offset, cur_bytes);
98
-#if defined(CONFIG_VDI_BLOCK_SIZE)
77
- if (n1 > 0) {
99
- /* TODO: Additional checks (SECTOR_SIZE * 2^n, ...). */
78
- QEMUIOVector local_qiov;
100
- block_size = qemu_opt_get_size_del(opts,
79
-
101
- BLOCK_OPT_CLUSTER_SIZE,
80
- qemu_iovec_init(&local_qiov, hd_qiov.niov);
102
- DEFAULT_CLUSTER_SIZE);
81
- qemu_iovec_concat(&local_qiov, &hd_qiov, 0, n1);
103
-#endif
82
-
104
-#if defined(CONFIG_VDI_STATIC_IMAGE)
83
- BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
105
- if (qemu_opt_get_bool_del(opts, BLOCK_OPT_STATIC, false)) {
84
- qemu_co_mutex_unlock(&s->lock);
106
+ bytes = vdi_opts->size;
85
- ret = bdrv_co_preadv(bs->backing, offset, n1,
107
+ if (vdi_opts->q_static) {
86
- &local_qiov, 0);
108
image_type = VDI_TYPE_STATIC;
87
- qemu_co_mutex_lock(&s->lock);
109
}
88
-
110
+#ifndef CONFIG_VDI_STATIC_IMAGE
89
- qemu_iovec_destroy(&local_qiov);
111
+ if (image_type == VDI_TYPE_STATIC) {
90
-
112
+ ret = -ENOTSUP;
91
- if (ret < 0) {
113
+ error_setg(errp, "Statically allocated images cannot be created in "
92
- goto fail;
114
+ "this build");
93
- }
115
+ goto exit;
94
+ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO);
116
+ }
95
+ qemu_co_mutex_unlock(&s->lock);
117
+#endif
96
+ ret = bdrv_co_preadv(bs->backing, offset, cur_bytes,
118
+#ifndef CONFIG_VDI_BLOCK_SIZE
97
+ &hd_qiov, 0);
119
+ if (block_size != DEFAULT_CLUSTER_SIZE) {
98
+ qemu_co_mutex_lock(&s->lock);
120
+ ret = -ENOTSUP;
99
+ if (ret < 0) {
121
+ error_setg(errp,
100
+ goto fail;
122
+ "A non-default cluster size is not supported in this build");
101
}
123
+ goto exit;
102
} else {
124
+ }
103
/* Note: in this case, no need to wait */
125
#endif
126
127
if (bytes > VDI_DISK_SIZE_MAX) {
128
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
129
goto exit;
130
}
131
132
- ret = bdrv_create_file(filename, opts, &local_err);
133
+ ret = bdrv_create_file(filename, file_opts, &local_err);
134
if (ret < 0) {
135
error_propagate(errp, local_err);
136
goto exit;
137
@@ -XXX,XX +XXX,XX @@ exit:
138
return ret;
139
}
140
141
+static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
142
+ Error **errp)
143
+{
144
+ QDict *qdict = NULL;
145
+ BlockdevCreateOptionsVdi *create_options = NULL;
146
+ uint64_t block_size = DEFAULT_CLUSTER_SIZE;
147
+ Visitor *v;
148
+ Error *local_err = NULL;
149
+ int ret;
150
+
151
+ /* Since CONFIG_VDI_BLOCK_SIZE is disabled by default,
152
+ * cluster-size is not part of the QAPI schema; therefore we have
153
+ * to parse it before creating the QAPI object. */
154
+#if defined(CONFIG_VDI_BLOCK_SIZE)
155
+ block_size = qemu_opt_get_size_del(opts,
156
+ BLOCK_OPT_CLUSTER_SIZE,
157
+ DEFAULT_CLUSTER_SIZE);
158
+ if (block_size < BDRV_SECTOR_SIZE || block_size > UINT32_MAX ||
159
+ !is_power_of_2(block_size))
160
+ {
161
+ error_setg(errp, "Invalid cluster size");
162
+ ret = -EINVAL;
163
+ goto done;
164
+ }
165
+#endif
166
+
167
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
168
+
169
+ qdict_put_str(qdict, "file", ""); /* FIXME */
170
+
171
+ /* Get the QAPI object */
172
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
173
+ visit_type_BlockdevCreateOptionsVdi(v, NULL, &create_options, &local_err);
174
+ visit_free(v);
175
+
176
+ if (local_err) {
177
+ error_propagate(errp, local_err);
178
+ ret = -EINVAL;
179
+ goto done;
180
+ }
181
+
182
+ create_options->size = ROUND_UP(create_options->size, BDRV_SECTOR_SIZE);
183
+
184
+ ret = vdi_co_do_create(filename, opts, create_options, block_size, errp);
185
+done:
186
+ QDECREF(qdict);
187
+ qapi_free_BlockdevCreateOptionsVdi(create_options);
188
+ return ret;
189
+}
190
+
191
static void vdi_close(BlockDriverState *bs)
192
{
193
BDRVVdiState *s = bs->opaque;
194
--
104
--
195
2.13.6
105
2.13.6
196
106
197
107
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to luks, which enables
1
Removing a quorum child node with x-blockdev-change results in a quorum
2
image creation over QMP.
2
driver state that cannot be recreated with create options because it
3
would require a list with gaps. This causes trouble in at least
4
.bdrv_refresh_filename().
5
6
Document this problem so that we won't accidentally mark the command
7
stable without having addressed it.
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
10
Reviewed-by: Alberto Garcia <berto@igalia.com>
6
---
11
---
7
qapi/block-core.json | 17 ++++++++++++++++-
12
qapi/block-core.json | 4 ++++
8
block/crypto.c | 34 ++++++++++++++++++++++++++++++++++
13
1 file changed, 4 insertions(+)
9
2 files changed, 50 insertions(+), 1 deletion(-)
10
14
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
12
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
17
--- a/qapi/block-core.json
14
+++ b/qapi/block-core.json
18
+++ b/qapi/block-core.json
15
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
16
'*preallocation': 'PreallocMode' } }
20
# does not support all kinds of operations, all kinds of children, nor
17
21
# all block drivers.
18
##
22
#
19
+# @BlockdevCreateOptionsLUKS:
23
+# FIXME Removing children from a quorum node means introducing gaps in the
24
+# child indices. This cannot be represented in the 'children' list of
25
+# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
20
+#
26
+#
21
+# Driver specific image creation options for LUKS.
27
# Warning: The data in a new quorum child MUST be consistent with that of
22
+#
28
# the rest of the array.
23
+# @file Node to create the image format on
24
+# @size Size of the virtual disk in bytes
25
+#
26
+# Since: 2.12
27
+##
28
+{ 'struct': 'BlockdevCreateOptionsLUKS',
29
+ 'base': 'QCryptoBlockCreateOptionsLUKS',
30
+ 'data': { 'file': 'BlockdevRef',
31
+ 'size': 'size' } }
32
+
33
+##
34
# @BlockdevCreateOptionsNfs:
35
#
29
#
36
# Driver specific image creation options for NFS.
37
@@ -XXX,XX +XXX,XX @@
38
'http': 'BlockdevCreateNotSupported',
39
'https': 'BlockdevCreateNotSupported',
40
'iscsi': 'BlockdevCreateNotSupported',
41
- 'luks': 'BlockdevCreateNotSupported',
42
+ 'luks': 'BlockdevCreateOptionsLUKS',
43
'nbd': 'BlockdevCreateNotSupported',
44
'nfs': 'BlockdevCreateOptionsNfs',
45
'null-aio': 'BlockdevCreateNotSupported',
46
diff --git a/block/crypto.c b/block/crypto.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block/crypto.c
49
+++ b/block/crypto.c
50
@@ -XXX,XX +XXX,XX @@ static int block_crypto_open_luks(BlockDriverState *bs,
51
bs, options, flags, errp);
52
}
53
54
+static int coroutine_fn
55
+block_crypto_co_create_luks(BlockdevCreateOptions *create_options, Error **errp)
56
+{
57
+ BlockdevCreateOptionsLUKS *luks_opts;
58
+ BlockDriverState *bs = NULL;
59
+ QCryptoBlockCreateOptions create_opts;
60
+ int ret;
61
+
62
+ assert(create_options->driver == BLOCKDEV_DRIVER_LUKS);
63
+ luks_opts = &create_options->u.luks;
64
+
65
+ bs = bdrv_open_blockdev_ref(luks_opts->file, errp);
66
+ if (bs == NULL) {
67
+ return -EIO;
68
+ }
69
+
70
+ create_opts = (QCryptoBlockCreateOptions) {
71
+ .format = Q_CRYPTO_BLOCK_FORMAT_LUKS,
72
+ .u.luks = *qapi_BlockdevCreateOptionsLUKS_base(luks_opts),
73
+ };
74
+
75
+ ret = block_crypto_co_create_generic(bs, luks_opts->size, &create_opts,
76
+ errp);
77
+ if (ret < 0) {
78
+ goto fail;
79
+ }
80
+
81
+ ret = 0;
82
+fail:
83
+ bdrv_unref(bs);
84
+ return ret;
85
+}
86
+
87
static int coroutine_fn block_crypto_co_create_opts_luks(const char *filename,
88
QemuOpts *opts,
89
Error **errp)
90
@@ -XXX,XX +XXX,XX @@ BlockDriver bdrv_crypto_luks = {
91
.bdrv_open = block_crypto_open_luks,
92
.bdrv_close = block_crypto_close,
93
.bdrv_child_perm = bdrv_format_default_perms,
94
+ .bdrv_co_create = block_crypto_co_create_luks,
95
.bdrv_co_create_opts = block_crypto_co_create_opts_luks,
96
.bdrv_truncate = block_crypto_truncate,
97
.create_opts = &block_crypto_create_opts_luks,
98
--
30
--
99
2.13.6
31
2.13.6
100
32
101
33
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Doug Gale <doug16k@gmail.com>
2
2
3
If speed is '0' it's not actually "less than" the previous speed.
3
Add trace output for commands, errors, and undefined behavior.
4
Kick the job in this case too.
4
Add guest error log output for undefined behavior.
5
Report invalid undefined accesses to MMIO.
6
Annotate unlikely error checks with unlikely.
5
7
6
Signed-off-by: John Snow <jsnow@redhat.com>
8
Signed-off-by: Doug Gale <doug16k@gmail.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
12
---
11
blockjob.c | 2 +-
13
hw/block/nvme.c | 349 ++++++++++++++++++++++++++++++++++++++++++--------
12
1 file changed, 1 insertion(+), 1 deletion(-)
14
hw/block/trace-events | 93 ++++++++++++++
15
2 files changed, 390 insertions(+), 52 deletions(-)
13
16
14
diff --git a/blockjob.c b/blockjob.c
17
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
15
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
16
--- a/blockjob.c
19
--- a/hw/block/nvme.c
17
+++ b/blockjob.c
20
+++ b/hw/block/nvme.c
18
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
21
@@ -XXX,XX +XXX,XX @@
19
}
22
#include "qapi/visitor.h"
20
23
#include "sysemu/block-backend.h"
21
job->speed = speed;
24
22
- if (speed <= old_speed) {
25
+#include "qemu/log.h"
23
+ if (speed && speed <= old_speed) {
26
+#include "trace.h"
27
#include "nvme.h"
28
29
+#define NVME_GUEST_ERR(trace, fmt, ...) \
30
+ do { \
31
+ (trace_##trace)(__VA_ARGS__); \
32
+ qemu_log_mask(LOG_GUEST_ERROR, #trace \
33
+ " in %s: " fmt "\n", __func__, ## __VA_ARGS__); \
34
+ } while (0)
35
+
36
static void nvme_process_sq(void *opaque);
37
38
static void nvme_addr_read(NvmeCtrl *n, hwaddr addr, void *buf, int size)
39
@@ -XXX,XX +XXX,XX @@ static void nvme_isr_notify(NvmeCtrl *n, NvmeCQueue *cq)
40
{
41
if (cq->irq_enabled) {
42
if (msix_enabled(&(n->parent_obj))) {
43
+ trace_nvme_irq_msix(cq->vector);
44
msix_notify(&(n->parent_obj), cq->vector);
45
} else {
46
+ trace_nvme_irq_pin();
47
pci_irq_pulse(&n->parent_obj);
48
}
49
+ } else {
50
+ trace_nvme_irq_masked();
51
}
52
}
53
54
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
55
trans_len = MIN(len, trans_len);
56
int num_prps = (len >> n->page_bits) + 1;
57
58
- if (!prp1) {
59
+ if (unlikely(!prp1)) {
60
+ trace_nvme_err_invalid_prp();
61
return NVME_INVALID_FIELD | NVME_DNR;
62
} else if (n->cmbsz && prp1 >= n->ctrl_mem.addr &&
63
prp1 < n->ctrl_mem.addr + int128_get64(n->ctrl_mem.size)) {
64
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
65
}
66
len -= trans_len;
67
if (len) {
68
- if (!prp2) {
69
+ if (unlikely(!prp2)) {
70
+ trace_nvme_err_invalid_prp2_missing();
71
goto unmap;
72
}
73
if (len > n->page_size) {
74
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
75
uint64_t prp_ent = le64_to_cpu(prp_list[i]);
76
77
if (i == n->max_prp_ents - 1 && len > n->page_size) {
78
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
79
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
80
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
81
goto unmap;
82
}
83
84
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
85
prp_ent = le64_to_cpu(prp_list[i]);
86
}
87
88
- if (!prp_ent || prp_ent & (n->page_size - 1)) {
89
+ if (unlikely(!prp_ent || prp_ent & (n->page_size - 1))) {
90
+ trace_nvme_err_invalid_prplist_ent(prp_ent);
91
goto unmap;
92
}
93
94
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_map_prp(QEMUSGList *qsg, QEMUIOVector *iov, uint64_t prp1,
95
i++;
96
}
97
} else {
98
- if (prp2 & (n->page_size - 1)) {
99
+ if (unlikely(prp2 & (n->page_size - 1))) {
100
+ trace_nvme_err_invalid_prp2_align(prp2);
101
goto unmap;
102
}
103
if (qsg->nsg) {
104
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_dma_read_prp(NvmeCtrl *n, uint8_t *ptr, uint32_t len,
105
QEMUIOVector iov;
106
uint16_t status = NVME_SUCCESS;
107
108
+ trace_nvme_dma_read(prp1, prp2);
109
+
110
if (nvme_map_prp(&qsg, &iov, prp1, prp2, len, n)) {
111
return NVME_INVALID_FIELD | NVME_DNR;
112
}
113
if (qsg.nsg > 0) {
114
- if (dma_buf_read(ptr, len, &qsg)) {
115
+ if (unlikely(dma_buf_read(ptr, len, &qsg))) {
116
+ trace_nvme_err_invalid_dma();
117
status = NVME_INVALID_FIELD | NVME_DNR;
118
}
119
qemu_sglist_destroy(&qsg);
120
} else {
121
- if (qemu_iovec_to_buf(&iov, 0, ptr, len) != len) {
122
+ if (unlikely(qemu_iovec_to_buf(&iov, 0, ptr, len) != len)) {
123
+ trace_nvme_err_invalid_dma();
124
status = NVME_INVALID_FIELD | NVME_DNR;
125
}
126
qemu_iovec_destroy(&iov);
127
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
128
uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS);
129
uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS);
130
131
- if (slba + nlb > ns->id_ns.nsze) {
132
+ if (unlikely(slba + nlb > ns->id_ns.nsze)) {
133
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
134
return NVME_LBA_RANGE | NVME_DNR;
135
}
136
137
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_rw(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd,
138
int is_write = rw->opcode == NVME_CMD_WRITE ? 1 : 0;
139
enum BlockAcctType acct = is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ;
140
141
- if ((slba + nlb) > ns->id_ns.nsze) {
142
+ trace_nvme_rw(is_write ? "write" : "read", nlb, data_size, slba);
143
+
144
+ if (unlikely((slba + nlb) > ns->id_ns.nsze)) {
145
block_acct_invalid(blk_get_stats(n->conf.blk), acct);
146
+ trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze);
147
return NVME_LBA_RANGE | NVME_DNR;
148
}
149
150
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
151
NvmeNamespace *ns;
152
uint32_t nsid = le32_to_cpu(cmd->nsid);
153
154
- if (nsid == 0 || nsid > n->num_namespaces) {
155
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
156
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
157
return NVME_INVALID_NSID | NVME_DNR;
158
}
159
160
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_io_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
161
case NVME_CMD_READ:
162
return nvme_rw(n, ns, cmd, req);
163
default:
164
+ trace_nvme_err_invalid_opc(cmd->opcode);
165
return NVME_INVALID_OPCODE | NVME_DNR;
166
}
167
}
168
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_sq(NvmeCtrl *n, NvmeCmd *cmd)
169
NvmeCQueue *cq;
170
uint16_t qid = le16_to_cpu(c->qid);
171
172
- if (!qid || nvme_check_sqid(n, qid)) {
173
+ if (unlikely(!qid || nvme_check_sqid(n, qid))) {
174
+ trace_nvme_err_invalid_del_sq(qid);
175
return NVME_INVALID_QID | NVME_DNR;
176
}
177
178
+ trace_nvme_del_sq(qid);
179
+
180
sq = n->sq[qid];
181
while (!QTAILQ_EMPTY(&sq->out_req_list)) {
182
req = QTAILQ_FIRST(&sq->out_req_list);
183
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_sq(NvmeCtrl *n, NvmeCmd *cmd)
184
uint16_t qflags = le16_to_cpu(c->sq_flags);
185
uint64_t prp1 = le64_to_cpu(c->prp1);
186
187
- if (!cqid || nvme_check_cqid(n, cqid)) {
188
+ trace_nvme_create_sq(prp1, sqid, cqid, qsize, qflags);
189
+
190
+ if (unlikely(!cqid || nvme_check_cqid(n, cqid))) {
191
+ trace_nvme_err_invalid_create_sq_cqid(cqid);
192
return NVME_INVALID_CQID | NVME_DNR;
193
}
194
- if (!sqid || !nvme_check_sqid(n, sqid)) {
195
+ if (unlikely(!sqid || !nvme_check_sqid(n, sqid))) {
196
+ trace_nvme_err_invalid_create_sq_sqid(sqid);
197
return NVME_INVALID_QID | NVME_DNR;
198
}
199
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
200
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
201
+ trace_nvme_err_invalid_create_sq_size(qsize);
202
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
203
}
204
- if (!prp1 || prp1 & (n->page_size - 1)) {
205
+ if (unlikely(!prp1 || prp1 & (n->page_size - 1))) {
206
+ trace_nvme_err_invalid_create_sq_addr(prp1);
207
return NVME_INVALID_FIELD | NVME_DNR;
208
}
209
- if (!(NVME_SQ_FLAGS_PC(qflags))) {
210
+ if (unlikely(!(NVME_SQ_FLAGS_PC(qflags)))) {
211
+ trace_nvme_err_invalid_create_sq_qflags(NVME_SQ_FLAGS_PC(qflags));
212
return NVME_INVALID_FIELD | NVME_DNR;
213
}
214
sq = g_malloc0(sizeof(*sq));
215
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd)
216
NvmeCQueue *cq;
217
uint16_t qid = le16_to_cpu(c->qid);
218
219
- if (!qid || nvme_check_cqid(n, qid)) {
220
+ if (unlikely(!qid || nvme_check_cqid(n, qid))) {
221
+ trace_nvme_err_invalid_del_cq_cqid(qid);
222
return NVME_INVALID_CQID | NVME_DNR;
223
}
224
225
cq = n->cq[qid];
226
- if (!QTAILQ_EMPTY(&cq->sq_list)) {
227
+ if (unlikely(!QTAILQ_EMPTY(&cq->sq_list))) {
228
+ trace_nvme_err_invalid_del_cq_notempty(qid);
229
return NVME_INVALID_QUEUE_DEL;
230
}
231
+ trace_nvme_del_cq(qid);
232
nvme_free_cq(cq, n);
233
return NVME_SUCCESS;
234
}
235
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_create_cq(NvmeCtrl *n, NvmeCmd *cmd)
236
uint16_t qflags = le16_to_cpu(c->cq_flags);
237
uint64_t prp1 = le64_to_cpu(c->prp1);
238
239
- if (!cqid || !nvme_check_cqid(n, cqid)) {
240
+ trace_nvme_create_cq(prp1, cqid, vector, qsize, qflags,
241
+ NVME_CQ_FLAGS_IEN(qflags) != 0);
242
+
243
+ if (unlikely(!cqid || !nvme_check_cqid(n, cqid))) {
244
+ trace_nvme_err_invalid_create_cq_cqid(cqid);
245
return NVME_INVALID_CQID | NVME_DNR;
246
}
247
- if (!qsize || qsize > NVME_CAP_MQES(n->bar.cap)) {
248
+ if (unlikely(!qsize || qsize > NVME_CAP_MQES(n->bar.cap))) {
249
+ trace_nvme_err_invalid_create_cq_size(qsize);
250
return NVME_MAX_QSIZE_EXCEEDED | NVME_DNR;
251
}
252
- if (!prp1) {
253
+ if (unlikely(!prp1)) {
254
+ trace_nvme_err_invalid_create_cq_addr(prp1);
255
return NVME_INVALID_FIELD | NVME_DNR;
256
}
257
- if (vector > n->num_queues) {
258
+ if (unlikely(vector > n->num_queues)) {
259
+ trace_nvme_err_invalid_create_cq_vector(vector);
260
return NVME_INVALID_IRQ_VECTOR | NVME_DNR;
261
}
262
- if (!(NVME_CQ_FLAGS_PC(qflags))) {
263
+ if (unlikely(!(NVME_CQ_FLAGS_PC(qflags)))) {
264
+ trace_nvme_err_invalid_create_cq_qflags(NVME_CQ_FLAGS_PC(qflags));
265
return NVME_INVALID_FIELD | NVME_DNR;
266
}
267
268
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ctrl(NvmeCtrl *n, NvmeIdentify *c)
269
uint64_t prp1 = le64_to_cpu(c->prp1);
270
uint64_t prp2 = le64_to_cpu(c->prp2);
271
272
+ trace_nvme_identify_ctrl();
273
+
274
return nvme_dma_read_prp(n, (uint8_t *)&n->id_ctrl, sizeof(n->id_ctrl),
275
prp1, prp2);
276
}
277
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_ns(NvmeCtrl *n, NvmeIdentify *c)
278
uint64_t prp1 = le64_to_cpu(c->prp1);
279
uint64_t prp2 = le64_to_cpu(c->prp2);
280
281
- if (nsid == 0 || nsid > n->num_namespaces) {
282
+ trace_nvme_identify_ns(nsid);
283
+
284
+ if (unlikely(nsid == 0 || nsid > n->num_namespaces)) {
285
+ trace_nvme_err_invalid_ns(nsid, n->num_namespaces);
286
return NVME_INVALID_NSID | NVME_DNR;
287
}
288
289
ns = &n->namespaces[nsid - 1];
290
+
291
return nvme_dma_read_prp(n, (uint8_t *)&ns->id_ns, sizeof(ns->id_ns),
292
prp1, prp2);
293
}
294
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify_nslist(NvmeCtrl *n, NvmeIdentify *c)
295
uint16_t ret;
296
int i, j = 0;
297
298
+ trace_nvme_identify_nslist(min_nsid);
299
+
300
list = g_malloc0(data_len);
301
for (i = 0; i < n->num_namespaces; i++) {
302
if (i < min_nsid) {
303
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_identify(NvmeCtrl *n, NvmeCmd *cmd)
304
case 0x02:
305
return nvme_identify_nslist(n, c);
306
default:
307
+ trace_nvme_err_invalid_identify_cns(le32_to_cpu(c->cns));
308
return NVME_INVALID_FIELD | NVME_DNR;
309
}
310
}
311
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
312
switch (dw10) {
313
case NVME_VOLATILE_WRITE_CACHE:
314
result = blk_enable_write_cache(n->conf.blk);
315
+ trace_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
316
break;
317
case NVME_NUMBER_OF_QUEUES:
318
result = cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
319
+ trace_nvme_getfeat_numq(result);
320
break;
321
default:
322
+ trace_nvme_err_invalid_getfeat(dw10);
323
return NVME_INVALID_FIELD | NVME_DNR;
324
}
325
326
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_set_feature(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
327
blk_set_enable_write_cache(n->conf.blk, dw11 & 1);
328
break;
329
case NVME_NUMBER_OF_QUEUES:
330
+ trace_nvme_setfeat_numq((dw11 & 0xFFFF) + 1,
331
+ ((dw11 >> 16) & 0xFFFF) + 1,
332
+ n->num_queues - 1, n->num_queues - 1);
333
req->cqe.result =
334
cpu_to_le32((n->num_queues - 2) | ((n->num_queues - 2) << 16));
335
break;
336
default:
337
+ trace_nvme_err_invalid_setfeat(dw10);
338
return NVME_INVALID_FIELD | NVME_DNR;
339
}
340
return NVME_SUCCESS;
341
@@ -XXX,XX +XXX,XX @@ static uint16_t nvme_admin_cmd(NvmeCtrl *n, NvmeCmd *cmd, NvmeRequest *req)
342
case NVME_ADM_CMD_GET_FEATURES:
343
return nvme_get_feature(n, cmd, req);
344
default:
345
+ trace_nvme_err_invalid_admin_opc(cmd->opcode);
346
return NVME_INVALID_OPCODE | NVME_DNR;
347
}
348
}
349
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
350
uint32_t page_bits = NVME_CC_MPS(n->bar.cc) + 12;
351
uint32_t page_size = 1 << page_bits;
352
353
- if (n->cq[0] || n->sq[0] || !n->bar.asq || !n->bar.acq ||
354
- n->bar.asq & (page_size - 1) || n->bar.acq & (page_size - 1) ||
355
- NVME_CC_MPS(n->bar.cc) < NVME_CAP_MPSMIN(n->bar.cap) ||
356
- NVME_CC_MPS(n->bar.cc) > NVME_CAP_MPSMAX(n->bar.cap) ||
357
- NVME_CC_IOCQES(n->bar.cc) < NVME_CTRL_CQES_MIN(n->id_ctrl.cqes) ||
358
- NVME_CC_IOCQES(n->bar.cc) > NVME_CTRL_CQES_MAX(n->id_ctrl.cqes) ||
359
- NVME_CC_IOSQES(n->bar.cc) < NVME_CTRL_SQES_MIN(n->id_ctrl.sqes) ||
360
- NVME_CC_IOSQES(n->bar.cc) > NVME_CTRL_SQES_MAX(n->id_ctrl.sqes) ||
361
- !NVME_AQA_ASQS(n->bar.aqa) || !NVME_AQA_ACQS(n->bar.aqa)) {
362
+ if (unlikely(n->cq[0])) {
363
+ trace_nvme_err_startfail_cq();
364
+ return -1;
365
+ }
366
+ if (unlikely(n->sq[0])) {
367
+ trace_nvme_err_startfail_sq();
368
+ return -1;
369
+ }
370
+ if (unlikely(!n->bar.asq)) {
371
+ trace_nvme_err_startfail_nbarasq();
372
+ return -1;
373
+ }
374
+ if (unlikely(!n->bar.acq)) {
375
+ trace_nvme_err_startfail_nbaracq();
376
+ return -1;
377
+ }
378
+ if (unlikely(n->bar.asq & (page_size - 1))) {
379
+ trace_nvme_err_startfail_asq_misaligned(n->bar.asq);
380
+ return -1;
381
+ }
382
+ if (unlikely(n->bar.acq & (page_size - 1))) {
383
+ trace_nvme_err_startfail_acq_misaligned(n->bar.acq);
384
+ return -1;
385
+ }
386
+ if (unlikely(NVME_CC_MPS(n->bar.cc) <
387
+ NVME_CAP_MPSMIN(n->bar.cap))) {
388
+ trace_nvme_err_startfail_page_too_small(
389
+ NVME_CC_MPS(n->bar.cc),
390
+ NVME_CAP_MPSMIN(n->bar.cap));
391
+ return -1;
392
+ }
393
+ if (unlikely(NVME_CC_MPS(n->bar.cc) >
394
+ NVME_CAP_MPSMAX(n->bar.cap))) {
395
+ trace_nvme_err_startfail_page_too_large(
396
+ NVME_CC_MPS(n->bar.cc),
397
+ NVME_CAP_MPSMAX(n->bar.cap));
398
+ return -1;
399
+ }
400
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) <
401
+ NVME_CTRL_CQES_MIN(n->id_ctrl.cqes))) {
402
+ trace_nvme_err_startfail_cqent_too_small(
403
+ NVME_CC_IOCQES(n->bar.cc),
404
+ NVME_CTRL_CQES_MIN(n->bar.cap));
405
+ return -1;
406
+ }
407
+ if (unlikely(NVME_CC_IOCQES(n->bar.cc) >
408
+ NVME_CTRL_CQES_MAX(n->id_ctrl.cqes))) {
409
+ trace_nvme_err_startfail_cqent_too_large(
410
+ NVME_CC_IOCQES(n->bar.cc),
411
+ NVME_CTRL_CQES_MAX(n->bar.cap));
412
+ return -1;
413
+ }
414
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) <
415
+ NVME_CTRL_SQES_MIN(n->id_ctrl.sqes))) {
416
+ trace_nvme_err_startfail_sqent_too_small(
417
+ NVME_CC_IOSQES(n->bar.cc),
418
+ NVME_CTRL_SQES_MIN(n->bar.cap));
419
+ return -1;
420
+ }
421
+ if (unlikely(NVME_CC_IOSQES(n->bar.cc) >
422
+ NVME_CTRL_SQES_MAX(n->id_ctrl.sqes))) {
423
+ trace_nvme_err_startfail_sqent_too_large(
424
+ NVME_CC_IOSQES(n->bar.cc),
425
+ NVME_CTRL_SQES_MAX(n->bar.cap));
426
+ return -1;
427
+ }
428
+ if (unlikely(!NVME_AQA_ASQS(n->bar.aqa))) {
429
+ trace_nvme_err_startfail_asqent_sz_zero();
430
+ return -1;
431
+ }
432
+ if (unlikely(!NVME_AQA_ACQS(n->bar.aqa))) {
433
+ trace_nvme_err_startfail_acqent_sz_zero();
434
return -1;
435
}
436
437
@@ -XXX,XX +XXX,XX @@ static int nvme_start_ctrl(NvmeCtrl *n)
438
static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
439
unsigned size)
440
{
441
+ if (unlikely(offset & (sizeof(uint32_t) - 1))) {
442
+ NVME_GUEST_ERR(nvme_ub_mmiowr_misaligned32,
443
+ "MMIO write not 32-bit aligned,"
444
+ " offset=0x%"PRIx64"", offset);
445
+ /* should be ignored, fall through for now */
446
+ }
447
+
448
+ if (unlikely(size < sizeof(uint32_t))) {
449
+ NVME_GUEST_ERR(nvme_ub_mmiowr_toosmall,
450
+ "MMIO write smaller than 32-bits,"
451
+ " offset=0x%"PRIx64", size=%u",
452
+ offset, size);
453
+ /* should be ignored, fall through for now */
454
+ }
455
+
456
switch (offset) {
457
- case 0xc:
458
+ case 0xc: /* INTMS */
459
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
460
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
461
+ "undefined access to interrupt mask set"
462
+ " when MSI-X is enabled");
463
+ /* should be ignored, fall through for now */
464
+ }
465
n->bar.intms |= data & 0xffffffff;
466
n->bar.intmc = n->bar.intms;
467
+ trace_nvme_mmio_intm_set(data & 0xffffffff,
468
+ n->bar.intmc);
469
break;
470
- case 0x10:
471
+ case 0x10: /* INTMC */
472
+ if (unlikely(msix_enabled(&(n->parent_obj)))) {
473
+ NVME_GUEST_ERR(nvme_ub_mmiowr_intmask_with_msix,
474
+ "undefined access to interrupt mask clr"
475
+ " when MSI-X is enabled");
476
+ /* should be ignored, fall through for now */
477
+ }
478
n->bar.intms &= ~(data & 0xffffffff);
479
n->bar.intmc = n->bar.intms;
480
+ trace_nvme_mmio_intm_clr(data & 0xffffffff,
481
+ n->bar.intmc);
482
break;
483
- case 0x14:
484
+ case 0x14: /* CC */
485
+ trace_nvme_mmio_cfg(data & 0xffffffff);
486
/* Windows first sends data, then sends enable bit */
487
if (!NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc) &&
488
!NVME_CC_SHN(data) && !NVME_CC_SHN(n->bar.cc))
489
@@ -XXX,XX +XXX,XX @@ static void nvme_write_bar(NvmeCtrl *n, hwaddr offset, uint64_t data,
490
491
if (NVME_CC_EN(data) && !NVME_CC_EN(n->bar.cc)) {
492
n->bar.cc = data;
493
- if (nvme_start_ctrl(n)) {
494
+ if (unlikely(nvme_start_ctrl(n))) {
495
+ trace_nvme_err_startfail();
496
n->bar.csts = NVME_CSTS_FAILED;
497
} else {
498
+ trace_nvme_mmio_start_success();
499
n->bar.csts = NVME_CSTS_READY;
500
}
501
} else if (!NVME_CC_EN(data) && NVME_CC_EN(n->bar.cc)) {
502
+ trace_nvme_mmio_stopped();
503
nvme_clear_ctrl(n);
504
n->bar.csts &= ~NVME_CSTS_READY;
505
}
506
if (NVME_CC_SHN(data) && !(NVME_CC_SHN(n->bar.cc))) {
507
- nvme_clear_ctrl(n);
508
- n->bar.cc = data;
509
- n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
510
+ trace_nvme_mmio_shutdown_set();
511
+ nvme_clear_ctrl(n);
512
+ n->bar.cc = data;
513
+ n->bar.csts |= NVME_CSTS_SHST_COMPLETE;
514
} else if (!NVME_CC_SHN(data) && NVME_CC_SHN(n->bar.cc)) {
515
- n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
516
- n->bar.cc = data;
517
+ trace_nvme_mmio_shutdown_cleared();
518
+ n->bar.csts &= ~NVME_CSTS_SHST_COMPLETE;
519
+ n->bar.cc = data;
520
+ }
521
+ break;
522
+ case 0x1C: /* CSTS */
523
+ if (data & (1 << 4)) {
524
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ssreset_w1c_unsupported,
525
+ "attempted to W1C CSTS.NSSRO"
526
+ " but CAP.NSSRS is zero (not supported)");
527
+ } else if (data != 0) {
528
+ NVME_GUEST_ERR(nvme_ub_mmiowr_ro_csts,
529
+ "attempted to set a read only bit"
530
+ " of controller status");
531
+ }
532
+ break;
533
+ case 0x20: /* NSSR */
534
+ if (data == 0x4E564D65) {
535
+ trace_nvme_ub_mmiowr_ssreset_unsupported();
536
+ } else {
537
+ /* The spec says that writes of other values have no effect */
538
+ return;
539
}
540
break;
541
- case 0x24:
542
+ case 0x24: /* AQA */
543
n->bar.aqa = data & 0xffffffff;
544
+ trace_nvme_mmio_aqattr(data & 0xffffffff);
545
break;
546
- case 0x28:
547
+ case 0x28: /* ASQ */
548
n->bar.asq = data;
549
+ trace_nvme_mmio_asqaddr(data);
550
break;
551
- case 0x2c:
552
+ case 0x2c: /* ASQ hi */
553
n->bar.asq |= data << 32;
554
+ trace_nvme_mmio_asqaddr_hi(data, n->bar.asq);
555
break;
556
- case 0x30:
557
+ case 0x30: /* ACQ */
558
+ trace_nvme_mmio_acqaddr(data);
559
n->bar.acq = data;
560
break;
561
- case 0x34:
562
+ case 0x34: /* ACQ hi */
563
n->bar.acq |= data << 32;
564
+ trace_nvme_mmio_acqaddr_hi(data, n->bar.acq);
565
break;
566
+ case 0x38: /* CMBLOC */
567
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbloc_reserved,
568
+ "invalid write to reserved CMBLOC"
569
+ " when CMBSZ is zero, ignored");
570
+ return;
571
+ case 0x3C: /* CMBSZ */
572
+ NVME_GUEST_ERR(nvme_ub_mmiowr_cmbsz_readonly,
573
+ "invalid write to read only CMBSZ, ignored");
574
+ return;
575
default:
576
+ NVME_GUEST_ERR(nvme_ub_mmiowr_invalid,
577
+ "invalid MMIO write,"
578
+ " offset=0x%"PRIx64", data=%"PRIx64"",
579
+ offset, data);
580
break;
581
}
582
}
583
@@ -XXX,XX +XXX,XX @@ static uint64_t nvme_mmio_read(void *opaque, hwaddr addr, unsigned size)
584
uint8_t *ptr = (uint8_t *)&n->bar;
585
uint64_t val = 0;
586
587
+ if (unlikely(addr & (sizeof(uint32_t) - 1))) {
588
+ NVME_GUEST_ERR(nvme_ub_mmiord_misaligned32,
589
+ "MMIO read not 32-bit aligned,"
590
+ " offset=0x%"PRIx64"", addr);
591
+ /* should RAZ, fall through for now */
592
+ } else if (unlikely(size < sizeof(uint32_t))) {
593
+ NVME_GUEST_ERR(nvme_ub_mmiord_toosmall,
594
+ "MMIO read smaller than 32-bits,"
595
+ " offset=0x%"PRIx64"", addr);
596
+ /* should RAZ, fall through for now */
597
+ }
598
+
599
if (addr < sizeof(n->bar)) {
600
memcpy(&val, ptr + addr, size);
601
+ } else {
602
+ NVME_GUEST_ERR(nvme_ub_mmiord_invalid_ofs,
603
+ "MMIO read beyond last register,"
604
+ " offset=0x%"PRIx64", returning 0", addr);
605
}
606
+
607
return val;
608
}
609
610
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
611
{
612
uint32_t qid;
613
614
- if (addr & ((1 << 2) - 1)) {
615
+ if (unlikely(addr & ((1 << 2) - 1))) {
616
+ NVME_GUEST_ERR(nvme_ub_db_wr_misaligned,
617
+ "doorbell write not 32-bit aligned,"
618
+ " offset=0x%"PRIx64", ignoring", addr);
24
return;
619
return;
25
}
620
}
26
621
622
if (((addr - 0x1000) >> 2) & 1) {
623
+ /* Completion queue doorbell write */
624
+
625
uint16_t new_head = val & 0xffff;
626
int start_sqs;
627
NvmeCQueue *cq;
628
629
qid = (addr - (0x1000 + (1 << 2))) >> 3;
630
- if (nvme_check_cqid(n, qid)) {
631
+ if (unlikely(nvme_check_cqid(n, qid))) {
632
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cq,
633
+ "completion queue doorbell write"
634
+ " for nonexistent queue,"
635
+ " sqid=%"PRIu32", ignoring", qid);
636
return;
637
}
638
639
cq = n->cq[qid];
640
- if (new_head >= cq->size) {
641
+ if (unlikely(new_head >= cq->size)) {
642
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_cqhead,
643
+ "completion queue doorbell write value"
644
+ " beyond queue size, sqid=%"PRIu32","
645
+ " new_head=%"PRIu16", ignoring",
646
+ qid, new_head);
647
return;
648
}
649
650
@@ -XXX,XX +XXX,XX @@ static void nvme_process_db(NvmeCtrl *n, hwaddr addr, int val)
651
nvme_isr_notify(n, cq);
652
}
653
} else {
654
+ /* Submission queue doorbell write */
655
+
656
uint16_t new_tail = val & 0xffff;
657
NvmeSQueue *sq;
658
659
qid = (addr - 0x1000) >> 3;
660
- if (nvme_check_sqid(n, qid)) {
661
+ if (unlikely(nvme_check_sqid(n, qid))) {
662
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sq,
663
+ "submission queue doorbell write"
664
+ " for nonexistent queue,"
665
+ " sqid=%"PRIu32", ignoring", qid);
666
return;
667
}
668
669
sq = n->sq[qid];
670
- if (new_tail >= sq->size) {
671
+ if (unlikely(new_tail >= sq->size)) {
672
+ NVME_GUEST_ERR(nvme_ub_db_wr_invalid_sqtail,
673
+ "submission queue doorbell write value"
674
+ " beyond queue size, sqid=%"PRIu32","
675
+ " new_tail=%"PRIu16", ignoring",
676
+ qid, new_tail);
677
return;
678
}
679
680
diff --git a/hw/block/trace-events b/hw/block/trace-events
681
index XXXXXXX..XXXXXXX 100644
682
--- a/hw/block/trace-events
683
+++ b/hw/block/trace-events
684
@@ -XXX,XX +XXX,XX @@ virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint6
685
hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d"
686
hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d"
687
688
+# hw/block/nvme.c
689
+# nvme traces for successful events
690
+nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u"
691
+nvme_irq_pin(void) "pulsing IRQ pin"
692
+nvme_irq_masked(void) "IRQ is masked"
693
+nvme_dma_read(uint64_t prp1, uint64_t prp2) "DMA read, prp1=0x%"PRIx64" prp2=0x%"PRIx64""
694
+nvme_rw(char const *verb, uint32_t blk_count, uint64_t byte_count, uint64_t lba) "%s %"PRIu32" blocks (%"PRIu64" bytes) from LBA %"PRIu64""
695
+nvme_create_sq(uint64_t addr, uint16_t sqid, uint16_t cqid, uint16_t qsize, uint16_t qflags) "create submission queue, addr=0x%"PRIx64", sqid=%"PRIu16", cqid=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16""
696
+nvme_create_cq(uint64_t addr, uint16_t cqid, uint16_t vector, uint16_t size, uint16_t qflags, int ien) "create completion queue, addr=0x%"PRIx64", cqid=%"PRIu16", vector=%"PRIu16", qsize=%"PRIu16", qflags=%"PRIu16", ien=%d"
697
+nvme_del_sq(uint16_t qid) "deleting submission queue sqid=%"PRIu16""
698
+nvme_del_cq(uint16_t cqid) "deleted completion queue, sqid=%"PRIu16""
699
+nvme_identify_ctrl(void) "identify controller"
700
+nvme_identify_ns(uint16_t ns) "identify namespace, nsid=%"PRIu16""
701
+nvme_identify_nslist(uint16_t ns) "identify namespace list, nsid=%"PRIu16""
702
+nvme_getfeat_vwcache(char const* result) "get feature volatile write cache, result=%s"
703
+nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
704
+nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
705
+nvme_mmio_intm_set(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask set, data=0x%"PRIx64", new_mask=0x%"PRIx64""
706
+nvme_mmio_intm_clr(uint64_t data, uint64_t new_mask) "wrote MMIO, interrupt mask clr, data=0x%"PRIx64", new_mask=0x%"PRIx64""
707
+nvme_mmio_cfg(uint64_t data) "wrote MMIO, config controller config=0x%"PRIx64""
708
+nvme_mmio_aqattr(uint64_t data) "wrote MMIO, admin queue attributes=0x%"PRIx64""
709
+nvme_mmio_asqaddr(uint64_t data) "wrote MMIO, admin submission queue address=0x%"PRIx64""
710
+nvme_mmio_acqaddr(uint64_t data) "wrote MMIO, admin completion queue address=0x%"PRIx64""
711
+nvme_mmio_asqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin submission queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
712
+nvme_mmio_acqaddr_hi(uint64_t data, uint64_t new_addr) "wrote MMIO, admin completion queue high half=0x%"PRIx64", new_address=0x%"PRIx64""
713
+nvme_mmio_start_success(void) "setting controller enable bit succeeded"
714
+nvme_mmio_stopped(void) "cleared controller enable bit"
715
+nvme_mmio_shutdown_set(void) "shutdown bit set"
716
+nvme_mmio_shutdown_cleared(void) "shutdown bit cleared"
717
+
718
+# nvme traces for error conditions
719
+nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size"
720
+nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64""
721
+nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64""
722
+nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred"
723
+nvme_err_invalid_field(void) "invalid field"
724
+nvme_err_invalid_prp(void) "invalid PRP"
725
+nvme_err_invalid_sgl(void) "invalid SGL"
726
+nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u"
727
+nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8""
728
+nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8""
729
+nvme_err_invalid_lba_range(uint64_t start, uint64_t len, uint64_t limit) "Invalid LBA start=%"PRIu64" len=%"PRIu64" limit=%"PRIu64""
730
+nvme_err_invalid_del_sq(uint16_t qid) "invalid submission queue deletion, sid=%"PRIu16""
731
+nvme_err_invalid_create_sq_cqid(uint16_t cqid) "failed creating submission queue, invalid cqid=%"PRIu16""
732
+nvme_err_invalid_create_sq_sqid(uint16_t sqid) "failed creating submission queue, invalid sqid=%"PRIu16""
733
+nvme_err_invalid_create_sq_size(uint16_t qsize) "failed creating submission queue, invalid qsize=%"PRIu16""
734
+nvme_err_invalid_create_sq_addr(uint64_t addr) "failed creating submission queue, addr=0x%"PRIx64""
735
+nvme_err_invalid_create_sq_qflags(uint16_t qflags) "failed creating submission queue, qflags=%"PRIu16""
736
+nvme_err_invalid_del_cq_cqid(uint16_t cqid) "failed deleting completion queue, cqid=%"PRIu16""
737
+nvme_err_invalid_del_cq_notempty(uint16_t cqid) "failed deleting completion queue, it is not empty, cqid=%"PRIu16""
738
+nvme_err_invalid_create_cq_cqid(uint16_t cqid) "failed creating completion queue, cqid=%"PRIu16""
739
+nvme_err_invalid_create_cq_size(uint16_t size) "failed creating completion queue, size=%"PRIu16""
740
+nvme_err_invalid_create_cq_addr(uint64_t addr) "failed creating completion queue, addr=0x%"PRIx64""
741
+nvme_err_invalid_create_cq_vector(uint16_t vector) "failed creating completion queue, vector=%"PRIu16""
742
+nvme_err_invalid_create_cq_qflags(uint16_t qflags) "failed creating completion queue, qflags=%"PRIu16""
743
+nvme_err_invalid_identify_cns(uint16_t cns) "identify, invalid cns=0x%"PRIx16""
744
+nvme_err_invalid_getfeat(int dw10) "invalid get features, dw10=0x%"PRIx32""
745
+nvme_err_invalid_setfeat(uint32_t dw10) "invalid set features, dw10=0x%"PRIx32""
746
+nvme_err_startfail_cq(void) "nvme_start_ctrl failed because there are non-admin completion queues"
747
+nvme_err_startfail_sq(void) "nvme_start_ctrl failed because there are non-admin submission queues"
748
+nvme_err_startfail_nbarasq(void) "nvme_start_ctrl failed because the admin submission queue address is null"
749
+nvme_err_startfail_nbaracq(void) "nvme_start_ctrl failed because the admin completion queue address is null"
750
+nvme_err_startfail_asq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin submission queue address is misaligned: 0x%"PRIx64""
751
+nvme_err_startfail_acq_misaligned(uint64_t addr) "nvme_start_ctrl failed because the admin completion queue address is misaligned: 0x%"PRIx64""
752
+nvme_err_startfail_page_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too small: log2size=%u, min=%u"
753
+nvme_err_startfail_page_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the page size is too large: log2size=%u, max=%u"
754
+nvme_err_startfail_cqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too small: log2size=%u, min=%u"
755
+nvme_err_startfail_cqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the completion queue entry size is too large: log2size=%u, max=%u"
756
+nvme_err_startfail_sqent_too_small(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too small: log2size=%u, min=%u"
757
+nvme_err_startfail_sqent_too_large(uint8_t log2ps, uint8_t maxlog2ps) "nvme_start_ctrl failed because the submission queue entry size is too large: log2size=%u, max=%u"
758
+nvme_err_startfail_asqent_sz_zero(void) "nvme_start_ctrl failed because the admin submission queue size is zero"
759
+nvme_err_startfail_acqent_sz_zero(void) "nvme_start_ctrl failed because the admin completion queue size is zero"
760
+nvme_err_startfail(void) "setting controller enable bit failed"
761
+
762
+# Traces for undefined behavior
763
+nvme_ub_mmiowr_misaligned32(uint64_t offset) "MMIO write not 32-bit aligned, offset=0x%"PRIx64""
764
+nvme_ub_mmiowr_toosmall(uint64_t offset, unsigned size) "MMIO write smaller than 32 bits, offset=0x%"PRIx64", size=%u"
765
+nvme_ub_mmiowr_intmask_with_msix(void) "undefined access to interrupt mask set when MSI-X is enabled"
766
+nvme_ub_mmiowr_ro_csts(void) "attempted to set a read only bit of controller status"
767
+nvme_ub_mmiowr_ssreset_w1c_unsupported(void) "attempted to W1C CSTS.NSSRO but CAP.NSSRS is zero (not supported)"
768
+nvme_ub_mmiowr_ssreset_unsupported(void) "attempted NVM subsystem reset but CAP.NSSRS is zero (not supported)"
769
+nvme_ub_mmiowr_cmbloc_reserved(void) "invalid write to reserved CMBLOC when CMBSZ is zero, ignored"
770
+nvme_ub_mmiowr_cmbsz_readonly(void) "invalid write to read only CMBSZ, ignored"
771
+nvme_ub_mmiowr_invalid(uint64_t offset, uint64_t data) "invalid MMIO write, offset=0x%"PRIx64", data=0x%"PRIx64""
772
+nvme_ub_mmiord_misaligned32(uint64_t offset) "MMIO read not 32-bit aligned, offset=0x%"PRIx64""
773
+nvme_ub_mmiord_toosmall(uint64_t offset) "MMIO read smaller than 32-bits, offset=0x%"PRIx64""
774
+nvme_ub_mmiord_invalid_ofs(uint64_t offset) "MMIO read beyond last register, offset=0x%"PRIx64", returning 0"
775
+nvme_ub_db_wr_misaligned(uint64_t offset) "doorbell write not 32-bit aligned, offset=0x%"PRIx64", ignoring"
776
+nvme_ub_db_wr_invalid_cq(uint32_t qid) "completion queue doorbell write for nonexistent queue, cqid=%"PRIu32", ignoring"
777
+nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue doorbell write value beyond queue size, cqid=%"PRIu32", new_head=%"PRIu16", ignoring"
778
+nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring"
779
+nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring"
780
+
781
# hw/block/xen_disk.c
782
xen_disk_alloc(char *name) "%s"
783
xen_disk_init(char *name) "%s"
27
--
784
--
28
2.13.6
785
2.13.6
29
786
30
787
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Reopen flags are not synchronized according to the
3
Management tools create overlays of running guests with qemu-img:
4
bdrv_reopen_queue_child precedence until bdrv_reopen_prepare. It is a
5
bit too late: we already check the consistency in bdrv_check_perm before
6
that.
7
4
8
This fixes the bug that when bdrv_reopen a RO node as RW, the flags for
5
$ qemu-img create -b /image/in/use.qcow2 -f qcow2 /overlay/image.qcow2
9
backing child are wrong. Before, we could recurse with flags.rw=1; now,
10
role->inherit_options + update_flags_from_options will make sure to
11
clear the bit when necessary. Note that this will not clear an
12
explicitly set bit, as in the case of parallel block jobs (e.g.
13
test_stream_parallel in 030), because the explicit options include
14
'read-only=false' (for an intermediate node used by a different job).
15
6
7
but this doesn't work anymore due to image locking:
8
9
qemu-img: /overlay/image.qcow2: Failed to get shared "write" lock
10
Is another process using the image?
11
Could not open backing image to determine size.
12
Use the force share option to allow this use case again.
13
14
Cc: qemu-stable@nongnu.org
16
Signed-off-by: Fam Zheng <famz@redhat.com>
15
Signed-off-by: Fam Zheng <famz@redhat.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Reviewed-by: Eric Blake <eblake@redhat.com>
18
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
19
---
18
---
20
block.c | 8 ++++++++
19
block.c | 3 ++-
21
1 file changed, 8 insertions(+)
20
1 file changed, 2 insertions(+), 1 deletion(-)
22
21
23
diff --git a/block.c b/block.c
22
diff --git a/block.c b/block.c
24
index XXXXXXX..XXXXXXX 100644
23
index XXXXXXX..XXXXXXX 100644
25
--- a/block.c
24
--- a/block.c
26
+++ b/block.c
25
+++ b/block.c
27
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
26
@@ -XXX,XX +XXX,XX @@ void bdrv_img_create(const char *filename, const char *fmt,
28
27
back_flags = flags;
29
/* Inherit from parent node */
28
back_flags &= ~(BDRV_O_RDWR | BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING);
30
if (parent_options) {
29
31
+ QemuOpts *opts;
30
+ backing_options = qdict_new();
32
+ QDict *options_copy;
31
if (backing_fmt) {
33
assert(!flags);
32
- backing_options = qdict_new();
34
role->inherit_options(&flags, options, parent_flags, parent_options);
33
qdict_put_str(backing_options, "driver", backing_fmt);
35
+ options_copy = qdict_clone_shallow(options);
34
}
36
+ opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort);
35
+ qdict_put_bool(backing_options, BDRV_OPT_FORCE_SHARE, true);
37
+ qemu_opts_absorb_qdict(opts, options_copy, NULL);
36
38
+ update_flags_from_options(&flags, opts);
37
bs = bdrv_open(full_backing, NULL, backing_options, back_flags,
39
+ qemu_opts_del(opts);
38
&local_err);
40
+ QDECREF(options_copy);
41
}
42
43
/* Old values are used for options that aren't set yet */
44
--
39
--
45
2.13.6
40
2.13.6
46
41
47
42
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Expose the "manual" property via QAPI for the backup-related jobs.
3
It's not working anymore since QEMU v1.3.0 - time to remove it now.
4
As of this commit, this allows the management API to request the
5
"concluded" and "dismiss" semantics for backup jobs.
6
4
7
Signed-off-by: John Snow <jsnow@redhat.com>
5
Signed-off-by: Thomas Huth <thuth@redhat.com>
6
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Markus Armbruster <armbru@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
---
9
---
10
qapi/block-core.json | 48 ++++++++++++++++++++++++++++++++++++++--------
10
blockdev.c | 11 -----------
11
blockdev.c | 31 +++++++++++++++++++++++++++---
11
qemu-doc.texi | 6 ------
12
blockjob.c | 2 ++
12
2 files changed, 17 deletions(-)
13
tests/qemu-iotests/109.out | 24 +++++++++++------------
14
4 files changed, 82 insertions(+), 23 deletions(-)
15
13
16
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qapi/block-core.json
19
+++ b/qapi/block-core.json
20
@@ -XXX,XX +XXX,XX @@
21
#
22
# @status: Current job state/status (since 2.12)
23
#
24
+# @auto-finalize: Job will finalize itself when PENDING, moving to
25
+# the CONCLUDED state. (since 2.12)
26
+#
27
+# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the NULL
28
+# state and disappearing from the query list. (since 2.12)
29
+#
30
# Since: 1.1
31
##
32
{ 'struct': 'BlockJobInfo',
33
'data': {'type': 'str', 'device': 'str', 'len': 'int',
34
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
35
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
36
- 'status': 'BlockJobStatus' } }
37
+ 'status': 'BlockJobStatus',
38
+ 'auto-finalize': 'bool', 'auto-dismiss': 'bool' } }
39
40
##
41
# @query-block-jobs:
42
@@ -XXX,XX +XXX,XX @@
43
# default 'report' (no limitations, since this applies to
44
# a different block device than @device).
45
#
46
+# @auto-finalize: When false, this job will wait in a PENDING state after it has
47
+# finished its work, waiting for @block-job-finalize.
48
+# When true, this job will automatically perform its abort or
49
+# commit actions.
50
+# Defaults to true. (Since 2.12)
51
+#
52
+# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it
53
+# has completed ceased all work, and wait for @block-job-dismiss.
54
+# When true, this job will automatically disappear from the query
55
+# list without user intervention.
56
+# Defaults to true. (Since 2.12)
57
+#
58
# Note: @on-source-error and @on-target-error only affect background
59
# I/O. If an error occurs during a guest write request, the device's
60
# rerror/werror actions will be used.
61
@@ -XXX,XX +XXX,XX @@
62
##
63
{ 'struct': 'DriveBackup',
64
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
65
- '*format': 'str', 'sync': 'MirrorSyncMode', '*mode': 'NewImageMode',
66
- '*speed': 'int', '*bitmap': 'str', '*compress': 'bool',
67
+ '*format': 'str', 'sync': 'MirrorSyncMode',
68
+ '*mode': 'NewImageMode', '*speed': 'int',
69
+ '*bitmap': 'str', '*compress': 'bool',
70
'*on-source-error': 'BlockdevOnError',
71
- '*on-target-error': 'BlockdevOnError' } }
72
+ '*on-target-error': 'BlockdevOnError',
73
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
74
75
##
76
# @BlockdevBackup:
77
@@ -XXX,XX +XXX,XX @@
78
# default 'report' (no limitations, since this applies to
79
# a different block device than @device).
80
#
81
+# @auto-finalize: When false, this job will wait in a PENDING state after it has
82
+# finished its work, waiting for @block-job-finalize.
83
+# When true, this job will automatically perform its abort or
84
+# commit actions.
85
+# Defaults to true. (Since 2.12)
86
+#
87
+# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it
88
+# has completed ceased all work, and wait for @block-job-dismiss.
89
+# When true, this job will automatically disappear from the query
90
+# list without user intervention.
91
+# Defaults to true. (Since 2.12)
92
+#
93
# Note: @on-source-error and @on-target-error only affect background
94
# I/O. If an error occurs during a guest write request, the device's
95
# rerror/werror actions will be used.
96
@@ -XXX,XX +XXX,XX @@
97
##
98
{ 'struct': 'BlockdevBackup',
99
'data': { '*job-id': 'str', 'device': 'str', 'target': 'str',
100
- 'sync': 'MirrorSyncMode',
101
- '*speed': 'int',
102
- '*compress': 'bool',
103
+ 'sync': 'MirrorSyncMode', '*speed': 'int', '*compress': 'bool',
104
'*on-source-error': 'BlockdevOnError',
105
- '*on-target-error': 'BlockdevOnError' } }
106
+ '*on-target-error': 'BlockdevOnError',
107
+ '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } }
108
109
##
110
# @blockdev-snapshot-sync:
111
diff --git a/blockdev.c b/blockdev.c
14
diff --git a/blockdev.c b/blockdev.c
112
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
113
--- a/blockdev.c
16
--- a/blockdev.c
114
+++ b/blockdev.c
17
+++ b/blockdev.c
115
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
18
@@ -XXX,XX +XXX,XX @@ QemuOptsList qemu_legacy_drive_opts = {
116
AioContext *aio_context;
19
.type = QEMU_OPT_STRING,
117
QDict *options = NULL;
20
.help = "chs translation (auto, lba, none)",
118
Error *local_err = NULL;
21
},{
119
- int flags;
22
- .name = "boot",
120
+ int flags, job_flags = BLOCK_JOB_DEFAULT;
23
- .type = QEMU_OPT_BOOL,
121
int64_t size;
24
- .help = "(deprecated, ignored)",
122
bool set_backing_hd = false;
25
- },{
123
26
.name = "addr",
124
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
27
.type = QEMU_OPT_STRING,
125
if (!backup->has_job_id) {
28
.help = "pci address (virtio only)",
126
backup->job_id = NULL;
29
@@ -XXX,XX +XXX,XX @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type)
30
goto fail;
127
}
31
}
128
+ if (!backup->has_auto_finalize) {
32
129
+ backup->auto_finalize = true;
33
- /* Deprecated option boot=[on|off] */
130
+ }
34
- if (qemu_opt_get(legacy_opts, "boot") != NULL) {
131
+ if (!backup->has_auto_dismiss) {
35
- fprintf(stderr, "qemu-kvm: boot=on|off is deprecated and will be "
132
+ backup->auto_dismiss = true;
36
- "ignored. Future versions will reject this parameter. Please "
133
+ }
37
- "update your scripts.\n");
134
if (!backup->has_compress) {
38
- }
135
backup->compress = false;
39
-
136
}
40
/* Other deprecated options */
137
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_drive_backup(DriveBackup *backup, BlockJobTxn *txn,
41
if (!qtest_enabled()) {
138
goto out;
42
for (i = 0; i < ARRAY_SIZE(deprecated); i++) {
139
}
43
diff --git a/qemu-doc.texi b/qemu-doc.texi
140
}
141
+ if (!backup->auto_finalize) {
142
+ job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
143
+ }
144
+ if (!backup->auto_dismiss) {
145
+ job_flags |= BLOCK_JOB_MANUAL_DISMISS;
146
+ }
147
148
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
149
backup->sync, bmap, backup->compress,
150
backup->on_source_error, backup->on_target_error,
151
- BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
152
+ job_flags, NULL, NULL, txn, &local_err);
153
bdrv_unref(target_bs);
154
if (local_err != NULL) {
155
error_propagate(errp, local_err);
156
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
157
Error *local_err = NULL;
158
AioContext *aio_context;
159
BlockJob *job = NULL;
160
+ int job_flags = BLOCK_JOB_DEFAULT;
161
162
if (!backup->has_speed) {
163
backup->speed = 0;
164
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
165
if (!backup->has_job_id) {
166
backup->job_id = NULL;
167
}
168
+ if (!backup->has_auto_finalize) {
169
+ backup->auto_finalize = true;
170
+ }
171
+ if (!backup->has_auto_dismiss) {
172
+ backup->auto_dismiss = true;
173
+ }
174
if (!backup->has_compress) {
175
backup->compress = false;
176
}
177
@@ -XXX,XX +XXX,XX @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
178
goto out;
179
}
180
}
181
+ if (!backup->auto_finalize) {
182
+ job_flags |= BLOCK_JOB_MANUAL_FINALIZE;
183
+ }
184
+ if (!backup->auto_dismiss) {
185
+ job_flags |= BLOCK_JOB_MANUAL_DISMISS;
186
+ }
187
job = backup_job_create(backup->job_id, bs, target_bs, backup->speed,
188
backup->sync, NULL, backup->compress,
189
backup->on_source_error, backup->on_target_error,
190
- BLOCK_JOB_DEFAULT, NULL, NULL, txn, &local_err);
191
+ job_flags, NULL, NULL, txn, &local_err);
192
if (local_err != NULL) {
193
error_propagate(errp, local_err);
194
}
195
diff --git a/blockjob.c b/blockjob.c
196
index XXXXXXX..XXXXXXX 100644
44
index XXXXXXX..XXXXXXX 100644
197
--- a/blockjob.c
45
--- a/qemu-doc.texi
198
+++ b/blockjob.c
46
+++ b/qemu-doc.texi
199
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
47
@@ -XXX,XX +XXX,XX @@ deprecated.
200
info->io_status = job->iostatus;
48
201
info->ready = job->ready;
49
@section System emulator command line arguments
202
info->status = job->status;
50
203
+ info->auto_finalize = job->auto_finalize;
51
-@subsection -drive boot=on|off (since 1.3.0)
204
+ info->auto_dismiss = job->auto_dismiss;
52
-
205
return info;
53
-The ``boot=on|off'' option to the ``-drive'' argument is
206
}
54
-ignored. Applications should use the ``bootindex=N'' parameter
207
55
-to set an absolute ordering between devices instead.
208
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
56
-
209
index XXXXXXX..XXXXXXX 100644
57
@subsection -tdf (since 1.3.0)
210
--- a/tests/qemu-iotests/109.out
58
211
+++ b/tests/qemu-iotests/109.out
59
The ``-tdf'' argument is ignored. The behaviour implemented
212
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
213
{"return": {}}
214
{"return": {}}
215
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
216
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
217
+{"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"}]}
218
{"return": {}}
219
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
220
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
221
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
222
{"return": {}}
223
{"return": {}}
224
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
225
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
226
+{"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"}]}
227
{"return": {}}
228
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
229
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
230
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
231
{"return": {}}
232
{"return": {}}
233
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
234
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
235
+{"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"}]}
236
{"return": {}}
237
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
238
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
239
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
240
{"return": {}}
241
{"return": {}}
242
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
243
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
244
+{"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"}]}
245
{"return": {}}
246
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
247
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
248
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
249
{"return": {}}
250
{"return": {}}
251
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
252
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
253
+{"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"}]}
254
{"return": {}}
255
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
256
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
257
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
258
{"return": {}}
259
{"return": {}}
260
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
261
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
262
+{"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"}]}
263
{"return": {}}
264
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
265
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
266
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
267
{"return": {}}
268
{"return": {}}
269
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
270
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
271
+{"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"}]}
272
{"return": {}}
273
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
274
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
275
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
276
{"return": {}}
277
{"return": {}}
278
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
279
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
280
+{"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"}]}
281
{"return": {}}
282
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
283
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
284
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
285
{"return": {}}
286
{"return": {}}
287
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
288
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
289
+{"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"}]}
290
{"return": {}}
291
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
292
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
293
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
294
{"return": {}}
295
{"return": {}}
296
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
297
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
298
+{"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"}]}
299
{"return": {}}
300
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
301
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
302
@@ -XXX,XX +XXX,XX @@ Automatically detecting the format is dangerous for raw images, write operations
303
Specify the 'raw' format explicitly to remove the restrictions.
304
{"return": {}}
305
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
306
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
307
+{"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"}]}
308
{"return": {}}
309
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
310
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
311
@@ -XXX,XX +XXX,XX @@ Images are identical.
312
{"return": {}}
313
{"return": {}}
314
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
315
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
316
+{"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"}]}
317
{"return": {}}
318
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
319
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
320
--
60
--
321
2.13.6
61
2.13.6
322
62
323
63
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
For jobs that are stuck waiting on others in a transaction, it would
3
It's been marked as deprecated since QEMU v2.10.0, and so far nobody
4
be nice to know that they are no longer "running" in that sense, but
4
complained that we should keep it, so let's remove this legacy option
5
instead are waiting on other jobs in the transaction.
5
now to simplify the code quite a bit.
6
6
7
Jobs that are "waiting" in this sense cannot be meaningfully altered
7
Signed-off-by: Thomas Huth <thuth@redhat.com>
8
any longer as they have left their running loop. The only meaningful
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
user verb for jobs in this state is "cancel," which will cancel the
9
Reviewed-by: Markus Armbruster <armbru@redhat.com>
10
whole transaction, too.
11
12
Transitions:
13
Running -> Waiting: Normal transition.
14
Ready -> Waiting: Normal transition.
15
Waiting -> Aborting: Transactional cancellation.
16
Waiting -> Concluded: Normal transition.
17
18
Removed Transitions:
19
Running -> Concluded: Jobs must go to WAITING first.
20
Ready -> Concluded: Jobs must go to WAITING first.
21
22
Verbs:
23
Cancel: Can be applied to WAITING jobs.
24
25
+---------+
26
|UNDEFINED|
27
+--+------+
28
|
29
+--v----+
30
+---------+CREATED+-----------------+
31
| +--+----+ |
32
| | |
33
| +--v----+ +------+ |
34
+---------+RUNNING<----->PAUSED| |
35
| +--+-+--+ +------+ |
36
| | | |
37
| | +------------------+ |
38
| | | |
39
| +--v--+ +-------+ | |
40
+---------+READY<------->STANDBY| | |
41
| +--+--+ +-------+ | |
42
| | | |
43
| +--v----+ | |
44
+---------+WAITING<---------------+ |
45
| +--+----+ |
46
| | |
47
+--v-----+ +--v------+ |
48
|ABORTING+--->CONCLUDED| |
49
+--------+ +--+------+ |
50
| |
51
+--v-+ |
52
|NULL<--------------------+
53
+----+
54
55
Signed-off-by: John Snow <jsnow@redhat.com>
56
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
57
---
11
---
58
qapi/block-core.json | 6 +++++-
12
vl.c | 86 ++-------------------------------------------------------
59
blockjob.c | 37 ++++++++++++++++++++-----------------
13
qemu-doc.texi | 8 ------
60
2 files changed, 25 insertions(+), 18 deletions(-)
14
qemu-options.hx | 19 ++-----------
61
15
3 files changed, 4 insertions(+), 109 deletions(-)
62
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
17
diff --git a/vl.c b/vl.c
63
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
64
--- a/qapi/block-core.json
19
--- a/vl.c
65
+++ b/qapi/block-core.json
20
+++ b/vl.c
66
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
67
# @standby: The job is ready, but paused. This is nearly identical to @paused.
22
const char *boot_order = NULL;
68
# The job may return to @ready or otherwise be canceled.
23
const char *boot_once = NULL;
69
#
24
DisplayState *ds;
70
+# @waiting: The job is waiting for other jobs in the transaction to converge
25
- int cyls, heads, secs, translation;
71
+# to the waiting state. This status will likely not be visible for
26
QemuOpts *opts, *machine_opts;
72
+# the last job in a transaction.
27
- QemuOpts *hda_opts = NULL, *icount_opts = NULL, *accel_opts = NULL;
73
+#
28
+ QemuOpts *icount_opts = NULL, *accel_opts = NULL;
74
# @aborting: The job is in the process of being aborted, and will finish with
29
QemuOptsList *olist;
75
# an error. The job will afterwards report that it is @concluded.
30
int optind;
76
# This status may not be visible to the management process.
31
const char *optarg;
77
@@ -XXX,XX +XXX,XX @@
32
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
78
##
33
79
{ 'enum': 'BlockJobStatus',
34
cpu_model = NULL;
80
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
35
snapshot = 0;
81
- 'aborting', 'concluded', 'null' ] }
36
- cyls = heads = secs = 0;
82
+ 'waiting', 'aborting', 'concluded', 'null' ] }
37
- translation = BIOS_ATA_TRANSLATION_AUTO;
83
38
84
##
39
nb_nics = 0;
85
# @BlockJobInfo:
40
86
diff --git a/blockjob.c b/blockjob.c
41
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
42
if (optind >= argc)
43
break;
44
if (argv[optind][0] != '-') {
45
- hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
46
+ drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS);
47
} else {
48
const QEMUOption *popt;
49
50
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
51
cpu_model = optarg;
52
break;
53
case QEMU_OPTION_hda:
54
- {
55
- char buf[256];
56
- if (cyls == 0)
57
- snprintf(buf, sizeof(buf), "%s", HD_OPTS);
58
- else
59
- snprintf(buf, sizeof(buf),
60
- "%s,cyls=%d,heads=%d,secs=%d%s",
61
- HD_OPTS , cyls, heads, secs,
62
- translation == BIOS_ATA_TRANSLATION_LBA ?
63
- ",trans=lba" :
64
- translation == BIOS_ATA_TRANSLATION_NONE ?
65
- ",trans=none" : "");
66
- drive_add(IF_DEFAULT, 0, optarg, buf);
67
- break;
68
- }
69
case QEMU_OPTION_hdb:
70
case QEMU_OPTION_hdc:
71
case QEMU_OPTION_hdd:
72
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv, char **envp)
73
case QEMU_OPTION_snapshot:
74
snapshot = 1;
75
break;
76
- case QEMU_OPTION_hdachs:
77
- {
78
- const char *p;
79
- p = optarg;
80
- cyls = strtol(p, (char **)&p, 0);
81
- if (cyls < 1 || cyls > 16383)
82
- goto chs_fail;
83
- if (*p != ',')
84
- goto chs_fail;
85
- p++;
86
- heads = strtol(p, (char **)&p, 0);
87
- if (heads < 1 || heads > 16)
88
- goto chs_fail;
89
- if (*p != ',')
90
- goto chs_fail;
91
- p++;
92
- secs = strtol(p, (char **)&p, 0);
93
- if (secs < 1 || secs > 63)
94
- goto chs_fail;
95
- if (*p == ',') {
96
- p++;
97
- if (!strcmp(p, "large")) {
98
- translation = BIOS_ATA_TRANSLATION_LARGE;
99
- } else if (!strcmp(p, "rechs")) {
100
- translation = BIOS_ATA_TRANSLATION_RECHS;
101
- } else if (!strcmp(p, "none")) {
102
- translation = BIOS_ATA_TRANSLATION_NONE;
103
- } else if (!strcmp(p, "lba")) {
104
- translation = BIOS_ATA_TRANSLATION_LBA;
105
- } else if (!strcmp(p, "auto")) {
106
- translation = BIOS_ATA_TRANSLATION_AUTO;
107
- } else {
108
- goto chs_fail;
109
- }
110
- } else if (*p != '\0') {
111
- chs_fail:
112
- error_report("invalid physical CHS format");
113
- exit(1);
114
- }
115
- if (hda_opts != NULL) {
116
- qemu_opt_set_number(hda_opts, "cyls", cyls,
117
- &error_abort);
118
- qemu_opt_set_number(hda_opts, "heads", heads,
119
- &error_abort);
120
- qemu_opt_set_number(hda_opts, "secs", secs,
121
- &error_abort);
122
- if (translation == BIOS_ATA_TRANSLATION_LARGE) {
123
- qemu_opt_set(hda_opts, "trans", "large",
124
- &error_abort);
125
- } else if (translation == BIOS_ATA_TRANSLATION_RECHS) {
126
- qemu_opt_set(hda_opts, "trans", "rechs",
127
- &error_abort);
128
- } else if (translation == BIOS_ATA_TRANSLATION_LBA) {
129
- qemu_opt_set(hda_opts, "trans", "lba",
130
- &error_abort);
131
- } else if (translation == BIOS_ATA_TRANSLATION_NONE) {
132
- qemu_opt_set(hda_opts, "trans", "none",
133
- &error_abort);
134
- }
135
- }
136
- }
137
- error_report("'-hdachs' is deprecated, please use '-device"
138
- " ide-hd,cyls=c,heads=h,secs=s,...' instead");
139
- break;
140
case QEMU_OPTION_numa:
141
opts = qemu_opts_parse_noisily(qemu_find_opts("numa"),
142
optarg, true);
143
diff --git a/qemu-doc.texi b/qemu-doc.texi
87
index XXXXXXX..XXXXXXX 100644
144
index XXXXXXX..XXXXXXX 100644
88
--- a/blockjob.c
145
--- a/qemu-doc.texi
89
+++ b/blockjob.c
146
+++ b/qemu-doc.texi
90
@@ -XXX,XX +XXX,XX @@ static QemuMutex block_job_mutex;
147
@@ -XXX,XX +XXX,XX @@ The ``--net dump'' argument is now replaced with the
91
148
``-object filter-dump'' argument which works in combination
92
/* BlockJob State Transition Table */
149
with the modern ``-netdev`` backends instead.
93
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
150
94
- /* U, C, R, P, Y, S, X, E, N */
151
-@subsection -hdachs (since 2.10.0)
95
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0},
152
-
96
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1, 0, 1},
153
-The ``-hdachs'' argument is now a synonym for setting
97
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1, 0},
154
-the ``cyls'', ``heads'', ``secs'', and ``trans'' properties
98
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0},
155
-on the ``ide-hd'' device using the ``-device'' argument.
99
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1, 0},
156
-The new syntax allows different settings to be provided
100
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
157
-per disk.
101
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 1, 1, 0},
158
-
102
- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 1},
159
@subsection -usbdevice (since 2.10.0)
103
- /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0},
160
104
+ /* U, C, R, P, Y, S, W, X, E, N */
161
The ``-usbdevice DEV'' argument is now a synonym for setting
105
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
162
diff --git a/qemu-options.hx b/qemu-options.hx
106
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 1, 0, 1},
163
index XXXXXXX..XXXXXXX 100644
107
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1, 0, 0},
164
--- a/qemu-options.hx
108
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
165
+++ b/qemu-options.hx
109
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
166
@@ -XXX,XX +XXX,XX @@ of available connectors of a given interface type.
110
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
167
@item media=@var{media}
111
+ /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
168
This option defines the type of the media: disk or cdrom.
112
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
169
@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}]
113
+ /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
170
-These options have the same definition as they have in @option{-hdachs}.
114
+ /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
171
-These parameters are deprecated, use the corresponding parameters
115
};
172
+Force disk physical geometry and the optional BIOS translation (trans=none or
116
173
+lba). These parameters are deprecated, use the corresponding parameters
117
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
174
of @code{-device} instead.
118
- /* U, C, R, P, Y, S, X, E, N */
175
@item snapshot=@var{snapshot}
119
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
176
@var{snapshot} is "on" or "off" and controls snapshot mode for the given drive
120
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
177
@@ -XXX,XX +XXX,XX @@ the raw disk image you use is not written back. You can however force
121
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
178
the write back by pressing @key{C-a s} (@pxref{disk_images}).
122
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
179
ETEXI
123
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
180
124
- [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 1, 0},
181
-DEF("hdachs", HAS_ARG, QEMU_OPTION_hdachs, \
125
+ /* U, C, R, P, Y, S, W, X, E, N */
182
- "-hdachs c,h,s[,t]\n" \
126
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 0, 0, 0},
183
- " force hard disk 0 physical geometry and the optional BIOS\n" \
127
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
184
- " translation (t=none or lba) (usually QEMU can guess them)\n",
128
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
185
- QEMU_ARCH_ALL)
129
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
186
-STEXI
130
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
187
-@item -hdachs @var{c},@var{h},@var{s},[,@var{t}]
131
+ [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
188
-@findex -hdachs
132
};
189
-Force hard disk 0 physical geometry (1 <= @var{c} <= 16383, 1 <=
133
190
-@var{h} <= 16, 1 <= @var{s} <= 63) and optionally force the BIOS
134
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
191
-translation mode (@var{t}=none, lba or auto). Usually QEMU can guess
135
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
192
-all those parameters. This option is deprecated, please use
136
BlockJob *other_job;
193
-@code{-device ide-hd,cyls=c,heads=h,secs=s,...} instead.
137
int rc = 0;
194
-ETEXI
138
195
-
139
+ block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
196
DEF("fsdev", HAS_ARG, QEMU_OPTION_fsdev,
140
+
197
"-fsdev fsdriver,id=id[,path=path,][security_model={mapped-xattr|mapped-file|passthrough|none}]\n"
141
/*
198
" [,writeout=immediate][,readonly][,socket=socket|sock_fd=sock_fd][,fmode=fmode][,dmode=dmode]\n"
142
* Successful completion, see if there are other running jobs in this
143
* txn.
144
--
199
--
145
2.13.6
200
2.13.6
146
201
147
202
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
From: Thomas Huth <thuth@redhat.com>
2
2
3
Signed-off-by: John Snow <jsnow@redhat.com>
3
Looks like we forgot to announce the deprecation of these options in
4
the corresponding chapter of the qemu-doc text, so let's do that now.
5
6
Signed-off-by: Thomas Huth <thuth@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Markus Armbruster <armbru@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
10
---
6
tests/qemu-iotests/056 | 187 +++++++++++++++++++++++++++++++++++++++++++++
11
qemu-doc.texi | 15 +++++++++++++++
7
tests/qemu-iotests/056.out | 4 +-
12
1 file changed, 15 insertions(+)
8
2 files changed, 189 insertions(+), 2 deletions(-)
9
13
10
diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056
14
diff --git a/qemu-doc.texi b/qemu-doc.texi
11
index XXXXXXX..XXXXXXX 100755
15
index XXXXXXX..XXXXXXX 100644
12
--- a/tests/qemu-iotests/056
16
--- a/qemu-doc.texi
13
+++ b/tests/qemu-iotests/056
17
+++ b/qemu-doc.texi
14
@@ -XXX,XX +XXX,XX @@ backing_img = os.path.join(iotests.test_dir, 'backing.img')
18
@@ -XXX,XX +XXX,XX @@ longer be directly supported in QEMU.
15
test_img = os.path.join(iotests.test_dir, 'test.img')
19
The ``-drive if=scsi'' argument is replaced by the the
16
target_img = os.path.join(iotests.test_dir, 'target.img')
20
``-device BUS-TYPE'' argument combined with ``-drive if=none''.
17
21
18
+def img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs):
22
+@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0)
19
+ fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt))
20
+ optargs = []
21
+ for k,v in kwargs.iteritems():
22
+ optargs = optargs + ['-o', '%s=%s' % (k,v)]
23
+ args = ['create', '-f', fmt] + optargs + [fullname, size]
24
+ iotests.qemu_img(*args)
25
+ return fullname
26
+
23
+
27
+def try_remove(img):
24
+The drive geometry arguments are replaced by the the geometry arguments
28
+ try:
25
+that can be specified with the ``-device'' parameter.
29
+ os.remove(img)
30
+ except OSError:
31
+ pass
32
+
26
+
33
+def io_write_patterns(img, patterns):
27
+@subsection -drive serial=... (since 2.10.0)
34
+ for pattern in patterns:
35
+ iotests.qemu_io('-c', 'write -P%s %s %s' % pattern, img)
36
+
28
+
29
+The drive serial argument is replaced by the the serial argument
30
+that can be specified with the ``-device'' parameter.
37
+
31
+
38
class TestSyncModesNoneAndTop(iotests.QMPTestCase):
32
+@subsection -drive addr=... (since 2.10.0)
39
image_len = 64 * 1024 * 1024 # MB
40
41
@@ -XXX,XX +XXX,XX @@ class TestBeforeWriteNotifier(iotests.QMPTestCase):
42
event = self.cancel_and_wait()
43
self.assert_qmp(event, 'data/type', 'backup')
44
45
+class BackupTest(iotests.QMPTestCase):
46
+ def setUp(self):
47
+ self.vm = iotests.VM()
48
+ self.test_img = img_create('test')
49
+ self.dest_img = img_create('dest')
50
+ self.vm.add_drive(self.test_img)
51
+ self.vm.launch()
52
+
33
+
53
+ def tearDown(self):
34
+The drive addr argument is replaced by the the addr argument
54
+ self.vm.shutdown()
35
+that can be specified with the ``-device'' parameter.
55
+ try_remove(self.test_img)
56
+ try_remove(self.dest_img)
57
+
36
+
58
+ def hmp_io_writes(self, drive, patterns):
37
@subsection -net dump (since 2.10.0)
59
+ for pattern in patterns:
38
60
+ self.vm.hmp_qemu_io(drive, 'write -P%s %s %s' % pattern)
39
The ``--net dump'' argument is now replaced with the
61
+ self.vm.hmp_qemu_io(drive, 'flush')
62
+
63
+ def qmp_backup_and_wait(self, cmd='drive-backup', serror=None,
64
+ aerror=None, **kwargs):
65
+ if not self.qmp_backup(cmd, serror, **kwargs):
66
+ return False
67
+ return self.qmp_backup_wait(kwargs['device'], aerror)
68
+
69
+ def qmp_backup(self, cmd='drive-backup',
70
+ error=None, **kwargs):
71
+ self.assertTrue('device' in kwargs)
72
+ res = self.vm.qmp(cmd, **kwargs)
73
+ if error:
74
+ self.assert_qmp(res, 'error/desc', error)
75
+ return False
76
+ self.assert_qmp(res, 'return', {})
77
+ return True
78
+
79
+ def qmp_backup_wait(self, device, error=None):
80
+ event = self.vm.event_wait(name="BLOCK_JOB_COMPLETED",
81
+ match={'data': {'device': device}})
82
+ self.assertNotEqual(event, None)
83
+ try:
84
+ failure = self.dictpath(event, 'data/error')
85
+ except AssertionError:
86
+ # Backup succeeded.
87
+ self.assert_qmp(event, 'data/offset', event['data']['len'])
88
+ return True
89
+ else:
90
+ # Failure.
91
+ self.assert_qmp(event, 'data/error', qerror)
92
+ return False
93
+
94
+ def test_dismiss_false(self):
95
+ res = self.vm.qmp('query-block-jobs')
96
+ self.assert_qmp(res, 'return', [])
97
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
98
+ sync='full', target=self.dest_img,
99
+ auto_dismiss=True)
100
+ res = self.vm.qmp('query-block-jobs')
101
+ self.assert_qmp(res, 'return', [])
102
+
103
+ def test_dismiss_true(self):
104
+ res = self.vm.qmp('query-block-jobs')
105
+ self.assert_qmp(res, 'return', [])
106
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
107
+ sync='full', target=self.dest_img,
108
+ auto_dismiss=False)
109
+ res = self.vm.qmp('query-block-jobs')
110
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
111
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
112
+ self.assert_qmp(res, 'return', {})
113
+ res = self.vm.qmp('query-block-jobs')
114
+ self.assert_qmp(res, 'return', [])
115
+
116
+ def test_dismiss_bad_id(self):
117
+ res = self.vm.qmp('query-block-jobs')
118
+ self.assert_qmp(res, 'return', [])
119
+ res = self.vm.qmp('block-job-dismiss', id='foobar')
120
+ self.assert_qmp(res, 'error/class', 'DeviceNotActive')
121
+
122
+ def test_dismiss_collision(self):
123
+ res = self.vm.qmp('query-block-jobs')
124
+ self.assert_qmp(res, 'return', [])
125
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
126
+ sync='full', target=self.dest_img,
127
+ auto_dismiss=False)
128
+ res = self.vm.qmp('query-block-jobs')
129
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
130
+ # Leave zombie job un-dismissed, observe a failure:
131
+ res = self.qmp_backup_and_wait(serror='Need a root block node',
132
+ device='drive0', format=iotests.imgfmt,
133
+ sync='full', target=self.dest_img,
134
+ auto_dismiss=False)
135
+ self.assertEqual(res, False)
136
+ # OK, dismiss the zombie.
137
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
138
+ self.assert_qmp(res, 'return', {})
139
+ res = self.vm.qmp('query-block-jobs')
140
+ self.assert_qmp(res, 'return', [])
141
+ # Ensure it's really gone.
142
+ self.qmp_backup_and_wait(device='drive0', format=iotests.imgfmt,
143
+ sync='full', target=self.dest_img,
144
+ auto_dismiss=False)
145
+
146
+ def dismissal_failure(self, dismissal_opt):
147
+ res = self.vm.qmp('query-block-jobs')
148
+ self.assert_qmp(res, 'return', [])
149
+ # Give blkdebug something to chew on
150
+ self.hmp_io_writes('drive0',
151
+ (('0x9a', 0, 512),
152
+ ('0x55', '8M', '352k'),
153
+ ('0x78', '15872k', '1M')))
154
+ # Add destination node via blkdebug
155
+ res = self.vm.qmp('blockdev-add',
156
+ node_name='target0',
157
+ driver=iotests.imgfmt,
158
+ file={
159
+ 'driver': 'blkdebug',
160
+ 'image': {
161
+ 'driver': 'file',
162
+ 'filename': self.dest_img
163
+ },
164
+ 'inject-error': [{
165
+ 'event': 'write_aio',
166
+ 'errno': 5,
167
+ 'immediately': False,
168
+ 'once': True
169
+ }],
170
+ })
171
+ self.assert_qmp(res, 'return', {})
172
+
173
+ res = self.qmp_backup(cmd='blockdev-backup',
174
+ device='drive0', target='target0',
175
+ on_target_error='stop',
176
+ sync='full',
177
+ auto_dismiss=dismissal_opt)
178
+ self.assertTrue(res)
179
+ event = self.vm.event_wait(name="BLOCK_JOB_ERROR",
180
+ match={'data': {'device': 'drive0'}})
181
+ self.assertNotEqual(event, None)
182
+ # OK, job should be wedged
183
+ res = self.vm.qmp('query-block-jobs')
184
+ self.assert_qmp(res, 'return[0]/status', 'paused')
185
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
186
+ self.assert_qmp(res, 'error/desc',
187
+ "Job 'drive0' in state 'paused' cannot accept"
188
+ " command verb 'dismiss'")
189
+ res = self.vm.qmp('query-block-jobs')
190
+ self.assert_qmp(res, 'return[0]/status', 'paused')
191
+ # OK, unstick job and move forward.
192
+ res = self.vm.qmp('block-job-resume', device='drive0')
193
+ self.assert_qmp(res, 'return', {})
194
+ # And now we need to wait for it to conclude;
195
+ res = self.qmp_backup_wait(device='drive0')
196
+ self.assertTrue(res)
197
+ if not dismissal_opt:
198
+ # Job should now be languishing:
199
+ res = self.vm.qmp('query-block-jobs')
200
+ self.assert_qmp(res, 'return[0]/status', 'concluded')
201
+ res = self.vm.qmp('block-job-dismiss', id='drive0')
202
+ self.assert_qmp(res, 'return', {})
203
+ res = self.vm.qmp('query-block-jobs')
204
+ self.assert_qmp(res, 'return', [])
205
+
206
+ def test_dismiss_premature(self):
207
+ self.dismissal_failure(False)
208
+
209
+ def test_dismiss_erroneous(self):
210
+ self.dismissal_failure(True)
211
+
212
if __name__ == '__main__':
213
iotests.main(supported_fmts=['qcow2', 'qed'])
214
diff --git a/tests/qemu-iotests/056.out b/tests/qemu-iotests/056.out
215
index XXXXXXX..XXXXXXX 100644
216
--- a/tests/qemu-iotests/056.out
217
+++ b/tests/qemu-iotests/056.out
218
@@ -XXX,XX +XXX,XX @@
219
-...
220
+.........
221
----------------------------------------------------------------------
222
-Ran 3 tests
223
+Ran 9 tests
224
225
OK
226
--
40
--
227
2.13.6
41
2.13.6
228
42
229
43
diff view generated by jsdifflib
1
From: Fam Zheng <famz@redhat.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Signed-off-by: Fam Zheng <famz@redhat.com>
3
Signed-off-by: Fam Zheng <famz@redhat.com>
4
Reviewed-by: Max Reitz <mreitz@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
---
5
---
7
tests/qemu-iotests/153 | 12 ++++++++++++
6
include/block/block_int.h | 1 -
8
tests/qemu-iotests/153.out | 5 +++++
7
block/io.c | 18 ------------------
9
2 files changed, 17 insertions(+)
8
2 files changed, 19 deletions(-)
10
9
11
diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153
10
diff --git a/include/block/block_int.h b/include/block/block_int.h
12
index XXXXXXX..XXXXXXX 100755
13
--- a/tests/qemu-iotests/153
14
+++ b/tests/qemu-iotests/153
15
@@ -XXX,XX +XXX,XX @@ rm -f "${TEST_IMG}.lnk" &>/dev/null
16
ln -s ${TEST_IMG} "${TEST_IMG}.lnk" || echo "Failed to create link"
17
_run_qemu_with_images "${TEST_IMG}.lnk" "${TEST_IMG}"
18
19
+echo
20
+echo "== Active commit to intermediate layer should work when base in use =="
21
+_launch_qemu -drive format=$IMGFMT,file="${TEST_IMG}.a",id=drive0,if=none \
22
+ -device virtio-blk,drive=drive0
23
+
24
+_send_qemu_cmd $QEMU_HANDLE \
25
+ "{ 'execute': 'qmp_capabilities' }" \
26
+ 'return'
27
+_run_cmd $QEMU_IMG commit -b "${TEST_IMG}.b" "${TEST_IMG}.c"
28
+
29
+_cleanup_qemu
30
+
31
_launch_qemu
32
33
_send_qemu_cmd $QEMU_HANDLE \
34
diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out
35
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
36
--- a/tests/qemu-iotests/153.out
12
--- a/include/block/block_int.h
37
+++ b/tests/qemu-iotests/153.out
13
+++ b/include/block/block_int.h
38
@@ -XXX,XX +XXX,XX @@ Is another process using the image?
14
@@ -XXX,XX +XXX,XX @@ bool blk_dev_is_tray_open(BlockBackend *blk);
39
== Symbolic link ==
15
bool blk_dev_is_medium_locked(BlockBackend *blk);
40
QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock
16
41
Is another process using the image?
17
void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes);
42
+
18
-bool bdrv_requests_pending(BlockDriverState *bs);
43
+== Active commit to intermediate layer should work when base in use ==
19
44
+{"return": {}}
20
void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out);
45
+
21
void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in);
46
+_qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c
22
diff --git a/block/io.c b/block/io.c
47
{"return": {}}
23
index XXXXXXX..XXXXXXX 100644
48
Adding drive
24
--- a/block/io.c
49
25
+++ b/block/io.c
26
@@ -XXX,XX +XXX,XX @@ void bdrv_disable_copy_on_read(BlockDriverState *bs)
27
assert(old >= 1);
28
}
29
30
-/* Check if any requests are in-flight (including throttled requests) */
31
-bool bdrv_requests_pending(BlockDriverState *bs)
32
-{
33
- BdrvChild *child;
34
-
35
- if (atomic_read(&bs->in_flight)) {
36
- return true;
37
- }
38
-
39
- QLIST_FOREACH(child, &bs->children, next) {
40
- if (bdrv_requests_pending(child->bs)) {
41
- return true;
42
- }
43
- }
44
-
45
- return false;
46
-}
47
-
48
typedef struct {
49
Coroutine *co;
50
BlockDriverState *bs;
50
--
51
--
51
2.13.6
52
2.13.6
52
53
53
54
diff view generated by jsdifflib
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
2
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
2
Reviewed-by: Fam Zheng <famz@redhat.com>
3
---
3
---
4
tests/qemu-iotests/209 | 210 +++++++++++++++++++++++++++++++++++++++++++
4
block/io.c | 6 ++++++
5
tests/qemu-iotests/209.out | 136 ++++++++++++++++++++++++++++
5
1 file changed, 6 insertions(+)
6
tests/qemu-iotests/common.rc | 2 +-
7
tests/qemu-iotests/group | 1 +
8
4 files changed, 348 insertions(+), 1 deletion(-)
9
create mode 100755 tests/qemu-iotests/209
10
create mode 100644 tests/qemu-iotests/209.out
11
6
12
diff --git a/tests/qemu-iotests/209 b/tests/qemu-iotests/209
7
diff --git a/block/io.c b/block/io.c
13
new file mode 100755
8
index XXXXXXX..XXXXXXX 100644
14
index XXXXXXX..XXXXXXX
9
--- a/block/io.c
15
--- /dev/null
10
+++ b/block/io.c
16
+++ b/tests/qemu-iotests/209
11
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
17
@@ -XXX,XX +XXX,XX @@
12
BdrvNextIterator it;
18
+#!/bin/bash
13
GSList *aio_ctxs = NULL, *ctx;
19
+#
14
20
+# Test luks and file image creation
15
+ /* BDRV_POLL_WHILE() for a node can only be called from its own I/O thread
21
+#
16
+ * or the main loop AioContext. We potentially use BDRV_POLL_WHILE() on
22
+# Copyright (C) 2018 Red Hat, Inc.
17
+ * nodes in several different AioContexts, so make sure we're in the main
23
+#
18
+ * context. */
24
+# This program is free software; you can redistribute it and/or modify
19
+ assert(qemu_get_current_aio_context() == qemu_get_aio_context());
25
+# it under the terms of the GNU General Public License as published by
26
+# the Free Software Foundation; either version 2 of the License, or
27
+# (at your option) any later version.
28
+#
29
+# This program is distributed in the hope that it will be useful,
30
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
31
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
+# GNU General Public License for more details.
33
+#
34
+# You should have received a copy of the GNU General Public License
35
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
36
+#
37
+
20
+
38
+# creator
21
block_job_pause_all();
39
+owner=kwolf@redhat.com
22
40
+
23
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
41
+seq=`basename $0`
42
+echo "QA output created by $seq"
43
+
44
+here=`pwd`
45
+status=1    # failure is the default!
46
+
47
+# get standard environment, filters and checks
48
+. ./common.rc
49
+. ./common.filter
50
+
51
+_supported_fmt luks
52
+_supported_proto file
53
+_supported_os Linux
54
+
55
+function do_run_qemu()
56
+{
57
+ echo Testing: "$@"
58
+ $QEMU -nographic -qmp stdio -serial none "$@"
59
+ echo
60
+}
61
+
62
+function run_qemu()
63
+{
64
+ do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \
65
+ | _filter_qemu | _filter_imgfmt \
66
+ | _filter_actual_image_size
67
+}
68
+
69
+echo
70
+echo "=== Successful image creation (defaults) ==="
71
+echo
72
+
73
+size=$((128 * 1024 * 1024))
74
+
75
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
76
+{ "execute": "qmp_capabilities" }
77
+{ "execute": "x-blockdev-create",
78
+ "arguments": {
79
+ "driver": "file",
80
+ "filename": "$TEST_IMG_FILE",
81
+ "size": 0
82
+ }
83
+}
84
+{ "execute": "blockdev-add",
85
+ "arguments": {
86
+ "driver": "file",
87
+ "node-name": "imgfile",
88
+ "filename": "$TEST_IMG_FILE"
89
+ }
90
+}
91
+{ "execute": "x-blockdev-create",
92
+ "arguments": {
93
+ "driver": "$IMGFMT",
94
+ "file": "imgfile",
95
+ "key-secret": "keysec0",
96
+ "size": $size,
97
+ "iter-time": 10
98
+ }
99
+}
100
+{ "execute": "quit" }
101
+EOF
102
+
103
+_img_info --format-specific | _filter_img_info --format-specific
104
+
105
+echo
106
+echo "=== Successful image creation (with non-default options) ==="
107
+echo
108
+
109
+# Choose a different size to show that we got a new image
110
+size=$((64 * 1024 * 1024))
111
+
112
+run_qemu -object secret,id=keysec0,data="foo" <<EOF
113
+{ "execute": "qmp_capabilities" }
114
+{ "execute": "x-blockdev-create",
115
+ "arguments": {
116
+ "driver": "file",
117
+ "filename": "$TEST_IMG_FILE",
118
+ "size": 0
119
+ }
120
+}
121
+{ "execute": "x-blockdev-create",
122
+ "arguments": {
123
+ "driver": "$IMGFMT",
124
+ "file": {
125
+ "driver": "file",
126
+ "filename": "$TEST_IMG_FILE"
127
+ },
128
+ "size": $size,
129
+ "key-secret": "keysec0",
130
+ "cipher-alg": "twofish-128",
131
+ "cipher-mode": "ctr",
132
+ "ivgen-alg": "plain64",
133
+ "ivgen-hash-alg": "md5",
134
+ "hash-alg": "sha1",
135
+ "iter-time": 10
136
+ }
137
+}
138
+{ "execute": "quit" }
139
+EOF
140
+
141
+_img_info --format-specific | _filter_img_info --format-specific
142
+
143
+echo
144
+echo "=== Invalid BlockdevRef ==="
145
+echo
146
+
147
+run_qemu <<EOF
148
+{ "execute": "qmp_capabilities" }
149
+{ "execute": "x-blockdev-create",
150
+ "arguments": {
151
+ "driver": "$IMGFMT",
152
+ "file": "this doesn't exist",
153
+ "size": $size
154
+ }
155
+}
156
+{ "execute": "quit" }
157
+EOF
158
+
159
+echo
160
+echo "=== Zero size ==="
161
+echo
162
+
163
+run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
164
+ -object secret,id=keysec0,data="foo" <<EOF
165
+{ "execute": "qmp_capabilities" }
166
+{ "execute": "x-blockdev-create",
167
+ "arguments": {
168
+ "driver": "$IMGFMT",
169
+ "file": "node0",
170
+ "key-secret": "keysec0",
171
+ "size": 0,
172
+ "iter-time": 10
173
+ }
174
+}
175
+{ "execute": "quit" }
176
+EOF
177
+
178
+_img_info | _filter_img_info
179
+
180
+
181
+echo
182
+echo "=== Invalid sizes ==="
183
+echo
184
+
185
+# TODO Negative image sizes aren't handled correctly, but this is a problem
186
+# with QAPI's implementation of the 'size' type and affects other commands as
187
+# well. Once this is fixed, we may want to add a test case here.
188
+
189
+# 1. 2^64 - 512
190
+# 2. 2^63 = 8 EB (qemu-img enforces image sizes less than this)
191
+# 3. 2^63 - 512 (generally valid, but with the crypto header the file will
192
+# exceed 63 bits)
193
+
194
+run_qemu -blockdev driver=file,filename="$TEST_IMG_FILE",node-name=node0 \
195
+ -object secret,id=keysec0,data="foo" <<EOF
196
+{ "execute": "qmp_capabilities" }
197
+{ "execute": "x-blockdev-create",
198
+ "arguments": {
199
+ "driver": "$IMGFMT",
200
+ "file": "node0",
201
+ "key-secret": "keysec0",
202
+ "size": 18446744073709551104
203
+ }
204
+}
205
+{ "execute": "x-blockdev-create",
206
+ "arguments": {
207
+ "driver": "$IMGFMT",
208
+ "file": "node0",
209
+ "key-secret": "keysec0",
210
+ "size": 9223372036854775808
211
+ }
212
+}
213
+{ "execute": "x-blockdev-create",
214
+ "arguments": {
215
+ "driver": "$IMGFMT",
216
+ "file": "node0",
217
+ "key-secret": "keysec0",
218
+ "size": 9223372036854775296
219
+ }
220
+}
221
+{ "execute": "quit" }
222
+EOF
223
+
224
+# success, all done
225
+echo "*** done"
226
+rm -f $seq.full
227
+status=0
228
diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out
229
new file mode 100644
230
index XXXXXXX..XXXXXXX
231
--- /dev/null
232
+++ b/tests/qemu-iotests/209.out
233
@@ -XXX,XX +XXX,XX @@
234
+QA output created by 209
235
+
236
+=== Successful image creation (defaults) ===
237
+
238
+Testing: -object secret,id=keysec0,data=foo
239
+QMP_VERSION
240
+{"return": {}}
241
+{"return": {}}
242
+{"return": {}}
243
+{"return": {}}
244
+{"return": {}}
245
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
246
+
247
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
248
+file format: IMGFMT
249
+virtual size: 128M (134217728 bytes)
250
+Format specific information:
251
+ ivgen alg: plain64
252
+ hash alg: sha256
253
+ cipher alg: aes-256
254
+ uuid: 00000000-0000-0000-0000-000000000000
255
+ cipher mode: xts
256
+ slots:
257
+ [0]:
258
+ active: true
259
+ iters: 1024
260
+ key offset: 4096
261
+ stripes: 4000
262
+ [1]:
263
+ active: false
264
+ key offset: 262144
265
+ [2]:
266
+ active: false
267
+ key offset: 520192
268
+ [3]:
269
+ active: false
270
+ key offset: 778240
271
+ [4]:
272
+ active: false
273
+ key offset: 1036288
274
+ [5]:
275
+ active: false
276
+ key offset: 1294336
277
+ [6]:
278
+ active: false
279
+ key offset: 1552384
280
+ [7]:
281
+ active: false
282
+ key offset: 1810432
283
+ payload offset: 2068480
284
+ master key iters: 1024
285
+
286
+=== Successful image creation (with non-default options) ===
287
+
288
+Testing: -object secret,id=keysec0,data=foo
289
+QMP_VERSION
290
+{"return": {}}
291
+{"return": {}}
292
+{"return": {}}
293
+{"return": {}}
294
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
295
+
296
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
297
+file format: IMGFMT
298
+virtual size: 64M (67108864 bytes)
299
+Format specific information:
300
+ ivgen alg: plain64
301
+ hash alg: sha1
302
+ cipher alg: twofish-128
303
+ uuid: 00000000-0000-0000-0000-000000000000
304
+ cipher mode: ctr
305
+ slots:
306
+ [0]:
307
+ active: true
308
+ iters: 1024
309
+ key offset: 4096
310
+ stripes: 4000
311
+ [1]:
312
+ active: false
313
+ key offset: 69632
314
+ [2]:
315
+ active: false
316
+ key offset: 135168
317
+ [3]:
318
+ active: false
319
+ key offset: 200704
320
+ [4]:
321
+ active: false
322
+ key offset: 266240
323
+ [5]:
324
+ active: false
325
+ key offset: 331776
326
+ [6]:
327
+ active: false
328
+ key offset: 397312
329
+ [7]:
330
+ active: false
331
+ key offset: 462848
332
+ payload offset: 528384
333
+ master key iters: 1024
334
+
335
+=== Invalid BlockdevRef ===
336
+
337
+Testing:
338
+QMP_VERSION
339
+{"return": {}}
340
+{"error": {"class": "GenericError", "desc": "Cannot find device=this doesn't exist nor node_name=this doesn't exist"}}
341
+{"return": {}}
342
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
343
+
344
+
345
+=== Zero size ===
346
+
347
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo
348
+QMP_VERSION
349
+{"return": {}}
350
+{"return": {}}
351
+{"return": {}}
352
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
353
+
354
+image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/t.IMGFMT"}, "key-secret": "keysec0"}
355
+file format: IMGFMT
356
+virtual size: 0 (0 bytes)
357
+
358
+=== Invalid sizes ===
359
+
360
+Testing: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0 -object secret,id=keysec0,data=foo
361
+QMP_VERSION
362
+{"return": {}}
363
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
364
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
365
+{"error": {"class": "GenericError", "desc": "The requested file size is too large"}}
366
+{"return": {}}
367
+{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
368
+
369
+*** done
370
diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc
371
index XXXXXXX..XXXXXXX 100644
372
--- a/tests/qemu-iotests/common.rc
373
+++ b/tests/qemu-iotests/common.rc
374
@@ -XXX,XX +XXX,XX @@ _img_info()
375
376
discard=0
377
regex_json_spec_start='^ *"format-specific": \{'
378
- $QEMU_IMG info "$@" "$TEST_IMG" 2>&1 | \
379
+ $QEMU_IMG info $QEMU_IMG_EXTRA_ARGS "$@" "$TEST_IMG" 2>&1 | \
380
sed -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \
381
-e "s#$TEST_DIR#TEST_DIR#g" \
382
-e "s#$IMGFMT#IMGFMT#g" \
383
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
384
index XXXXXXX..XXXXXXX 100644
385
--- a/tests/qemu-iotests/group
386
+++ b/tests/qemu-iotests/group
387
@@ -XXX,XX +XXX,XX @@
388
205 rw auto quick
389
206 rw auto
390
207 rw auto
391
+209 rw auto
392
--
24
--
393
2.13.6
25
2.13.6
394
26
395
27
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
bdrv_drained_begin() doesn't increase bs->quiesce_counter recursively
2
and also doesn't notify other parent nodes of children, which both means
3
that the child nodes are not actually drained, and bdrv_drained_begin()
4
is providing useful functionality only on a single node.
2
5
3
Instead of automatically transitioning from PENDING to CONCLUDED, gate
6
To keep things consistent, we also shouldn't call the block driver
4
the .prepare() and .commit() phases behind an explicit acknowledgement
7
callbacks recursively.
5
provided by the QMP monitor if auto_finalize = false has been requested.
6
8
7
This allows us to perform graph changes in prepare and/or commit so that
9
A proper recursive drain version that provides an actually working
8
graph changes do not occur autonomously without knowledge of the
10
drained section for child nodes will be introduced later.
9
controlling management layer.
10
11
11
Transactions that have reached the "PENDING" state together can all be
12
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
12
moved to invoke their finalization methods by issuing block_job_finalize
13
Reviewed-by: Fam Zheng <famz@redhat.com>
13
to any one job in the transaction.
14
---
15
block/io.c | 16 +++++++++-------
16
1 file changed, 9 insertions(+), 7 deletions(-)
14
17
15
Jobs in a transaction with mixed job->auto_finalize settings will all
18
diff --git a/block/io.c b/block/io.c
16
remain stuck in the "PENDING" state, as if the entire transaction was
17
specified with auto_finalize = false. Jobs that specified
18
auto_finalize = true, however, will still not emit the PENDING event.
19
20
Signed-off-by: John Snow <jsnow@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
23
qapi/block-core.json | 23 ++++++++++++++++++-
24
include/block/blockjob.h | 17 ++++++++++++++
25
blockdev.c | 14 +++++++++++
26
blockjob.c | 60 +++++++++++++++++++++++++++++++++++-------------
27
block/trace-events | 1 +
28
5 files changed, 98 insertions(+), 17 deletions(-)
29
30
diff --git a/qapi/block-core.json b/qapi/block-core.json
31
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
32
--- a/qapi/block-core.json
20
--- a/block/io.c
33
+++ b/qapi/block-core.json
21
+++ b/block/io.c
34
@@ -XXX,XX +XXX,XX @@
22
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
35
#
36
# @dismiss: see @block-job-dismiss
37
#
38
+# @finalize: see @block-job-finalize
39
+#
40
# Since: 2.12
41
##
42
{ 'enum': 'BlockJobVerb',
43
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss' ] }
44
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
45
+ 'finalize' ] }
46
47
##
48
# @BlockJobStatus:
49
@@ -XXX,XX +XXX,XX @@
50
{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } }
51
52
##
53
+# @block-job-finalize:
54
+#
55
+# Once a job that has manual=true reaches the pending state, it can be
56
+# instructed to finalize any graph changes and do any necessary cleanup
57
+# via this command.
58
+# For jobs in a transaction, instructing one job to finalize will force
59
+# ALL jobs in the transaction to finalize, so it is only necessary to instruct
60
+# a single member job to finalize.
61
+#
62
+# @id: The job identifier.
63
+#
64
+# Returns: Nothing on success
65
+#
66
+# Since: 2.12
67
+##
68
+{ 'command': 'block-job-finalize', 'data': { 'id': 'str' } }
69
+
70
+##
71
# @BlockdevDiscardOptions:
72
#
73
# Determines how to handle discard requests.
74
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
75
index XXXXXXX..XXXXXXX 100644
76
--- a/include/block/blockjob.h
77
+++ b/include/block/blockjob.h
78
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job);
79
*/
80
void block_job_complete(BlockJob *job, Error **errp);
81
82
+
83
+/**
84
+ * block_job_finalize:
85
+ * @job: The job to fully commit and finish.
86
+ * @errp: Error object.
87
+ *
88
+ * For jobs that have finished their work and are pending
89
+ * awaiting explicit acknowledgement to commit their work,
90
+ * This will commit that work.
91
+ *
92
+ * FIXME: Make the below statement universally true:
93
+ * For jobs that support the manual workflow mode, all graph
94
+ * changes that occur as a result will occur after this command
95
+ * and before a successful reply.
96
+ */
97
+void block_job_finalize(BlockJob *job, Error **errp);
98
+
99
/**
100
* block_job_dismiss:
101
* @job: The job to be dismissed.
102
diff --git a/blockdev.c b/blockdev.c
103
index XXXXXXX..XXXXXXX 100644
104
--- a/blockdev.c
105
+++ b/blockdev.c
106
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_complete(const char *device, Error **errp)
107
aio_context_release(aio_context);
108
}
23
}
109
24
110
+void qmp_block_job_finalize(const char *id, Error **errp)
25
/* Recursively call BlockDriver.bdrv_co_drain_begin/end callbacks */
111
+{
26
-static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
112
+ AioContext *aio_context;
27
+static void bdrv_drain_invoke(BlockDriverState *bs, bool begin, bool recursive)
113
+ BlockJob *job = find_block_job(id, &aio_context, errp);
114
+
115
+ if (!job) {
116
+ return;
117
+ }
118
+
119
+ trace_qmp_block_job_finalize(job);
120
+ block_job_finalize(job, errp);
121
+ aio_context_release(aio_context);
122
+}
123
+
124
void qmp_block_job_dismiss(const char *id, Error **errp)
125
{
28
{
126
AioContext *aio_context;
29
BdrvChild *child, *tmp;
127
diff --git a/blockjob.c b/blockjob.c
30
BdrvCoDrainData data = { .bs = bs, .done = false, .begin = begin};
128
index XXXXXXX..XXXXXXX 100644
31
@@ -XXX,XX +XXX,XX @@ static void bdrv_drain_invoke(BlockDriverState *bs, bool begin)
129
--- a/blockjob.c
32
bdrv_coroutine_enter(bs, data.co);
130
+++ b/blockjob.c
33
BDRV_POLL_WHILE(bs, !data.done);
131
@@ -XXX,XX +XXX,XX @@ bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
34
132
[BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
35
- QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
133
[BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
36
- bdrv_drain_invoke(child->bs, begin);
134
[BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
37
+ if (recursive) {
135
+ [BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
38
+ QLIST_FOREACH_SAFE(child, &bs->children, next, tmp) {
136
[BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
39
+ bdrv_drain_invoke(child->bs, begin, true);
137
};
40
+ }
138
139
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
140
}
41
}
141
}
42
}
142
43
143
-static int block_job_completed_single(BlockJob *job)
44
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
144
+static int block_job_finalize_single(BlockJob *job)
45
bdrv_parent_drained_begin(bs);
145
{
146
assert(job->completed);
147
148
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
149
assert(other_job->cancelled);
150
block_job_finish_sync(other_job, NULL, NULL);
151
}
152
- block_job_completed_single(other_job);
153
+ block_job_finalize_single(other_job);
154
aio_context_release(ctx);
155
}
46
}
156
47
157
block_job_txn_unref(txn);
48
- bdrv_drain_invoke(bs, true);
49
+ bdrv_drain_invoke(bs, true, false);
50
bdrv_drain_recurse(bs);
158
}
51
}
159
52
160
+static int block_job_needs_finalize(BlockJob *job)
53
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
161
+{
162
+ return !job->auto_finalize;
163
+}
164
+
165
+static void block_job_do_finalize(BlockJob *job)
166
+{
167
+ int rc;
168
+ assert(job && job->txn);
169
+
170
+ /* prepare the transaction to complete */
171
+ rc = block_job_txn_apply(job->txn, block_job_prepare, true);
172
+ if (rc) {
173
+ block_job_completed_txn_abort(job);
174
+ } else {
175
+ block_job_txn_apply(job->txn, block_job_finalize_single, true);
176
+ }
177
+}
178
+
179
static void block_job_completed_txn_success(BlockJob *job)
180
{
181
BlockJobTxn *txn = job->txn;
182
BlockJob *other_job;
183
- int rc = 0;
184
185
block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
186
187
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
188
assert(other_job->ret == 0);
189
}
54
}
190
55
191
- /* Jobs may require some prep-work to complete without failure */
56
/* Re-enable things in child-to-parent order */
192
- rc = block_job_txn_apply(txn, block_job_prepare, true);
57
- bdrv_drain_invoke(bs, false);
193
- if (rc) {
58
+ bdrv_drain_invoke(bs, false, false);
194
- block_job_completed_txn_abort(job);
59
bdrv_parent_drained_end(bs);
195
- return;
60
aio_enable_external(bdrv_get_aio_context(bs));
196
- }
197
-
198
- /* We are the last completed job, commit the transaction. */
199
block_job_txn_apply(txn, block_job_event_pending, false);
200
- block_job_txn_apply(txn, block_job_completed_single, true);
201
+
202
+ /* If no jobs need manual finalization, automatically do so */
203
+ if (block_job_txn_apply(txn, block_job_needs_finalize, false) == 0) {
204
+ block_job_do_finalize(job);
205
+ }
206
}
61
}
207
62
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
208
/* Assumes the block_job_mutex is held */
63
aio_context_acquire(aio_context);
209
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
64
aio_disable_external(aio_context);
210
job->driver->complete(job, errp);
65
bdrv_parent_drained_begin(bs);
211
}
66
- bdrv_drain_invoke(bs, true);
212
67
+ bdrv_drain_invoke(bs, true, true);
213
+void block_job_finalize(BlockJob *job, Error **errp)
68
aio_context_release(aio_context);
214
+{
69
215
+ assert(job && job->id && job->txn);
70
if (!g_slist_find(aio_ctxs, aio_context)) {
216
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
71
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
217
+ return;
72
218
+ }
73
/* Re-enable things in child-to-parent order */
219
+ block_job_do_finalize(job);
74
aio_context_acquire(aio_context);
220
+}
75
- bdrv_drain_invoke(bs, false);
221
+
76
+ bdrv_drain_invoke(bs, false, true);
222
void block_job_dismiss(BlockJob **jobptr, Error **errp)
77
bdrv_parent_drained_end(bs);
223
{
78
aio_enable_external(aio_context);
224
BlockJob *job = *jobptr;
79
aio_context_release(aio_context);
225
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
226
{
227
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
228
block_job_do_dismiss(job);
229
- } else if (block_job_started(job)) {
230
- block_job_cancel_async(job);
231
- block_job_enter(job);
232
- } else {
233
+ return;
234
+ }
235
+ block_job_cancel_async(job);
236
+ if (!block_job_started(job)) {
237
block_job_completed(job, -ECANCELED);
238
+ } else if (job->deferred_to_main_loop) {
239
+ block_job_completed_txn_abort(job);
240
+ } else {
241
+ block_job_enter(job);
242
}
243
}
244
245
diff --git a/block/trace-events b/block/trace-events
246
index XXXXXXX..XXXXXXX 100644
247
--- a/block/trace-events
248
+++ b/block/trace-events
249
@@ -XXX,XX +XXX,XX @@ qmp_block_job_cancel(void *job) "job %p"
250
qmp_block_job_pause(void *job) "job %p"
251
qmp_block_job_resume(void *job) "job %p"
252
qmp_block_job_complete(void *job) "job %p"
253
+qmp_block_job_finalize(void *job) "job %p"
254
qmp_block_job_dismiss(void *job) "job %p"
255
qmp_block_stream(void *bs, void *job) "bs %p job %p"
256
257
--
80
--
258
2.13.6
81
2.13.6
259
82
260
83
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
The existing test is for bdrv_drain_all_begin/end() only. Generalise the
2
test case so that it can be run for the other variants as well. At the
3
moment this is only bdrv_drain_begin/end(), but in a while, we'll add
4
another one.
2
5
3
The completed_single function is getting a little mucked up with
6
Also, add a backing file to the test node to test whether the operations
4
checking to see which callbacks exist, so let's factor them out.
7
work recursively.
5
8
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: Eric Blake <eblake@redhat.com>
8
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
---
10
---
11
blockjob.c | 35 ++++++++++++++++++++++++++---------
11
tests/test-bdrv-drain.c | 69 ++++++++++++++++++++++++++++++++++++++++++++-----
12
1 file changed, 26 insertions(+), 9 deletions(-)
12
1 file changed, 62 insertions(+), 7 deletions(-)
13
13
14
diff --git a/blockjob.c b/blockjob.c
14
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
15
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
16
--- a/blockjob.c
16
--- a/tests/test-bdrv-drain.c
17
+++ b/blockjob.c
17
+++ b/tests/test-bdrv-drain.c
18
@@ -XXX,XX +XXX,XX @@ static void block_job_update_rc(BlockJob *job)
18
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_test = {
19
}
19
20
.bdrv_co_drain_begin = bdrv_test_co_drain_begin,
21
.bdrv_co_drain_end = bdrv_test_co_drain_end,
22
+
23
+ .bdrv_child_perm = bdrv_format_default_perms,
24
};
25
26
static void aio_ret_cb(void *opaque, int ret)
27
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
28
*aio_ret = ret;
20
}
29
}
21
30
22
+static void block_job_commit(BlockJob *job)
31
-static void test_drv_cb_drain_all(void)
32
+enum drain_type {
33
+ BDRV_DRAIN_ALL,
34
+ BDRV_DRAIN,
35
+};
36
+
37
+static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
23
+{
38
+{
24
+ assert(!job->ret);
39
+ switch (drain_type) {
25
+ if (job->driver->commit) {
40
+ case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
26
+ job->driver->commit(job);
41
+ case BDRV_DRAIN: bdrv_drained_begin(bs); break;
42
+ default: g_assert_not_reached();
27
+ }
43
+ }
28
+}
44
+}
29
+
45
+
30
+static void block_job_abort(BlockJob *job)
46
+static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
31
+{
47
+{
32
+ assert(job->ret);
48
+ switch (drain_type) {
33
+ if (job->driver->abort) {
49
+ case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
34
+ job->driver->abort(job);
50
+ case BDRV_DRAIN: bdrv_drained_end(bs); break;
51
+ default: g_assert_not_reached();
35
+ }
52
+ }
36
+}
53
+}
37
+
54
+
38
+static void block_job_clean(BlockJob *job)
55
+static void test_drv_cb_common(enum drain_type drain_type, bool recursive)
56
{
57
BlockBackend *blk;
58
- BlockDriverState *bs;
59
- BDRVTestState *s;
60
+ BlockDriverState *bs, *backing;
61
+ BDRVTestState *s, *backing_s;
62
BlockAIOCB *acb;
63
int aio_ret;
64
65
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
66
s = bs->opaque;
67
blk_insert_bs(blk, bs, &error_abort);
68
69
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
70
+ backing_s = backing->opaque;
71
+ bdrv_set_backing_hd(bs, backing, &error_abort);
72
+
73
/* Simple bdrv_drain_all_begin/end pair, check that CBs are called */
74
g_assert_cmpint(s->drain_count, ==, 0);
75
- bdrv_drain_all_begin();
76
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
77
+
78
+ do_drain_begin(drain_type, bs);
79
+
80
g_assert_cmpint(s->drain_count, ==, 1);
81
- bdrv_drain_all_end();
82
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
83
+
84
+ do_drain_end(drain_type, bs);
85
+
86
g_assert_cmpint(s->drain_count, ==, 0);
87
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
88
89
/* Now do the same while a request is pending */
90
aio_ret = -EINPROGRESS;
91
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_all(void)
92
g_assert_cmpint(aio_ret, ==, -EINPROGRESS);
93
94
g_assert_cmpint(s->drain_count, ==, 0);
95
- bdrv_drain_all_begin();
96
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
97
+
98
+ do_drain_begin(drain_type, bs);
99
+
100
g_assert_cmpint(aio_ret, ==, 0);
101
g_assert_cmpint(s->drain_count, ==, 1);
102
- bdrv_drain_all_end();
103
+ g_assert_cmpint(backing_s->drain_count, ==, !!recursive);
104
+
105
+ do_drain_end(drain_type, bs);
106
+
107
g_assert_cmpint(s->drain_count, ==, 0);
108
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
109
110
+ bdrv_unref(backing);
111
bdrv_unref(bs);
112
blk_unref(blk);
113
}
114
115
+static void test_drv_cb_drain_all(void)
39
+{
116
+{
40
+ if (job->driver->clean) {
117
+ test_drv_cb_common(BDRV_DRAIN_ALL, true);
41
+ job->driver->clean(job);
42
+ }
43
+}
118
+}
44
+
119
+
45
static void block_job_completed_single(BlockJob *job)
120
+static void test_drv_cb_drain(void)
121
+{
122
+ test_drv_cb_common(BDRV_DRAIN, false);
123
+}
124
+
125
int main(int argc, char **argv)
46
{
126
{
47
assert(job->completed);
127
bdrv_init();
48
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
128
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
49
block_job_update_rc(job);
129
g_test_init(&argc, &argv, NULL);
50
130
51
if (!job->ret) {
131
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
52
- if (job->driver->commit) {
132
+ g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
53
- job->driver->commit(job);
133
54
- }
134
return g_test_run();
55
+ block_job_commit(job);
135
}
56
} else {
57
- if (job->driver->abort) {
58
- job->driver->abort(job);
59
- }
60
- }
61
- if (job->driver->clean) {
62
- job->driver->clean(job);
63
+ block_job_abort(job);
64
}
65
+ block_job_clean(job);
66
67
if (job->cb) {
68
job->cb(job->opaque, job->ret);
69
--
136
--
70
2.13.6
137
2.13.6
71
138
72
139
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
This is currently only working correctly for bdrv_drain(), not for
2
bdrv_drain_all(). Leave a comment for the drain_all case, we'll address
3
it later.
2
4
3
For jobs that have reached their CONCLUDED state, prior to having their
4
last reference put down (meaning jobs that have completed successfully,
5
unsuccessfully, or have been canceled), allow the user to dismiss the
6
job's lingering status report via block-job-dismiss.
7
8
This gives management APIs the chance to conclusively determine if a job
9
failed or succeeded, even if the event broadcast was missed.
10
11
Note: block_job_do_dismiss and block_job_decommission happen to do
12
exactly the same thing, but they're called from different semantic
13
contexts, so both aliases are kept to improve readability.
14
15
Note 2: Don't worry about the 0x04 flag definition for AUTO_DISMISS, she
16
has a friend coming in a future patch to fill the hole where 0x02 is.
17
18
Verbs:
19
Dismiss: operates on CONCLUDED jobs only.
20
Signed-off-by: John Snow <jsnow@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
6
---
23
qapi/block-core.json | 24 +++++++++++++++++++++++-
7
tests/test-bdrv-drain.c | 45 +++++++++++++++++++++++++++++++++++++++++++++
24
include/block/blockjob.h | 14 ++++++++++++++
8
1 file changed, 45 insertions(+)
25
blockdev.c | 14 ++++++++++++++
26
blockjob.c | 26 ++++++++++++++++++++++++--
27
block/trace-events | 1 +
28
5 files changed, 76 insertions(+), 3 deletions(-)
29
9
30
diff --git a/qapi/block-core.json b/qapi/block-core.json
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
31
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
32
--- a/qapi/block-core.json
12
--- a/tests/test-bdrv-drain.c
33
+++ b/qapi/block-core.json
13
+++ b/tests/test-bdrv-drain.c
34
@@ -XXX,XX +XXX,XX @@
14
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
35
#
15
test_drv_cb_common(BDRV_DRAIN, false);
36
# @complete: see @block-job-complete
16
}
37
#
17
38
+# @dismiss: see @block-job-dismiss
18
+static void test_quiesce_common(enum drain_type drain_type, bool recursive)
39
+#
19
+{
40
# Since: 2.12
20
+ BlockBackend *blk;
41
##
21
+ BlockDriverState *bs, *backing;
42
{ 'enum': 'BlockJobVerb',
43
- 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete' ] }
44
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss' ] }
45
46
##
47
# @BlockJobStatus:
48
@@ -XXX,XX +XXX,XX @@
49
{ 'command': 'block-job-complete', 'data': { 'device': 'str' } }
50
51
##
52
+# @block-job-dismiss:
53
+#
54
+# For jobs that have already concluded, remove them from the block-job-query
55
+# list. This command only needs to be run for jobs which were started with
56
+# QEMU 2.12+ job lifetime management semantics.
57
+#
58
+# This command will refuse to operate on any job that has not yet reached
59
+# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
60
+# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
61
+# to be used as appropriate.
62
+#
63
+# @id: The job identifier.
64
+#
65
+# Returns: Nothing on success
66
+#
67
+# Since: 2.12
68
+##
69
+{ 'command': 'block-job-dismiss', 'data': { 'id': 'str' } }
70
+
22
+
71
+##
23
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
72
# @BlockdevDiscardOptions:
24
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
73
#
25
+ &error_abort);
74
# Determines how to handle discard requests.
26
+ blk_insert_bs(blk, bs, &error_abort);
75
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
76
index XXXXXXX..XXXXXXX 100644
77
--- a/include/block/blockjob.h
78
+++ b/include/block/blockjob.h
79
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
80
/** Current state; See @BlockJobStatus for details. */
81
BlockJobStatus status;
82
83
+ /** True if this job should automatically dismiss itself */
84
+ bool auto_dismiss;
85
+
27
+
86
BlockJobTxn *txn;
28
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
87
QLIST_ENTRY(BlockJob) txn_list;
29
+ bdrv_set_backing_hd(bs, backing, &error_abort);
88
} BlockJob;
89
@@ -XXX,XX +XXX,XX @@ typedef enum BlockJobCreateFlags {
90
BLOCK_JOB_DEFAULT = 0x00,
91
/* BlockJob is not QMP-created and should not send QMP events */
92
BLOCK_JOB_INTERNAL = 0x01,
93
+ /* BlockJob requires manual dismiss step */
94
+ BLOCK_JOB_MANUAL_DISMISS = 0x04,
95
} BlockJobCreateFlags;
96
97
/**
98
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job);
99
void block_job_complete(BlockJob *job, Error **errp);
100
101
/**
102
+ * block_job_dismiss:
103
+ * @job: The job to be dismissed.
104
+ * @errp: Error object.
105
+ *
106
+ * Remove a concluded job from the query list.
107
+ */
108
+void block_job_dismiss(BlockJob **job, Error **errp);
109
+
30
+
110
+/**
31
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
111
* block_job_query:
32
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
112
* @job: The job to get information about.
113
*
114
diff --git a/blockdev.c b/blockdev.c
115
index XXXXXXX..XXXXXXX 100644
116
--- a/blockdev.c
117
+++ b/blockdev.c
118
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_complete(const char *device, Error **errp)
119
aio_context_release(aio_context);
120
}
121
122
+void qmp_block_job_dismiss(const char *id, Error **errp)
123
+{
124
+ AioContext *aio_context;
125
+ BlockJob *job = find_block_job(id, &aio_context, errp);
126
+
33
+
127
+ if (!job) {
34
+ do_drain_begin(drain_type, bs);
128
+ return;
129
+ }
130
+
35
+
131
+ trace_qmp_block_job_dismiss(job);
36
+ g_assert_cmpint(bs->quiesce_counter, ==, 1);
132
+ block_job_dismiss(&job, errp);
37
+ g_assert_cmpint(backing->quiesce_counter, ==, !!recursive);
133
+ aio_context_release(aio_context);
38
+
39
+ do_drain_end(drain_type, bs);
40
+
41
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
43
+
44
+ bdrv_unref(backing);
45
+ bdrv_unref(bs);
46
+ blk_unref(blk);
134
+}
47
+}
135
+
48
+
136
void qmp_change_backing_file(const char *device,
49
+static void test_quiesce_drain_all(void)
137
const char *image_node_name,
138
const char *backing_file,
139
diff --git a/blockjob.c b/blockjob.c
140
index XXXXXXX..XXXXXXX 100644
141
--- a/blockjob.c
142
+++ b/blockjob.c
143
@@ -XXX,XX +XXX,XX @@ bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
144
[BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
145
[BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
146
[BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
147
+ [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 1, 0},
148
};
149
150
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
151
@@ -XXX,XX +XXX,XX @@ static void block_job_decommission(BlockJob *job)
152
block_job_unref(job);
153
}
154
155
+static void block_job_do_dismiss(BlockJob *job)
156
+{
50
+{
157
+ block_job_decommission(job);
51
+ // XXX drain_all doesn't quiesce
52
+ //test_quiesce_common(BDRV_DRAIN_ALL, true);
158
+}
53
+}
159
+
54
+
160
static void block_job_conclude(BlockJob *job)
55
+static void test_quiesce_drain(void)
161
{
162
block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
163
+ if (job->auto_dismiss || !block_job_started(job)) {
164
+ block_job_do_dismiss(job);
165
+ }
166
}
167
168
static void block_job_completed_single(BlockJob *job)
169
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
170
QLIST_REMOVE(job, txn_list);
171
block_job_txn_unref(job->txn);
172
block_job_conclude(job);
173
- block_job_decommission(job);
174
}
175
176
static void block_job_cancel_async(BlockJob *job)
177
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
178
job->driver->complete(job, errp);
179
}
180
181
+void block_job_dismiss(BlockJob **jobptr, Error **errp)
182
+{
56
+{
183
+ BlockJob *job = *jobptr;
57
+ test_quiesce_common(BDRV_DRAIN, false);
184
+ /* similarly to _complete, this is QMP-interface only. */
185
+ assert(job->id);
186
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
187
+ return;
188
+ }
189
+
190
+ block_job_do_dismiss(job);
191
+ *jobptr = NULL;
192
+}
58
+}
193
+
59
+
194
void block_job_user_pause(BlockJob *job, Error **errp)
60
int main(int argc, char **argv)
195
{
61
{
196
if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
62
bdrv_init();
197
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
63
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
198
void block_job_cancel(BlockJob *job)
64
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
199
{
65
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
200
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
66
201
- return;
67
+ g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
202
+ block_job_do_dismiss(job);
68
+ g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
203
} else if (block_job_started(job)) {
69
+
204
block_job_cancel_async(job);
70
return g_test_run();
205
block_job_enter(job);
71
}
206
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
207
job->paused = true;
208
job->pause_count = 1;
209
job->refcnt = 1;
210
+ job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
211
block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
212
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
213
QEMU_CLOCK_REALTIME, SCALE_NS,
214
diff --git a/block/trace-events b/block/trace-events
215
index XXXXXXX..XXXXXXX 100644
216
--- a/block/trace-events
217
+++ b/block/trace-events
218
@@ -XXX,XX +XXX,XX @@ qmp_block_job_cancel(void *job) "job %p"
219
qmp_block_job_pause(void *job) "job %p"
220
qmp_block_job_resume(void *job) "job %p"
221
qmp_block_job_complete(void *job) "job %p"
222
+qmp_block_job_dismiss(void *job) "job %p"
223
qmp_block_stream(void *bs, void *job) "bs %p job %p"
224
225
# block/file-win32.c
226
--
72
--
227
2.13.6
73
2.13.6
228
74
229
75
diff view generated by jsdifflib
1
From: Liang Li <liliang.opensource@gmail.com>
1
Block jobs already paused themselves when their main BlockBackend
2
entered a drained section. This is not good enough: We also want to
3
pause a block job and may not submit new requests if, for example, the
4
mirror target node should be drained.
2
5
3
When doing drive mirror to a low speed shared storage, if there was heavy
6
This implements .drained_begin/end callbacks in child_job in order to
4
BLK IO write workload in VM after the 'ready' event, drive mirror block job
7
consider all block nodes related to the job, and removes the
5
can't be canceled immediately, it would keep running until the heavy BLK IO
8
BlockBackend callbacks which are unnecessary now because the root of the
6
workload stopped in the VM.
9
job main BlockBackend is always referenced with a child_job, too.
7
10
8
Libvirt depends on the current block-job-cancel semantics, which is that
9
when used without a flag after the 'ready' event, the command blocks
10
until data is in sync. However, these semantics are awkward in other
11
situations, for example, people may use drive mirror for realtime
12
backups while still wanting to use block live migration. Libvirt cannot
13
start a block live migration while another drive mirror is in progress,
14
but the user would rather abandon the backup attempt as broken and
15
proceed with the live migration than be stuck waiting for the current
16
drive mirror backup to finish.
17
18
The drive-mirror command already includes a 'force' flag, which libvirt
19
does not use, although it documented the flag as only being useful to
20
quit a job which is paused. However, since quitting a paused job has
21
the same effect as abandoning a backup in a non-paused job (namely, the
22
destination file is not in sync, and the command completes immediately),
23
we can just improve the documentation to make the force flag obviously
24
useful.
25
26
Cc: Paolo Bonzini <pbonzini@redhat.com>
27
Cc: Jeff Cody <jcody@redhat.com>
28
Cc: Kevin Wolf <kwolf@redhat.com>
29
Cc: Max Reitz <mreitz@redhat.com>
30
Cc: Eric Blake <eblake@redhat.com>
31
Cc: John Snow <jsnow@redhat.com>
32
Reported-by: Huaitong Han <huanhuaitong@didichuxing.com>
33
Signed-off-by: Huaitong Han <huanhuaitong@didichuxing.com>
34
Signed-off-by: Liang Li <liliangleo@didichuxing.com>
35
Signed-off-by: Jeff Cody <jcody@redhat.com>
36
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
11
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
37
---
12
---
38
qapi/block-core.json | 5 +++--
13
blockjob.c | 22 +++++++++-------------
39
include/block/blockjob.h | 12 ++++++++++--
14
1 file changed, 9 insertions(+), 13 deletions(-)
40
block/mirror.c | 10 ++++------
41
blockdev.c | 4 ++--
42
blockjob.c | 16 +++++++++-------
43
tests/test-blockjob-txn.c | 8 ++++----
44
hmp-commands.hx | 3 ++-
45
7 files changed, 34 insertions(+), 24 deletions(-)
46
15
47
diff --git a/qapi/block-core.json b/qapi/block-core.json
48
index XXXXXXX..XXXXXXX 100644
49
--- a/qapi/block-core.json
50
+++ b/qapi/block-core.json
51
@@ -XXX,XX +XXX,XX @@
52
# the name of the parameter), but since QEMU 2.7 it can have
53
# other values.
54
#
55
-# @force: whether to allow cancellation of a paused job (default
56
-# false). Since 1.3.
57
+# @force: If true, and the job has already emitted the event BLOCK_JOB_READY,
58
+# abandon the job immediately (even if it is paused) instead of waiting
59
+# for the destination to complete its final synchronization (since 1.3)
60
#
61
# Returns: Nothing on success
62
# If no background operation is active on this device, DeviceNotActive
63
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
64
index XXXXXXX..XXXXXXX 100644
65
--- a/include/block/blockjob.h
66
+++ b/include/block/blockjob.h
67
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
68
bool cancelled;
69
70
/**
71
+ * Set to true if the job should abort immediately without waiting
72
+ * for data to be in sync.
73
+ */
74
+ bool force;
75
+
76
+ /**
77
* Counter for pause request. If non-zero, the block job is either paused,
78
* or if busy == true will pause itself as soon as possible.
79
*/
80
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job);
81
/**
82
* block_job_cancel:
83
* @job: The job to be canceled.
84
+ * @force: Quit a job without waiting for data to be in sync.
85
*
86
* Asynchronously cancel the specified job.
87
*/
88
-void block_job_cancel(BlockJob *job);
89
+void block_job_cancel(BlockJob *job, bool force);
90
91
/**
92
* block_job_complete:
93
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp);
94
/**
95
* block_job_user_cancel:
96
* @job: The job to be cancelled.
97
+ * @force: Quit a job without waiting for data to be in sync.
98
*
99
* Cancels the specified job, but may refuse to do so if the
100
* operation isn't currently meaningful.
101
*/
102
-void block_job_user_cancel(BlockJob *job, Error **errp);
103
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp);
104
105
/**
106
* block_job_cancel_sync:
107
diff --git a/block/mirror.c b/block/mirror.c
108
index XXXXXXX..XXXXXXX 100644
109
--- a/block/mirror.c
110
+++ b/block/mirror.c
111
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn mirror_run(void *opaque)
112
113
ret = 0;
114
trace_mirror_before_sleep(s, cnt, s->synced, delay_ns);
115
- if (!s->synced) {
116
- block_job_sleep_ns(&s->common, delay_ns);
117
- if (block_job_is_cancelled(&s->common)) {
118
- break;
119
- }
120
+ if (block_job_is_cancelled(&s->common) && s->common.force) {
121
+ break;
122
} else if (!should_complete) {
123
delay_ns = (s->in_flight == 0 && cnt == 0 ? SLICE_TIME : 0);
124
block_job_sleep_ns(&s->common, delay_ns);
125
@@ -XXX,XX +XXX,XX @@ immediate_exit:
126
* or it was cancelled prematurely so that we do not guarantee that
127
* the target is a copy of the source.
128
*/
129
- assert(ret < 0 || (!s->synced && block_job_is_cancelled(&s->common)));
130
+ assert(ret < 0 || ((s->common.force || !s->synced) &&
131
+ block_job_is_cancelled(&s->common)));
132
assert(need_drain);
133
mirror_wait_for_all_io(s);
134
}
135
diff --git a/blockdev.c b/blockdev.c
136
index XXXXXXX..XXXXXXX 100644
137
--- a/blockdev.c
138
+++ b/blockdev.c
139
@@ -XXX,XX +XXX,XX @@ void blockdev_mark_auto_del(BlockBackend *blk)
140
aio_context_acquire(aio_context);
141
142
if (bs->job) {
143
- block_job_cancel(bs->job);
144
+ block_job_cancel(bs->job, false);
145
}
146
147
aio_context_release(aio_context);
148
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
149
}
150
151
trace_qmp_block_job_cancel(job);
152
- block_job_user_cancel(job, errp);
153
+ block_job_user_cancel(job, force, errp);
154
out:
155
aio_context_release(aio_context);
156
}
157
diff --git a/blockjob.c b/blockjob.c
16
diff --git a/blockjob.c b/blockjob.c
158
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
159
--- a/blockjob.c
18
--- a/blockjob.c
160
+++ b/blockjob.c
19
+++ b/blockjob.c
161
@@ -XXX,XX +XXX,XX @@ static int block_job_finalize_single(BlockJob *job)
20
@@ -XXX,XX +XXX,XX @@ static char *child_job_get_parent_desc(BdrvChild *c)
162
return 0;
21
job->id);
163
}
22
}
164
23
165
-static void block_job_cancel_async(BlockJob *job)
24
-static const BdrvChildRole child_job = {
166
+static void block_job_cancel_async(BlockJob *job, bool force)
25
- .get_parent_desc = child_job_get_parent_desc,
26
- .stay_at_node = true,
27
-};
28
-
29
-static void block_job_drained_begin(void *opaque)
30
+static void child_job_drained_begin(BdrvChild *c)
167
{
31
{
168
if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
32
- BlockJob *job = opaque;
169
block_job_iostatus_reset(job);
33
+ BlockJob *job = c->opaque;
170
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job)
34
block_job_pause(job);
171
job->pause_count--;
172
}
173
job->cancelled = true;
174
+ /* To prevent 'force == false' overriding a previous 'force == true' */
175
+ job->force |= force;
176
}
35
}
177
36
178
static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
37
-static void block_job_drained_end(void *opaque)
179
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
38
+static void child_job_drained_end(BdrvChild *c)
180
* on the caller, so leave it. */
39
{
181
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
40
- BlockJob *job = opaque;
182
if (other_job != job) {
41
+ BlockJob *job = c->opaque;
183
- block_job_cancel_async(other_job);
184
+ block_job_cancel_async(other_job, false);
185
}
186
}
187
while (!QLIST_EMPTY(&txn->jobs)) {
188
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
189
block_job_resume(job);
42
block_job_resume(job);
190
}
43
}
191
44
192
-void block_job_cancel(BlockJob *job)
45
-static const BlockDevOps block_job_dev_ops = {
193
+void block_job_cancel(BlockJob *job, bool force)
46
- .drained_begin = block_job_drained_begin,
194
{
47
- .drained_end = block_job_drained_end,
195
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
48
+static const BdrvChildRole child_job = {
196
block_job_do_dismiss(job);
49
+ .get_parent_desc = child_job_get_parent_desc,
197
return;
50
+ .drained_begin = child_job_drained_begin,
198
}
51
+ .drained_end = child_job_drained_end,
199
- block_job_cancel_async(job);
52
+ .stay_at_node = true,
200
+ block_job_cancel_async(job, force);
53
};
201
if (!block_job_started(job)) {
54
202
block_job_completed(job, -ECANCELED);
55
void block_job_remove_all_bdrv(BlockJob *job)
203
} else if (job->deferred_to_main_loop) {
56
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
204
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
57
block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
205
}
58
bs->job = job;
206
}
59
207
60
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
208
-void block_job_user_cancel(BlockJob *job, Error **errp)
61
bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
209
+void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
62
210
{
63
QLIST_INSERT_HEAD(&block_jobs, job, job_list);
211
if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
212
return;
213
}
214
- block_job_cancel(job);
215
+ block_job_cancel(job, force);
216
}
217
218
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
219
@@ -XXX,XX +XXX,XX @@ void block_job_user_cancel(BlockJob *job, Error **errp)
220
* function pointer casts there. */
221
static void block_job_cancel_err(BlockJob *job, Error **errp)
222
{
223
- block_job_cancel(job);
224
+ block_job_cancel(job, false);
225
}
226
227
int block_job_cancel_sync(BlockJob *job)
228
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
229
index XXXXXXX..XXXXXXX 100644
230
--- a/tests/test-blockjob-txn.c
231
+++ b/tests/test-blockjob-txn.c
232
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
233
block_job_start(job);
234
235
if (expected == -ECANCELED) {
236
- block_job_cancel(job);
237
+ block_job_cancel(job, false);
238
}
239
240
while (result == -EINPROGRESS) {
241
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
242
block_job_txn_unref(txn);
243
244
if (expected1 == -ECANCELED) {
245
- block_job_cancel(job1);
246
+ block_job_cancel(job1, false);
247
}
248
if (expected2 == -ECANCELED) {
249
- block_job_cancel(job2);
250
+ block_job_cancel(job2, false);
251
}
252
253
while (result1 == -EINPROGRESS || result2 == -EINPROGRESS) {
254
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
255
block_job_start(job1);
256
block_job_start(job2);
257
258
- block_job_cancel(job1);
259
+ block_job_cancel(job1, false);
260
261
/* Now make job2 finish before the main loop kicks jobs. This simulates
262
* the race between a pending kick and another job completing.
263
diff --git a/hmp-commands.hx b/hmp-commands.hx
264
index XXXXXXX..XXXXXXX 100644
265
--- a/hmp-commands.hx
266
+++ b/hmp-commands.hx
267
@@ -XXX,XX +XXX,XX @@ ETEXI
268
.args_type = "force:-f,device:B",
269
.params = "[-f] device",
270
.help = "stop an active background block operation (use -f"
271
- "\n\t\t\t if the operation is currently paused)",
272
+ "\n\t\t\t if you want to abort the operation immediately"
273
+ "\n\t\t\t instead of keep running until data is in sync)",
274
.cmd = hmp_block_job_cancel,
275
},
276
277
--
64
--
278
2.13.6
65
2.13.6
279
66
280
67
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
Block jobs must be paused if any of the involved nodes are drained.
2
2
3
Whatever the state a blockjob is in, it should be able to be canceled
4
by the block layer.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
4
---
9
tests/test-blockjob.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++-
5
tests/test-bdrv-drain.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 229 insertions(+), 4 deletions(-)
6
1 file changed, 121 insertions(+)
11
7
12
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
13
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/test-blockjob.c
10
--- a/tests/test-bdrv-drain.c
15
+++ b/tests/test-blockjob.c
11
+++ b/tests/test-bdrv-drain.c
16
@@ -XXX,XX +XXX,XX @@ static void block_job_cb(void *opaque, int ret)
12
@@ -XXX,XX +XXX,XX @@
17
{
13
14
#include "qemu/osdep.h"
15
#include "block/block.h"
16
+#include "block/blockjob_int.h"
17
#include "sysemu/block-backend.h"
18
#include "qapi/error.h"
19
20
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
21
test_quiesce_common(BDRV_DRAIN, false);
18
}
22
}
19
23
20
-static BlockJob *do_test_id(BlockBackend *blk, const char *id,
24
+
21
- bool should_succeed)
25
+typedef struct TestBlockJob {
22
+static BlockJob *mk_job(BlockBackend *blk, const char *id,
26
+ BlockJob common;
23
+ const BlockJobDriver *drv, bool should_succeed,
27
+ bool should_complete;
24
+ int flags)
28
+} TestBlockJob;
25
{
29
+
26
BlockJob *job;
30
+static void test_job_completed(BlockJob *job, void *opaque)
27
Error *errp = NULL;
28
29
- job = block_job_create(id, &test_block_job_driver, NULL, blk_bs(blk),
30
- 0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb,
31
+ job = block_job_create(id, drv, NULL, blk_bs(blk),
32
+ 0, BLK_PERM_ALL, 0, flags, block_job_cb,
33
NULL, &errp);
34
if (should_succeed) {
35
g_assert_null(errp);
36
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
37
return job;
38
}
39
40
+static BlockJob *do_test_id(BlockBackend *blk, const char *id,
41
+ bool should_succeed)
42
+{
31
+{
43
+ return mk_job(blk, id, &test_block_job_driver,
44
+ should_succeed, BLOCK_JOB_DEFAULT);
45
+}
46
+
47
/* This creates a BlockBackend (optionally with a name) with a
48
* BlockDriverState inserted. */
49
static BlockBackend *create_blk(const char *name)
50
@@ -XXX,XX +XXX,XX @@ static void test_job_ids(void)
51
destroy_blk(blk[2]);
52
}
53
54
+typedef struct CancelJob {
55
+ BlockJob common;
56
+ BlockBackend *blk;
57
+ bool should_converge;
58
+ bool should_complete;
59
+ bool completed;
60
+} CancelJob;
61
+
62
+static void cancel_job_completed(BlockJob *job, void *opaque)
63
+{
64
+ CancelJob *s = opaque;
65
+ s->completed = true;
66
+ block_job_completed(job, 0);
32
+ block_job_completed(job, 0);
67
+}
33
+}
68
+
34
+
69
+static void cancel_job_complete(BlockJob *job, Error **errp)
35
+static void coroutine_fn test_job_start(void *opaque)
70
+{
36
+{
71
+ CancelJob *s = container_of(job, CancelJob, common);
37
+ TestBlockJob *s = opaque;
38
+
39
+ while (!s->should_complete) {
40
+ block_job_sleep_ns(&s->common, 100000);
41
+ }
42
+
43
+ block_job_defer_to_main_loop(&s->common, test_job_completed, NULL);
44
+}
45
+
46
+static void test_job_complete(BlockJob *job, Error **errp)
47
+{
48
+ TestBlockJob *s = container_of(job, TestBlockJob, common);
72
+ s->should_complete = true;
49
+ s->should_complete = true;
73
+}
50
+}
74
+
51
+
75
+static void coroutine_fn cancel_job_start(void *opaque)
52
+BlockJobDriver test_job_driver = {
53
+ .instance_size = sizeof(TestBlockJob),
54
+ .start = test_job_start,
55
+ .complete = test_job_complete,
56
+};
57
+
58
+static void test_blockjob_common(enum drain_type drain_type)
76
+{
59
+{
77
+ CancelJob *s = opaque;
60
+ BlockBackend *blk_src, *blk_target;
61
+ BlockDriverState *src, *target;
62
+ BlockJob *job;
63
+ int ret;
78
+
64
+
79
+ while (!s->should_complete) {
65
+ src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR,
80
+ if (block_job_is_cancelled(&s->common)) {
66
+ &error_abort);
81
+ goto defer;
67
+ blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
82
+ }
68
+ blk_insert_bs(blk_src, src, &error_abort);
83
+
69
+
84
+ if (!s->common.ready && s->should_converge) {
70
+ target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR,
85
+ block_job_event_ready(&s->common);
71
+ &error_abort);
86
+ }
72
+ blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
73
+ blk_insert_bs(blk_target, target, &error_abort);
87
+
74
+
88
+ block_job_sleep_ns(&s->common, 100000);
75
+ job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
76
+ 0, NULL, NULL, &error_abort);
77
+ block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
78
+ block_job_start(job);
79
+
80
+ g_assert_cmpint(job->pause_count, ==, 0);
81
+ g_assert_false(job->paused);
82
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
83
+
84
+ do_drain_begin(drain_type, src);
85
+
86
+ if (drain_type == BDRV_DRAIN_ALL) {
87
+ /* bdrv_drain_all() drains both src and target, and involves an
88
+ * additional block_job_pause_all() */
89
+ g_assert_cmpint(job->pause_count, ==, 3);
90
+ } else {
91
+ g_assert_cmpint(job->pause_count, ==, 1);
89
+ }
92
+ }
93
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
94
+ /* g_assert_true(job->paused); */
95
+ g_assert_false(job->busy); /* The job is paused */
90
+
96
+
91
+ defer:
97
+ do_drain_end(drain_type, src);
92
+ block_job_defer_to_main_loop(&s->common, cancel_job_completed, s);
98
+
99
+ g_assert_cmpint(job->pause_count, ==, 0);
100
+ g_assert_false(job->paused);
101
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
102
+
103
+ do_drain_begin(drain_type, target);
104
+
105
+ if (drain_type == BDRV_DRAIN_ALL) {
106
+ /* bdrv_drain_all() drains both src and target, and involves an
107
+ * additional block_job_pause_all() */
108
+ g_assert_cmpint(job->pause_count, ==, 3);
109
+ } else {
110
+ g_assert_cmpint(job->pause_count, ==, 1);
111
+ }
112
+ /* XXX We don't wait until the job is actually paused. Is this okay? */
113
+ /* g_assert_true(job->paused); */
114
+ g_assert_false(job->busy); /* The job is paused */
115
+
116
+ do_drain_end(drain_type, target);
117
+
118
+ g_assert_cmpint(job->pause_count, ==, 0);
119
+ g_assert_false(job->paused);
120
+ g_assert_false(job->busy); /* We're in block_job_sleep_ns() */
121
+
122
+ ret = block_job_complete_sync(job, &error_abort);
123
+ g_assert_cmpint(ret, ==, 0);
124
+
125
+ blk_unref(blk_src);
126
+ blk_unref(blk_target);
127
+ bdrv_unref(src);
128
+ bdrv_unref(target);
93
+}
129
+}
94
+
130
+
95
+static const BlockJobDriver test_cancel_driver = {
131
+static void test_blockjob_drain_all(void)
96
+ .instance_size = sizeof(CancelJob),
97
+ .start = cancel_job_start,
98
+ .complete = cancel_job_complete,
99
+};
100
+
101
+static CancelJob *create_common(BlockJob **pjob)
102
+{
132
+{
103
+ BlockBackend *blk;
133
+ test_blockjob_common(BDRV_DRAIN_ALL);
104
+ BlockJob *job;
105
+ CancelJob *s;
106
+
107
+ blk = create_blk(NULL);
108
+ job = mk_job(blk, "Steve", &test_cancel_driver, true,
109
+ BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
110
+ block_job_ref(job);
111
+ assert(job->status == BLOCK_JOB_STATUS_CREATED);
112
+ s = container_of(job, CancelJob, common);
113
+ s->blk = blk;
114
+
115
+ *pjob = job;
116
+ return s;
117
+}
134
+}
118
+
135
+
119
+static void cancel_common(CancelJob *s)
136
+static void test_blockjob_drain(void)
120
+{
137
+{
121
+ BlockJob *job = &s->common;
138
+ test_blockjob_common(BDRV_DRAIN);
122
+ BlockBackend *blk = s->blk;
123
+ BlockJobStatus sts = job->status;
124
+
125
+ block_job_cancel_sync(job);
126
+ if ((sts != BLOCK_JOB_STATUS_CREATED) &&
127
+ (sts != BLOCK_JOB_STATUS_CONCLUDED)) {
128
+ BlockJob *dummy = job;
129
+ block_job_dismiss(&dummy, &error_abort);
130
+ }
131
+ assert(job->status == BLOCK_JOB_STATUS_NULL);
132
+ block_job_unref(job);
133
+ destroy_blk(blk);
134
+}
135
+
136
+static void test_cancel_created(void)
137
+{
138
+ BlockJob *job;
139
+ CancelJob *s;
140
+
141
+ s = create_common(&job);
142
+ cancel_common(s);
143
+}
144
+
145
+static void test_cancel_running(void)
146
+{
147
+ BlockJob *job;
148
+ CancelJob *s;
149
+
150
+ s = create_common(&job);
151
+
152
+ block_job_start(job);
153
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
154
+
155
+ cancel_common(s);
156
+}
157
+
158
+static void test_cancel_paused(void)
159
+{
160
+ BlockJob *job;
161
+ CancelJob *s;
162
+
163
+ s = create_common(&job);
164
+
165
+ block_job_start(job);
166
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
167
+
168
+ block_job_user_pause(job, &error_abort);
169
+ block_job_enter(job);
170
+ assert(job->status == BLOCK_JOB_STATUS_PAUSED);
171
+
172
+ cancel_common(s);
173
+}
174
+
175
+static void test_cancel_ready(void)
176
+{
177
+ BlockJob *job;
178
+ CancelJob *s;
179
+
180
+ s = create_common(&job);
181
+
182
+ block_job_start(job);
183
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
184
+
185
+ s->should_converge = true;
186
+ block_job_enter(job);
187
+ assert(job->status == BLOCK_JOB_STATUS_READY);
188
+
189
+ cancel_common(s);
190
+}
191
+
192
+static void test_cancel_standby(void)
193
+{
194
+ BlockJob *job;
195
+ CancelJob *s;
196
+
197
+ s = create_common(&job);
198
+
199
+ block_job_start(job);
200
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
201
+
202
+ s->should_converge = true;
203
+ block_job_enter(job);
204
+ assert(job->status == BLOCK_JOB_STATUS_READY);
205
+
206
+ block_job_user_pause(job, &error_abort);
207
+ block_job_enter(job);
208
+ assert(job->status == BLOCK_JOB_STATUS_STANDBY);
209
+
210
+ cancel_common(s);
211
+}
212
+
213
+static void test_cancel_pending(void)
214
+{
215
+ BlockJob *job;
216
+ CancelJob *s;
217
+
218
+ s = create_common(&job);
219
+
220
+ block_job_start(job);
221
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
222
+
223
+ s->should_converge = true;
224
+ block_job_enter(job);
225
+ assert(job->status == BLOCK_JOB_STATUS_READY);
226
+
227
+ block_job_complete(job, &error_abort);
228
+ block_job_enter(job);
229
+ while (!s->completed) {
230
+ aio_poll(qemu_get_aio_context(), true);
231
+ }
232
+ assert(job->status == BLOCK_JOB_STATUS_PENDING);
233
+
234
+ cancel_common(s);
235
+}
236
+
237
+static void test_cancel_concluded(void)
238
+{
239
+ BlockJob *job;
240
+ CancelJob *s;
241
+
242
+ s = create_common(&job);
243
+
244
+ block_job_start(job);
245
+ assert(job->status == BLOCK_JOB_STATUS_RUNNING);
246
+
247
+ s->should_converge = true;
248
+ block_job_enter(job);
249
+ assert(job->status == BLOCK_JOB_STATUS_READY);
250
+
251
+ block_job_complete(job, &error_abort);
252
+ block_job_enter(job);
253
+ while (!s->completed) {
254
+ aio_poll(qemu_get_aio_context(), true);
255
+ }
256
+ assert(job->status == BLOCK_JOB_STATUS_PENDING);
257
+
258
+ block_job_finalize(job, &error_abort);
259
+ assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
260
+
261
+ cancel_common(s);
262
+}
139
+}
263
+
140
+
264
int main(int argc, char **argv)
141
int main(int argc, char **argv)
265
{
142
{
266
qemu_init_main_loop(&error_abort);
143
bdrv_init();
267
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
144
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
268
145
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
269
g_test_init(&argc, &argv, NULL);
146
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
270
g_test_add_func("/blockjob/ids", test_job_ids);
147
271
+ g_test_add_func("/blockjob/cancel/created", test_cancel_created);
148
+ g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
272
+ g_test_add_func("/blockjob/cancel/running", test_cancel_running);
149
+ g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
273
+ g_test_add_func("/blockjob/cancel/paused", test_cancel_paused);
150
+
274
+ g_test_add_func("/blockjob/cancel/ready", test_cancel_ready);
275
+ g_test_add_func("/blockjob/cancel/standby", test_cancel_standby);
276
+ g_test_add_func("/blockjob/cancel/pending", test_cancel_pending);
277
+ g_test_add_func("/blockjob/cancel/concluded", test_cancel_concluded);
278
return g_test_run();
151
return g_test_run();
279
}
152
}
280
--
153
--
281
2.13.6
154
2.13.6
282
155
283
156
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
Block jobs are already paused using the BdrvChildRole drain callbacks,
2
so we don't need an additional block_job_pause_all() call.
2
3
3
model all independent jobs as single job transactions.
4
5
It's one less case we have to worry about when we add more states to the
6
transition machine. This way, we can just treat all job lifetimes exactly
7
the same. This helps tighten assertions of the STM graph and removes some
8
conditionals that would have been needed in the coming commits adding a
9
more explicit job lifetime management API.
10
11
Signed-off-by: John Snow <jsnow@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
14
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
15
---
5
---
16
include/block/blockjob.h | 1 -
6
block/io.c | 4 ----
17
include/block/blockjob_int.h | 3 ++-
7
tests/test-bdrv-drain.c | 10 ++++------
18
block/backup.c | 3 +--
8
2 files changed, 4 insertions(+), 10 deletions(-)
19
block/commit.c | 2 +-
20
block/mirror.c | 2 +-
21
block/stream.c | 2 +-
22
blockjob.c | 25 ++++++++++++++++---------
23
tests/test-bdrv-drain.c | 4 ++--
24
tests/test-blockjob-txn.c | 19 +++++++------------
25
tests/test-blockjob.c | 2 +-
26
10 files changed, 32 insertions(+), 31 deletions(-)
27
9
28
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
10
diff --git a/block/io.c b/block/io.c
29
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
30
--- a/include/block/blockjob.h
12
--- a/block/io.c
31
+++ b/include/block/blockjob.h
13
+++ b/block/io.c
32
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
14
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
33
*/
15
* context. */
34
QEMUTimer sleep_timer;
16
assert(qemu_get_current_aio_context() == qemu_get_aio_context());
35
17
36
- /** Non-NULL if this job is part of a transaction */
18
- block_job_pause_all();
37
BlockJobTxn *txn;
19
-
38
QLIST_ENTRY(BlockJob) txn_list;
20
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
39
} BlockJob;
21
AioContext *aio_context = bdrv_get_aio_context(bs);
40
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
22
41
index XXXXXXX..XXXXXXX 100644
23
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
42
--- a/include/block/blockjob_int.h
24
aio_enable_external(aio_context);
43
+++ b/include/block/blockjob_int.h
25
aio_context_release(aio_context);
44
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
45
* @job_id: The id of the newly-created job, or %NULL to have one
46
* generated automatically.
47
* @job_type: The class object for the newly-created job.
48
+ * @txn: The transaction this job belongs to, if any. %NULL otherwise.
49
* @bs: The block
50
* @perm, @shared_perm: Permissions to request for @bs
51
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
52
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
53
* called from a wrapper that is specific to the job type.
54
*/
55
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
56
- BlockDriverState *bs, uint64_t perm,
57
+ BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
58
uint64_t shared_perm, int64_t speed, int flags,
59
BlockCompletionFunc *cb, void *opaque, Error **errp);
60
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 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
66
}
26
}
67
27
-
68
/* job->common.len is fixed, so we can't allow resize */
28
- block_job_resume_all();
69
- job = block_job_create(job_id, &backup_job_driver, bs,
70
+ job = block_job_create(job_id, &backup_job_driver, txn, bs,
71
BLK_PERM_CONSISTENT_READ,
72
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE |
73
BLK_PERM_WRITE_UNCHANGED | BLK_PERM_GRAPH_MOD,
74
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
75
block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL,
76
&error_abort);
77
job->common.len = len;
78
- block_job_txn_add_job(txn, &job->common);
79
80
return &job->common;
81
82
diff --git a/block/commit.c b/block/commit.c
83
index XXXXXXX..XXXXXXX 100644
84
--- a/block/commit.c
85
+++ b/block/commit.c
86
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
87
return;
88
}
89
90
- s = block_job_create(job_id, &commit_job_driver, bs, 0, BLK_PERM_ALL,
91
+ s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL,
92
speed, BLOCK_JOB_DEFAULT, NULL, NULL, errp);
93
if (!s) {
94
return;
95
diff --git a/block/mirror.c b/block/mirror.c
96
index XXXXXXX..XXXXXXX 100644
97
--- a/block/mirror.c
98
+++ b/block/mirror.c
99
@@ -XXX,XX +XXX,XX @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs,
100
}
101
102
/* Make sure that the source is not resized while the job is running */
103
- s = block_job_create(job_id, driver, mirror_top_bs,
104
+ s = block_job_create(job_id, driver, NULL, mirror_top_bs,
105
BLK_PERM_CONSISTENT_READ,
106
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
107
BLK_PERM_WRITE | BLK_PERM_GRAPH_MOD, speed,
108
diff --git a/block/stream.c b/block/stream.c
109
index XXXXXXX..XXXXXXX 100644
110
--- a/block/stream.c
111
+++ b/block/stream.c
112
@@ -XXX,XX +XXX,XX @@ void stream_start(const char *job_id, BlockDriverState *bs,
113
/* Prevent concurrent jobs trying to modify the graph structure here, we
114
* already have our own plans. Also don't allow resize as the image size is
115
* queried only at the job start and then cached. */
116
- s = block_job_create(job_id, &stream_job_driver, bs,
117
+ s = block_job_create(job_id, &stream_job_driver, NULL, bs,
118
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
119
BLK_PERM_GRAPH_MOD,
120
BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED |
121
diff --git a/blockjob.c b/blockjob.c
122
index XXXXXXX..XXXXXXX 100644
123
--- a/blockjob.c
124
+++ b/blockjob.c
125
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
126
}
127
}
128
129
- if (job->txn) {
130
- QLIST_REMOVE(job, txn_list);
131
- block_job_txn_unref(job->txn);
132
- }
133
+ QLIST_REMOVE(job, txn_list);
134
+ block_job_txn_unref(job->txn);
135
block_job_unref(job);
136
}
29
}
137
30
138
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
31
void bdrv_drain_all(void)
139
*/
140
141
void *block_job_create(const char *job_id, const BlockJobDriver *driver,
142
- BlockDriverState *bs, uint64_t perm,
143
+ BlockJobTxn *txn, BlockDriverState *bs, uint64_t perm,
144
uint64_t shared_perm, int64_t speed, int flags,
145
BlockCompletionFunc *cb, void *opaque, Error **errp)
146
{
147
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
148
return NULL;
149
}
150
}
151
+
152
+ /* Single jobs are modeled as single-job transactions for sake of
153
+ * consolidating the job management logic */
154
+ if (!txn) {
155
+ txn = block_job_txn_new();
156
+ block_job_txn_add_job(txn, job);
157
+ block_job_txn_unref(txn);
158
+ } else {
159
+ block_job_txn_add_job(txn, job);
160
+ }
161
+
162
return job;
163
}
164
165
@@ -XXX,XX +XXX,XX @@ void block_job_early_fail(BlockJob *job)
166
167
void block_job_completed(BlockJob *job, int ret)
168
{
169
+ assert(job && job->txn && !job->completed);
170
assert(blk_bs(job->blk)->job == job);
171
- assert(!job->completed);
172
job->completed = true;
173
job->ret = ret;
174
- if (!job->txn) {
175
- block_job_completed_single(job);
176
- } else if (ret < 0 || block_job_is_cancelled(job)) {
177
+ if (ret < 0 || block_job_is_cancelled(job)) {
178
block_job_completed_txn_abort(job);
179
} else {
180
block_job_completed_txn_success(job);
181
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
32
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
182
index XXXXXXX..XXXXXXX 100644
33
index XXXXXXX..XXXXXXX 100644
183
--- a/tests/test-bdrv-drain.c
34
--- a/tests/test-bdrv-drain.c
184
+++ b/tests/test-bdrv-drain.c
35
+++ b/tests/test-bdrv-drain.c
185
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
36
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
186
blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
37
do_drain_begin(drain_type, src);
187
blk_insert_bs(blk_target, target, &error_abort);
38
188
39
if (drain_type == BDRV_DRAIN_ALL) {
189
- job = block_job_create("job0", &test_job_driver, src, 0, BLK_PERM_ALL, 0,
40
- /* bdrv_drain_all() drains both src and target, and involves an
190
- 0, NULL, NULL, &error_abort);
41
- * additional block_job_pause_all() */
191
+ job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL,
42
- g_assert_cmpint(job->pause_count, ==, 3);
192
+ 0, 0, NULL, NULL, &error_abort);
43
+ /* bdrv_drain_all() drains both src and target */
193
block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort);
44
+ g_assert_cmpint(job->pause_count, ==, 2);
194
block_job_start(job);
45
} else {
195
46
g_assert_cmpint(job->pause_count, ==, 1);
196
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
47
}
197
index XXXXXXX..XXXXXXX 100644
48
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_common(enum drain_type drain_type)
198
--- a/tests/test-blockjob-txn.c
49
do_drain_begin(drain_type, target);
199
+++ b/tests/test-blockjob-txn.c
50
200
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
51
if (drain_type == BDRV_DRAIN_ALL) {
201
*/
52
- /* bdrv_drain_all() drains both src and target, and involves an
202
static BlockJob *test_block_job_start(unsigned int iterations,
53
- * additional block_job_pause_all() */
203
bool use_timer,
54
- g_assert_cmpint(job->pause_count, ==, 3);
204
- int rc, int *result)
55
+ /* bdrv_drain_all() drains both src and target */
205
+ int rc, int *result, BlockJobTxn *txn)
56
+ g_assert_cmpint(job->pause_count, ==, 2);
206
{
57
} else {
207
BlockDriverState *bs;
58
g_assert_cmpint(job->pause_count, ==, 1);
208
TestBlockJob *s;
59
}
209
@@ -XXX,XX +XXX,XX @@ static BlockJob *test_block_job_start(unsigned int iterations,
210
g_assert_nonnull(bs);
211
212
snprintf(job_id, sizeof(job_id), "job%u", counter++);
213
- s = block_job_create(job_id, &test_block_job_driver, bs,
214
+ s = block_job_create(job_id, &test_block_job_driver, txn, bs,
215
0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT,
216
test_block_job_cb, data, &error_abort);
217
s->iterations = iterations;
218
@@ -XXX,XX +XXX,XX @@ static void test_single_job(int expected)
219
int result = -EINPROGRESS;
220
221
txn = block_job_txn_new();
222
- job = test_block_job_start(1, true, expected, &result);
223
- block_job_txn_add_job(txn, job);
224
+ job = test_block_job_start(1, true, expected, &result, txn);
225
block_job_start(job);
226
227
if (expected == -ECANCELED) {
228
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
229
int result2 = -EINPROGRESS;
230
231
txn = block_job_txn_new();
232
- job1 = test_block_job_start(1, true, expected1, &result1);
233
- block_job_txn_add_job(txn, job1);
234
- job2 = test_block_job_start(2, true, expected2, &result2);
235
- block_job_txn_add_job(txn, job2);
236
+ job1 = test_block_job_start(1, true, expected1, &result1, txn);
237
+ job2 = test_block_job_start(2, true, expected2, &result2, txn);
238
block_job_start(job1);
239
block_job_start(job2);
240
241
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs_fail_cancel_race(void)
242
int result2 = -EINPROGRESS;
243
244
txn = block_job_txn_new();
245
- job1 = test_block_job_start(1, true, -ECANCELED, &result1);
246
- block_job_txn_add_job(txn, job1);
247
- job2 = test_block_job_start(2, false, 0, &result2);
248
- block_job_txn_add_job(txn, job2);
249
+ job1 = test_block_job_start(1, true, -ECANCELED, &result1, txn);
250
+ job2 = test_block_job_start(2, false, 0, &result2, txn);
251
block_job_start(job1);
252
block_job_start(job2);
253
254
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
255
index XXXXXXX..XXXXXXX 100644
256
--- a/tests/test-blockjob.c
257
+++ b/tests/test-blockjob.c
258
@@ -XXX,XX +XXX,XX @@ static BlockJob *do_test_id(BlockBackend *blk, const char *id,
259
BlockJob *job;
260
Error *errp = NULL;
261
262
- job = block_job_create(id, &test_block_job_driver, blk_bs(blk),
263
+ job = block_job_create(id, &test_block_job_driver, NULL, blk_bs(blk),
264
0, BLK_PERM_ALL, 0, BLOCK_JOB_DEFAULT, block_job_cb,
265
NULL, &errp);
266
if (should_succeed) {
267
--
60
--
268
2.13.6
61
2.13.6
269
62
270
63
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Trivial; Document what the job creation flags do,
4
and some general tidying.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
9
include/block/blockjob.h | 8 ++++----
10
include/block/blockjob_int.h | 4 +++-
11
2 files changed, 7 insertions(+), 5 deletions(-)
12
13
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
14
index XXXXXXX..XXXXXXX 100644
15
--- a/include/block/blockjob.h
16
+++ b/include/block/blockjob.h
17
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
18
/** Reference count of the block job */
19
int refcnt;
20
21
- /* True if this job has reported completion by calling block_job_completed.
22
- */
23
+ /** True when job has reported completion by calling block_job_completed. */
24
bool completed;
25
26
- /* ret code passed to block_job_completed.
27
- */
28
+ /** ret code passed to block_job_completed. */
29
int ret;
30
31
/**
32
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
33
} BlockJob;
34
35
typedef enum BlockJobCreateFlags {
36
+ /* Default behavior */
37
BLOCK_JOB_DEFAULT = 0x00,
38
+ /* BlockJob is not QMP-created and should not send QMP events */
39
BLOCK_JOB_INTERNAL = 0x01,
40
} BlockJobCreateFlags;
41
42
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
43
index XXXXXXX..XXXXXXX 100644
44
--- a/include/block/blockjob_int.h
45
+++ b/include/block/blockjob_int.h
46
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
47
* block_job_create:
48
* @job_id: The id of the newly-created job, or %NULL to have one
49
* generated automatically.
50
- * @job_type: The class object for the newly-created job.
51
+ * @driver: The class object for the newly-created job.
52
* @txn: The transaction this job belongs to, if any. %NULL otherwise.
53
* @bs: The block
54
* @perm, @shared_perm: Permissions to request for @bs
55
* @speed: The maximum speed, in bytes per second, or 0 for unlimited.
56
+ * @flags: Creation flags for the Block Job.
57
+ * See @BlockJobCreateFlags
58
* @cb: Completion function for the job.
59
* @opaque: Opaque pointer value passed to @cb.
60
* @errp: Error object.
61
--
62
2.13.6
63
64
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
bdrv_do_drained_begin() restricts the call of parent callbacks and
2
aio_disable_external() to the outermost drain section, but the block
3
driver callbacks are always called. bdrv_do_drained_end() must match
4
this behaviour, otherwise nodes stay drained even if begin/end calls
5
were balanced.
2
6
3
We're about to add several new states, and booleans are becoming
4
unwieldly and difficult to reason about. It would help to have a
5
more explicit bookkeeping of the state of blockjobs. To this end,
6
add a new "status" field and add our existing states in a redundant
7
manner alongside the bools they are replacing:
8
9
UNDEFINED: Placeholder, default state. Not currently visible to QMP
10
unless changes occur in the future to allow creating jobs
11
without starting them via QMP.
12
CREATED: replaces !!job->co && paused && !busy
13
RUNNING: replaces effectively (!paused && busy)
14
PAUSED: Nearly redundant with info->paused, which shows pause_count.
15
This reports the actual status of the job, which almost always
16
matches the paused request status. It differs in that it is
17
strictly only true when the job has actually gone dormant.
18
READY: replaces job->ready.
19
STANDBY: Paused, but job->ready is true.
20
21
New state additions in coming commits will not be quite so redundant:
22
23
WAITING: Waiting on transaction. This job has finished all the work
24
it can until the transaction converges, fails, or is canceled.
25
PENDING: Pending authorization from user. This job has finished all the
26
work it can until the job or transaction is finalized via
27
block_job_finalize. This implies the transaction has converged
28
and left the WAITING phase.
29
ABORTING: Job has encountered an error condition and is in the process
30
of aborting.
31
CONCLUDED: Job has ceased all operations and has a return code available
32
for query and may be dismissed via block_job_dismiss.
33
NULL: Job has been dismissed and (should) be destroyed. Should never
34
be visible to QMP.
35
36
Some of these states appear somewhat superfluous, but it helps define the
37
expected flow of a job; so some of the states wind up being synchronous
38
empty transitions. Importantly, jobs can be in only one of these states
39
at any given time, which helps code and external users alike reason about
40
the current condition of a job unambiguously.
41
42
Signed-off-by: John Snow <jsnow@redhat.com>
43
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
44
---
8
---
45
qapi/block-core.json | 31 ++++++++++++++++++++++++++++++-
9
block/io.c | 12 +++++++-----
46
include/block/blockjob.h | 3 +++
10
1 file changed, 7 insertions(+), 5 deletions(-)
47
blockjob.c | 9 +++++++++
48
tests/qemu-iotests/109.out | 24 ++++++++++++------------
49
4 files changed, 54 insertions(+), 13 deletions(-)
50
11
51
diff --git a/qapi/block-core.json b/qapi/block-core.json
12
diff --git a/block/io.c b/block/io.c
52
index XXXXXXX..XXXXXXX 100644
13
index XXXXXXX..XXXXXXX 100644
53
--- a/qapi/block-core.json
14
--- a/block/io.c
54
+++ b/qapi/block-core.json
15
+++ b/block/io.c
55
@@ -XXX,XX +XXX,XX @@
16
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs)
56
'data': ['commit', 'stream', 'mirror', 'backup'] }
17
57
18
void bdrv_drained_end(BlockDriverState *bs)
58
##
19
{
59
+# @BlockJobStatus:
20
+ int old_quiesce_counter;
60
+#
61
+# Indicates the present state of a given blockjob in its lifetime.
62
+#
63
+# @undefined: Erroneous, default state. Should not ever be visible.
64
+#
65
+# @created: The job has been created, but not yet started.
66
+#
67
+# @running: The job is currently running.
68
+#
69
+# @paused: The job is running, but paused. The pause may be requested by
70
+# either the QMP user or by internal processes.
71
+#
72
+# @ready: The job is running, but is ready for the user to signal completion.
73
+# This is used for long-running jobs like mirror that are designed to
74
+# run indefinitely.
75
+#
76
+# @standby: The job is ready, but paused. This is nearly identical to @paused.
77
+# The job may return to @ready or otherwise be canceled.
78
+#
79
+# Since: 2.12
80
+##
81
+{ 'enum': 'BlockJobStatus',
82
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby'] }
83
+
21
+
84
+##
22
if (qemu_in_coroutine()) {
85
# @BlockJobInfo:
23
bdrv_co_yield_to_drain(bs, false);
86
#
24
return;
87
# Information about a long-running block device operation.
25
}
88
@@ -XXX,XX +XXX,XX @@
26
assert(bs->quiesce_counter > 0);
89
#
27
- if (atomic_fetch_dec(&bs->quiesce_counter) > 1) {
90
# @ready: true if the job may be completed (since 2.2)
28
- return;
91
#
29
- }
92
+# @status: Current job state/status (since 2.12)
30
+ old_quiesce_counter = atomic_fetch_dec(&bs->quiesce_counter);
93
+#
31
94
# Since: 1.1
32
/* Re-enable things in child-to-parent order */
95
##
33
bdrv_drain_invoke(bs, false, false);
96
{ 'struct': 'BlockJobInfo',
34
- bdrv_parent_drained_end(bs);
97
'data': {'type': 'str', 'device': 'str', 'len': 'int',
35
- aio_enable_external(bdrv_get_aio_context(bs));
98
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
36
+ if (old_quiesce_counter == 1) {
99
- 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool'} }
37
+ bdrv_parent_drained_end(bs);
100
+ 'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
38
+ aio_enable_external(bdrv_get_aio_context(bs));
101
+ 'status': 'BlockJobStatus' } }
39
+ }
102
103
##
104
# @query-block-jobs:
105
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
106
index XXXXXXX..XXXXXXX 100644
107
--- a/include/block/blockjob.h
108
+++ b/include/block/blockjob.h
109
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
110
*/
111
QEMUTimer sleep_timer;
112
113
+ /** Current state; See @BlockJobStatus for details. */
114
+ BlockJobStatus status;
115
+
116
BlockJobTxn *txn;
117
QLIST_ENTRY(BlockJob) txn_list;
118
} BlockJob;
119
diff --git a/blockjob.c b/blockjob.c
120
index XXXXXXX..XXXXXXX 100644
121
--- a/blockjob.c
122
+++ b/blockjob.c
123
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
124
job->pause_count--;
125
job->busy = true;
126
job->paused = false;
127
+ job->status = BLOCK_JOB_STATUS_RUNNING;
128
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
129
}
40
}
130
41
131
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
42
/*
132
info->speed = job->speed;
133
info->io_status = job->iostatus;
134
info->ready = job->ready;
135
+ info->status = job->status;
136
return info;
137
}
138
139
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
140
job->paused = true;
141
job->pause_count = 1;
142
job->refcnt = 1;
143
+ job->status = BLOCK_JOB_STATUS_CREATED;
144
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
145
QEMU_CLOCK_REALTIME, SCALE_NS,
146
block_job_sleep_timer_cb, job);
147
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
148
}
149
150
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
151
+ BlockJobStatus status = job->status;
152
+ job->status = status == BLOCK_JOB_STATUS_READY ? \
153
+ BLOCK_JOB_STATUS_STANDBY : \
154
+ BLOCK_JOB_STATUS_PAUSED;
155
job->paused = true;
156
block_job_do_yield(job, -1);
157
job->paused = false;
158
+ job->status = status;
159
}
160
161
if (job->driver->resume) {
162
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
163
164
void block_job_event_ready(BlockJob *job)
165
{
166
+ job->status = BLOCK_JOB_STATUS_READY;
167
job->ready = true;
168
169
if (block_job_is_internal(job)) {
170
diff --git a/tests/qemu-iotests/109.out b/tests/qemu-iotests/109.out
171
index XXXXXXX..XXXXXXX 100644
172
--- a/tests/qemu-iotests/109.out
173
+++ b/tests/qemu-iotests/109.out
174
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
175
{"return": {}}
176
{"return": {}}
177
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
178
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
179
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
180
{"return": {}}
181
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
182
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
183
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
184
{"return": {}}
185
{"return": {}}
186
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
187
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
188
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 197120, "offset": 197120, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
189
{"return": {}}
190
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
191
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 197120, "offset": 197120, "speed": 0, "type": "mirror"}}
192
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
193
{"return": {}}
194
{"return": {}}
195
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
196
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
197
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
198
{"return": {}}
199
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
200
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
201
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
202
{"return": {}}
203
{"return": {}}
204
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
205
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
206
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 1024, "offset": 1024, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
207
{"return": {}}
208
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
209
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 1024, "offset": 1024, "speed": 0, "type": "mirror"}}
210
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
211
{"return": {}}
212
{"return": {}}
213
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
214
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
215
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 65536, "offset": 65536, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
216
{"return": {}}
217
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
218
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 65536, "offset": 65536, "speed": 0, "type": "mirror"}}
219
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
220
{"return": {}}
221
{"return": {}}
222
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
223
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
224
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
225
{"return": {}}
226
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
227
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
228
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
229
{"return": {}}
230
{"return": {}}
231
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
232
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
233
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2560, "offset": 2560, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
234
{"return": {}}
235
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
236
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2560, "offset": 2560, "speed": 0, "type": "mirror"}}
237
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
238
{"return": {}}
239
{"return": {}}
240
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
241
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
242
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 31457280, "offset": 31457280, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
243
{"return": {}}
244
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
245
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 31457280, "offset": 31457280, "speed": 0, "type": "mirror"}}
246
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
247
{"return": {}}
248
{"return": {}}
249
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
250
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
251
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 327680, "offset": 327680, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
252
{"return": {}}
253
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
254
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 327680, "offset": 327680, "speed": 0, "type": "mirror"}}
255
@@ -XXX,XX +XXX,XX @@ read 65536/65536 bytes at offset 0
256
{"return": {}}
257
{"return": {}}
258
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
259
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
260
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 2048, "offset": 2048, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
261
{"return": {}}
262
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
263
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 2048, "offset": 2048, "speed": 0, "type": "mirror"}}
264
@@ -XXX,XX +XXX,XX @@ Automatically detecting the format is dangerous for raw images, write operations
265
Specify the 'raw' format explicitly to remove the restrictions.
266
{"return": {}}
267
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
268
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
269
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
270
{"return": {}}
271
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
272
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
273
@@ -XXX,XX +XXX,XX @@ Images are identical.
274
{"return": {}}
275
{"return": {}}
276
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
277
-{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
278
+{"return": [{"io-status": "ok", "device": "src", "busy": false, "len": 512, "offset": 512, "status": "ready", "paused": false, "speed": 0, "ready": true, "type": "mirror"}]}
279
{"return": {}}
280
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}}
281
{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "src", "len": 512, "offset": 512, "speed": 0, "type": "mirror"}}
282
--
43
--
283
2.13.6
44
2.13.6
284
45
285
46
diff view generated by jsdifflib
1
From: Max Reitz <mreitz@redhat.com>
2
3
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
---
2
---
6
block/vdi.c | 46 ++++++++++++++++++++++++++++------------------
3
tests/test-bdrv-drain.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++
7
1 file changed, 28 insertions(+), 18 deletions(-)
4
1 file changed, 57 insertions(+)
8
5
9
diff --git a/block/vdi.c b/block/vdi.c
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
10
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
11
--- a/block/vdi.c
8
--- a/tests/test-bdrv-drain.c
12
+++ b/block/vdi.c
9
+++ b/tests/test-bdrv-drain.c
13
@@ -XXX,XX +XXX,XX @@ nonallocating_write:
10
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
14
return ret;
11
enum drain_type {
12
BDRV_DRAIN_ALL,
13
BDRV_DRAIN,
14
+ DRAIN_TYPE_MAX,
15
};
16
17
static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
18
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
19
test_quiesce_common(BDRV_DRAIN, false);
15
}
20
}
16
21
17
-static int coroutine_fn vdi_co_do_create(const char *filename,
22
+static void test_nested(void)
18
- QemuOpts *file_opts,
23
+{
19
- BlockdevCreateOptionsVdi *vdi_opts,
24
+ BlockBackend *blk;
20
+static int coroutine_fn vdi_co_do_create(BlockdevCreateOptionsVdi *vdi_opts,
25
+ BlockDriverState *bs, *backing;
21
size_t block_size, Error **errp)
26
+ BDRVTestState *s, *backing_s;
22
{
27
+ enum drain_type outer, inner;
23
int ret = 0;
28
+
24
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(const char *filename,
29
+ blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
25
size_t i;
30
+ bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR,
26
size_t bmap_size;
31
+ &error_abort);
27
int64_t offset = 0;
32
+ s = bs->opaque;
28
- Error *local_err = NULL;
33
+ blk_insert_bs(blk, bs, &error_abort);
29
+ BlockDriverState *bs_file = NULL;
34
+
30
BlockBackend *blk = NULL;
35
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
31
uint32_t *bmap = NULL;
36
+ backing_s = backing->opaque;
32
37
+ bdrv_set_backing_hd(bs, backing, &error_abort);
33
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(const char *filename,
38
+
34
goto exit;
39
+ for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) {
35
}
40
+ for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) {
36
41
+ /* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
37
- ret = bdrv_create_file(filename, file_opts, &local_err);
42
+ int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
38
- if (ret < 0) {
43
+ (inner != BDRV_DRAIN_ALL);
39
- error_propagate(errp, local_err);
44
+ int backing_quiesce = 0;
40
+ bs_file = bdrv_open_blockdev_ref(vdi_opts->file, errp);
45
+ int backing_cb_cnt = (outer != BDRV_DRAIN) +
41
+ if (!bs_file) {
46
+ (inner != BDRV_DRAIN);
42
+ ret = -EIO;
47
+
43
goto exit;
48
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
44
}
49
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
45
50
+ g_assert_cmpint(s->drain_count, ==, 0);
46
- blk = blk_new_open(filename, NULL, NULL,
51
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
47
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
52
+
48
- &local_err);
53
+ do_drain_begin(outer, bs);
49
- if (blk == NULL) {
54
+ do_drain_begin(inner, bs);
50
- error_propagate(errp, local_err);
55
+
51
- ret = -EIO;
56
+ g_assert_cmpint(bs->quiesce_counter, ==, bs_quiesce);
52
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
57
+ g_assert_cmpint(backing->quiesce_counter, ==, backing_quiesce);
53
+ ret = blk_insert_bs(blk, bs_file, errp);
58
+ g_assert_cmpint(s->drain_count, ==, 2);
54
+ if (ret < 0) {
59
+ g_assert_cmpint(backing_s->drain_count, ==, backing_cb_cnt);
55
goto exit;
60
+
56
}
61
+ do_drain_end(inner, bs);
57
62
+ do_drain_end(outer, bs);
58
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(const char *filename,
63
+
59
vdi_header_to_le(&header);
64
+ g_assert_cmpint(bs->quiesce_counter, ==, 0);
60
ret = blk_pwrite(blk, offset, &header, sizeof(header), 0);
65
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
61
if (ret < 0) {
66
+ g_assert_cmpint(s->drain_count, ==, 0);
62
- error_setg(errp, "Error writing header to %s", filename);
67
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
63
+ error_setg(errp, "Error writing header");
68
+ }
64
goto exit;
65
}
66
offset += sizeof(header);
67
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(const char *filename,
68
}
69
ret = blk_pwrite(blk, offset, bmap, bmap_size, 0);
70
if (ret < 0) {
71
- error_setg(errp, "Error writing bmap to %s", filename);
72
+ error_setg(errp, "Error writing bmap");
73
goto exit;
74
}
75
offset += bmap_size;
76
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_do_create(const char *filename,
77
ret = blk_truncate(blk, offset + blocks * block_size,
78
PREALLOC_MODE_OFF, errp);
79
if (ret < 0) {
80
- error_prepend(errp, "Failed to statically allocate %s", filename);
81
+ error_prepend(errp, "Failed to statically allocate file");
82
goto exit;
83
}
84
}
85
86
exit:
87
blk_unref(blk);
88
+ bdrv_unref(bs_file);
89
g_free(bmap);
90
return ret;
91
}
92
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
93
{
94
QDict *qdict = NULL;
95
BlockdevCreateOptionsVdi *create_options = NULL;
96
+ BlockDriverState *bs_file = NULL;
97
uint64_t block_size = DEFAULT_CLUSTER_SIZE;
98
Visitor *v;
99
Error *local_err = NULL;
100
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
101
102
qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vdi_create_opts, true);
103
104
- qdict_put_str(qdict, "file", ""); /* FIXME */
105
+ ret = bdrv_create_file(filename, opts, errp);
106
+ if (ret < 0) {
107
+ goto done;
108
+ }
69
+ }
109
+
70
+
110
+ bs_file = bdrv_open(filename, NULL, NULL,
71
+ bdrv_unref(backing);
111
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
72
+ bdrv_unref(bs);
112
+ if (!bs_file) {
73
+ blk_unref(blk);
113
+ ret = -EIO;
74
+}
114
+ goto done;
115
+ }
116
+
75
+
117
+ qdict_put_str(qdict, "file", bs_file->node_name);
76
118
77
typedef struct TestBlockJob {
119
/* Get the QAPI object */
78
BlockJob common;
120
v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
79
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
121
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vdi_co_create_opts(const char *filename, QemuOpts *opts,
80
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
122
81
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
123
create_options->size = ROUND_UP(create_options->size, BDRV_SECTOR_SIZE);
82
124
83
+ g_test_add_func("/bdrv-drain/nested", test_nested);
125
- ret = vdi_co_do_create(filename, opts, create_options, block_size, errp);
84
+
126
+ ret = vdi_co_do_create(create_options, block_size, errp);
85
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
127
done:
86
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
128
QDECREF(qdict);
129
qapi_free_BlockdevCreateOptionsVdi(create_options);
130
+ bdrv_unref(bs_file);
131
return ret;
132
}
133
87
134
--
88
--
135
2.13.6
89
2.13.6
136
90
137
91
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
This is in preparation for subtree drains, i.e. drained sections that
2
2
affect not only a single node, but recursively all child nodes, too.
3
The state transition table has mostly been implied. We're about to make
3
4
it a bit more complex, so let's make the STM explicit instead.
4
Calling the parent callbacks for drain is pointless when we just came
5
5
from that parent node recursively and leads to multiple increases of
6
Perform state transitions with a function that for now just asserts the
6
bs->quiesce_counter in a single drain call. Don't do it.
7
transition is appropriate.
7
8
8
In order for this to work correctly, the parent callback must be called
9
Transitions:
9
for every bdrv_drain_begin/end() call, not only for the outermost one:
10
Undefined -> Created: During job initialization.
10
11
Created -> Running: Once the job is started.
11
If we have a node N with two parents A and B, recursive draining of A
12
Jobs cannot transition from "Created" to "Paused"
12
should cause the quiesce_counter of B to increase because its child N is
13
directly, but will instead synchronously transition
13
drained independently of B. If now B is recursively drained, too, A must
14
to running to paused immediately.
14
increase its quiesce_counter because N is drained independently of A
15
Running -> Paused: Normal workflow for pauses.
15
only now, even if N is going from quiesce_counter 1 to 2.
16
Running -> Ready: Normal workflow for jobs reaching their sync point.
16
17
(e.g. mirror)
18
Ready -> Standby: Normal workflow for pausing ready jobs.
19
Paused -> Running: Normal resume.
20
Standby -> Ready: Resume of a Standby job.
21
22
+---------+
23
|UNDEFINED|
24
+--+------+
25
|
26
+--v----+
27
|CREATED|
28
+--+----+
29
|
30
+--v----+ +------+
31
|RUNNING<----->PAUSED|
32
+--+----+ +------+
33
|
34
+--v--+ +-------+
35
|READY<------->STANDBY|
36
+-----+ +-------+
37
38
Notably, there is no state presently defined as of this commit that
39
deals with a job after the "running" or "ready" states, so this table
40
will be adjusted alongside the commits that introduce those states.
41
42
Signed-off-by: John Snow <jsnow@redhat.com>
43
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
44
---
18
---
45
blockjob.c | 40 +++++++++++++++++++++++++++++++++-------
19
include/block/block.h | 4 ++--
46
block/trace-events | 3 +++
20
block.c | 13 +++++++++----
47
2 files changed, 36 insertions(+), 7 deletions(-)
21
block/io.c | 47 ++++++++++++++++++++++++++++++++++-------------
48
22
3 files changed, 45 insertions(+), 19 deletions(-)
49
diff --git a/blockjob.c b/blockjob.c
23
24
diff --git a/include/block/block.h b/include/block/block.h
50
index XXXXXXX..XXXXXXX 100644
25
index XXXXXXX..XXXXXXX 100644
51
--- a/blockjob.c
26
--- a/include/block/block.h
52
+++ b/blockjob.c
27
+++ b/include/block/block.h
28
@@ -XXX,XX +XXX,XX @@ void bdrv_io_unplug(BlockDriverState *bs);
29
* Begin a quiesced section of all users of @bs. This is part of
30
* bdrv_drained_begin.
31
*/
32
-void bdrv_parent_drained_begin(BlockDriverState *bs);
33
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore);
34
35
/**
36
* bdrv_parent_drained_end:
37
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_begin(BlockDriverState *bs);
38
* End a quiesced section of all users of @bs. This is part of
39
* bdrv_drained_end.
40
*/
41
-void bdrv_parent_drained_end(BlockDriverState *bs);
42
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
43
44
/**
45
* bdrv_drained_begin:
46
diff --git a/block.c b/block.c
47
index XXXXXXX..XXXXXXX 100644
48
--- a/block.c
49
+++ b/block.c
50
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
51
BlockDriverState *new_bs)
52
{
53
BlockDriverState *old_bs = child->bs;
54
+ int i;
55
56
if (old_bs && new_bs) {
57
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
58
}
59
if (old_bs) {
60
if (old_bs->quiesce_counter && child->role->drained_end) {
61
- child->role->drained_end(child);
62
+ for (i = 0; i < old_bs->quiesce_counter; i++) {
63
+ child->role->drained_end(child);
64
+ }
65
}
66
if (child->role->detach) {
67
child->role->detach(child);
68
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
69
if (new_bs) {
70
QLIST_INSERT_HEAD(&new_bs->parents, child, next_parent);
71
if (new_bs->quiesce_counter && child->role->drained_begin) {
72
- child->role->drained_begin(child);
73
+ for (i = 0; i < new_bs->quiesce_counter; i++) {
74
+ child->role->drained_begin(child);
75
+ }
76
}
77
78
if (child->role->attach) {
79
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
80
AioContext *ctx = bdrv_get_aio_context(bs);
81
82
aio_disable_external(ctx);
83
- bdrv_parent_drained_begin(bs);
84
+ bdrv_parent_drained_begin(bs, NULL);
85
bdrv_drain(bs); /* ensure there are no in-flight requests */
86
87
while (aio_poll(ctx, false)) {
88
@@ -XXX,XX +XXX,XX @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context)
89
*/
90
aio_context_acquire(new_context);
91
bdrv_attach_aio_context(bs, new_context);
92
- bdrv_parent_drained_end(bs);
93
+ bdrv_parent_drained_end(bs, NULL);
94
aio_enable_external(ctx);
95
aio_context_release(new_context);
96
}
97
diff --git a/block/io.c b/block/io.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/block/io.c
100
+++ b/block/io.c
53
@@ -XXX,XX +XXX,XX @@
101
@@ -XXX,XX +XXX,XX @@
54
#include "block/block.h"
102
static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs,
55
#include "block/blockjob_int.h"
103
int64_t offset, int bytes, BdrvRequestFlags flags);
56
#include "block/block_int.h"
104
57
+#include "block/trace.h"
105
-void bdrv_parent_drained_begin(BlockDriverState *bs)
58
#include "sysemu/block-backend.h"
106
+void bdrv_parent_drained_begin(BlockDriverState *bs, BdrvChild *ignore)
59
#include "qapi/error.h"
107
{
60
#include "qapi/qapi-events-block-core.h"
108
BdrvChild *c, *next;
61
@@ -XXX,XX +XXX,XX @@
109
62
* block_job_enter. */
110
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
63
static QemuMutex block_job_mutex;
111
+ if (c == ignore) {
64
112
+ continue;
65
+/* BlockJob State Transition Table */
113
+ }
66
+bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
114
if (c->role->drained_begin) {
67
+ /* U, C, R, P, Y, S */
115
c->role->drained_begin(c);
68
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0},
116
}
69
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0},
117
}
70
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0},
118
}
71
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0},
119
72
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1},
120
-void bdrv_parent_drained_end(BlockDriverState *bs)
73
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0},
121
+void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore)
74
+};
122
{
123
BdrvChild *c, *next;
124
125
QLIST_FOREACH_SAFE(c, &bs->parents, next_parent, next) {
126
+ if (c == ignore) {
127
+ continue;
128
+ }
129
if (c->role->drained_end) {
130
c->role->drained_end(c);
131
}
132
@@ -XXX,XX +XXX,XX @@ typedef struct {
133
BlockDriverState *bs;
134
bool done;
135
bool begin;
136
+ BdrvChild *parent;
137
} BdrvCoDrainData;
138
139
static void coroutine_fn bdrv_drain_invoke_entry(void *opaque)
140
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
141
return waited;
142
}
143
144
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
145
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
75
+
146
+
76
+static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
147
static void bdrv_co_drain_bh_cb(void *opaque)
148
{
149
BdrvCoDrainData *data = opaque;
150
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
151
152
bdrv_dec_in_flight(bs);
153
if (data->begin) {
154
- bdrv_drained_begin(bs);
155
+ bdrv_do_drained_begin(bs, data->parent);
156
} else {
157
- bdrv_drained_end(bs);
158
+ bdrv_do_drained_end(bs, data->parent);
159
}
160
161
data->done = true;
162
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
163
}
164
165
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
166
- bool begin)
167
+ bool begin, BdrvChild *parent)
168
{
169
BdrvCoDrainData data;
170
171
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
172
.bs = bs,
173
.done = false,
174
.begin = begin,
175
+ .parent = parent,
176
};
177
bdrv_inc_in_flight(bs);
178
aio_bh_schedule_oneshot(bdrv_get_aio_context(bs),
179
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
180
assert(data.done);
181
}
182
183
-void bdrv_drained_begin(BlockDriverState *bs)
184
+static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
185
{
186
if (qemu_in_coroutine()) {
187
- bdrv_co_yield_to_drain(bs, true);
188
+ bdrv_co_yield_to_drain(bs, true, parent);
189
return;
190
}
191
192
/* Stop things in parent-to-child order */
193
if (atomic_fetch_inc(&bs->quiesce_counter) == 0) {
194
aio_disable_external(bdrv_get_aio_context(bs));
195
- bdrv_parent_drained_begin(bs);
196
}
197
198
+ bdrv_parent_drained_begin(bs, parent);
199
bdrv_drain_invoke(bs, true, false);
200
bdrv_drain_recurse(bs);
201
}
202
203
-void bdrv_drained_end(BlockDriverState *bs)
204
+void bdrv_drained_begin(BlockDriverState *bs)
77
+{
205
+{
78
+ BlockJobStatus s0 = job->status;
206
+ bdrv_do_drained_begin(bs, NULL);
79
+ assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
80
+ trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
81
+ "allowed" : "disallowed",
82
+ qapi_enum_lookup(&BlockJobStatus_lookup,
83
+ s0),
84
+ qapi_enum_lookup(&BlockJobStatus_lookup,
85
+ s1));
86
+ assert(BlockJobSTT[s0][s1]);
87
+ job->status = s1;
88
+}
207
+}
89
+
208
+
90
static void block_job_lock(void)
209
+static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
91
{
210
{
92
qemu_mutex_lock(&block_job_mutex);
211
int old_quiesce_counter;
93
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
212
94
job->pause_count--;
213
if (qemu_in_coroutine()) {
95
job->busy = true;
214
- bdrv_co_yield_to_drain(bs, false);
96
job->paused = false;
215
+ bdrv_co_yield_to_drain(bs, false, parent);
97
- job->status = BLOCK_JOB_STATUS_RUNNING;
216
return;
98
+ block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
217
}
99
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
218
assert(bs->quiesce_counter > 0);
100
}
219
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_end(BlockDriverState *bs)
101
220
102
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
221
/* Re-enable things in child-to-parent order */
103
job->paused = true;
222
bdrv_drain_invoke(bs, false, false);
104
job->pause_count = 1;
223
+ bdrv_parent_drained_end(bs, parent);
105
job->refcnt = 1;
224
if (old_quiesce_counter == 1) {
106
- job->status = BLOCK_JOB_STATUS_CREATED;
225
- bdrv_parent_drained_end(bs);
107
+ block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
226
aio_enable_external(bdrv_get_aio_context(bs));
108
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
227
}
109
QEMU_CLOCK_REALTIME, SCALE_NS,
228
}
110
block_job_sleep_timer_cb, job);
229
111
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
230
+void bdrv_drained_end(BlockDriverState *bs)
112
231
+{
113
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
232
+ bdrv_do_drained_end(bs, NULL);
114
BlockJobStatus status = job->status;
233
+}
115
- job->status = status == BLOCK_JOB_STATUS_READY ? \
116
- BLOCK_JOB_STATUS_STANDBY : \
117
- BLOCK_JOB_STATUS_PAUSED;
118
+ block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
119
+ BLOCK_JOB_STATUS_STANDBY : \
120
+ BLOCK_JOB_STATUS_PAUSED);
121
job->paused = true;
122
block_job_do_yield(job, -1);
123
job->paused = false;
124
- job->status = status;
125
+ block_job_state_transition(job, status);
126
}
127
128
if (job->driver->resume) {
129
@@ -XXX,XX +XXX,XX @@ void block_job_iostatus_reset(BlockJob *job)
130
131
void block_job_event_ready(BlockJob *job)
132
{
133
- job->status = BLOCK_JOB_STATUS_READY;
134
+ block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
135
job->ready = true;
136
137
if (block_job_is_internal(job)) {
138
diff --git a/block/trace-events b/block/trace-events
139
index XXXXXXX..XXXXXXX 100644
140
--- a/block/trace-events
141
+++ b/block/trace-events
142
@@ -XXX,XX +XXX,XX @@
143
bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\""
144
bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
145
146
+# blockjob.c
147
+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)"
148
+
234
+
149
# block/block-backend.c
235
/*
150
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"
236
* Wait for pending requests to complete on a single BlockDriverState subtree,
151
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"
237
* and suspend block driver's internal I/O until next request arrives.
238
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
239
/* Stop things in parent-to-child order */
240
aio_context_acquire(aio_context);
241
aio_disable_external(aio_context);
242
- bdrv_parent_drained_begin(bs);
243
+ bdrv_parent_drained_begin(bs, NULL);
244
bdrv_drain_invoke(bs, true, true);
245
aio_context_release(aio_context);
246
247
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
248
/* Re-enable things in child-to-parent order */
249
aio_context_acquire(aio_context);
250
bdrv_drain_invoke(bs, false, true);
251
- bdrv_parent_drained_end(bs);
252
+ bdrv_parent_drained_end(bs, NULL);
253
aio_enable_external(aio_context);
254
aio_context_release(aio_context);
255
}
152
--
256
--
153
2.13.6
257
2.13.6
154
258
155
259
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Split out the pause command into the actual pause and the wait.
4
Not every usage presently needs to resubmit a pause request.
5
6
The intent with the next commit will be to explicitly disallow
7
redundant or meaningless pause/resume requests, so the tests
8
need to become more judicious to reflect that.
9
10
Signed-off-by: John Snow <jsnow@redhat.com>
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
12
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
14
---
15
tests/qemu-iotests/030 | 6 ++----
16
tests/qemu-iotests/055 | 17 ++++++-----------
17
tests/qemu-iotests/iotests.py | 12 ++++++++----
18
3 files changed, 16 insertions(+), 19 deletions(-)
19
20
diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030
21
index XXXXXXX..XXXXXXX 100755
22
--- a/tests/qemu-iotests/030
23
+++ b/tests/qemu-iotests/030
24
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
25
result = self.vm.qmp('block-stream', device='drive0')
26
self.assert_qmp(result, 'return', {})
27
28
- result = self.vm.qmp('block-job-pause', device='drive0')
29
- self.assert_qmp(result, 'return', {})
30
-
31
+ self.pause_job('drive0', wait=False)
32
self.vm.resume_drive('drive0')
33
- self.pause_job('drive0')
34
+ self.pause_wait('drive0')
35
36
result = self.vm.qmp('query-block-jobs')
37
offset = self.dictpath(result, 'return[0]/offset')
38
diff --git a/tests/qemu-iotests/055 b/tests/qemu-iotests/055
39
index XXXXXXX..XXXXXXX 100755
40
--- a/tests/qemu-iotests/055
41
+++ b/tests/qemu-iotests/055
42
@@ -XXX,XX +XXX,XX @@ class TestSingleDrive(iotests.QMPTestCase):
43
target=target, sync='full')
44
self.assert_qmp(result, 'return', {})
45
46
- result = self.vm.qmp('block-job-pause', device='drive0')
47
- self.assert_qmp(result, 'return', {})
48
-
49
+ self.pause_job('drive0', wait=False)
50
self.vm.resume_drive('drive0')
51
- self.pause_job('drive0')
52
+ self.pause_wait('drive0')
53
54
result = self.vm.qmp('query-block-jobs')
55
offset = self.dictpath(result, 'return[0]/offset')
56
@@ -XXX,XX +XXX,XX @@ class TestSingleTransaction(iotests.QMPTestCase):
57
])
58
self.assert_qmp(result, 'return', {})
59
60
- result = self.vm.qmp('block-job-pause', device='drive0')
61
- self.assert_qmp(result, 'return', {})
62
+ self.pause_job('drive0', wait=False)
63
64
result = self.vm.qmp('block-job-set-speed', device='drive0', speed=0)
65
self.assert_qmp(result, 'return', {})
66
67
- self.pause_job('drive0')
68
+ self.pause_wait('drive0')
69
70
result = self.vm.qmp('query-block-jobs')
71
offset = self.dictpath(result, 'return[0]/offset')
72
@@ -XXX,XX +XXX,XX @@ class TestDriveCompression(iotests.QMPTestCase):
73
result = self.vm.qmp(cmd, device='drive0', sync='full', compress=True, **args)
74
self.assert_qmp(result, 'return', {})
75
76
- result = self.vm.qmp('block-job-pause', device='drive0')
77
- self.assert_qmp(result, 'return', {})
78
-
79
+ self.pause_job('drive0', wait=False)
80
self.vm.resume_drive('drive0')
81
- self.pause_job('drive0')
82
+ self.pause_wait('drive0')
83
84
result = self.vm.qmp('query-block-jobs')
85
offset = self.dictpath(result, 'return[0]/offset')
86
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
87
index XXXXXXX..XXXXXXX 100644
88
--- a/tests/qemu-iotests/iotests.py
89
+++ b/tests/qemu-iotests/iotests.py
90
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
91
event = self.wait_until_completed(drive=drive)
92
self.assert_qmp(event, 'data/type', 'mirror')
93
94
- def pause_job(self, job_id='job0'):
95
- result = self.vm.qmp('block-job-pause', device=job_id)
96
- self.assert_qmp(result, 'return', {})
97
-
98
+ def pause_wait(self, job_id='job0'):
99
with Timeout(1, "Timeout waiting for job to pause"):
100
while True:
101
result = self.vm.qmp('query-block-jobs')
102
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
103
if job['device'] == job_id and job['paused'] == True and job['busy'] == False:
104
return job
105
106
+ def pause_job(self, job_id='job0', wait=True):
107
+ result = self.vm.qmp('block-job-pause', device=job_id)
108
+ self.assert_qmp(result, 'return', {})
109
+ if wait:
110
+ return self.pause_wait(job_id)
111
+ return result
112
+
113
114
def notrun(reason):
115
'''Skip this test suite'''
116
--
117
2.13.6
118
119
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
bdrv_drained_begin() waits for the completion of requests in the whole
2
subtree, but it only actually keeps its immediate bs parameter quiesced
3
until bdrv_drained_end().
2
4
3
Which commands ("verbs") are appropriate for jobs in which state is
5
Add a version that keeps the whole subtree drained. As of this commit,
4
also somewhat burdensome to keep track of.
6
graph changes cannot be allowed during a subtree drained section, but
7
this will be fixed soon.
5
8
6
As of this commit, it looks rather useless, but begins to look more
7
interesting the more states we add to the STM table.
8
9
A recurring theme is that no verb will apply to an 'undefined' job.
10
11
Further, it's not presently possible to restrict the "pause" or "resume"
12
verbs any more than they are in this commit because of the asynchronous
13
nature of how jobs enter the PAUSED state; justifications for some
14
seemingly erroneous applications are given below.
15
16
=====
17
Verbs
18
=====
19
20
Cancel: Any state except undefined.
21
Pause: Any state except undefined;
22
'created': Requests that the job pauses as it starts.
23
'running': Normal usage. (PAUSED)
24
'paused': The job may be paused for internal reasons,
25
but the user may wish to force an indefinite
26
user-pause, so this is allowed.
27
'ready': Normal usage. (STANDBY)
28
'standby': Same logic as above.
29
Resume: Any state except undefined;
30
'created': Will lift a user's pause-on-start request.
31
'running': Will lift a pause request before it takes effect.
32
'paused': Normal usage.
33
'ready': Will lift a pause request before it takes effect.
34
'standby': Normal usage.
35
Set-speed: Any state except undefined, though ready may not be meaningful.
36
Complete: Only a 'ready' job may accept a complete request.
37
38
=======
39
Changes
40
=======
41
42
(1)
43
44
To facilitate "nice" error checking, all five major block-job verb
45
interfaces in blockjob.c now support an errp parameter:
46
47
- block_job_user_cancel is added as a new interface.
48
- block_job_user_pause gains an errp paramter
49
- block_job_user_resume gains an errp parameter
50
- block_job_set_speed already had an errp parameter.
51
- block_job_complete already had an errp parameter.
52
53
(2)
54
55
block-job-pause and block-job-resume will no longer no-op when trying
56
to pause an already paused job, or trying to resume a job that isn't
57
paused. These functions will now report that they did not perform the
58
action requested because it was not possible.
59
60
iotests have been adjusted to address this new behavior.
61
62
(3)
63
64
block-job-complete doesn't worry about checking !block_job_started,
65
because the permission table guards against this.
66
67
(4)
68
69
test-bdrv-drain's job implementation needs to announce that it is
70
'ready' now, in order to be completed.
71
72
Signed-off-by: John Snow <jsnow@redhat.com>
73
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
74
Reviewed-by: Eric Blake <eblake@redhat.com>
75
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
76
---
10
---
77
qapi/block-core.json | 20 ++++++++++++++
11
include/block/block.h | 13 +++++++++++++
78
include/block/blockjob.h | 13 +++++++--
12
block/io.c | 54 ++++++++++++++++++++++++++++++++++++++++-----------
79
blockdev.c | 10 +++----
13
2 files changed, 56 insertions(+), 11 deletions(-)
80
blockjob.c | 71 ++++++++++++++++++++++++++++++++++++++++++------
81
tests/test-bdrv-drain.c | 1 +
82
block/trace-events | 1 +
83
6 files changed, 100 insertions(+), 16 deletions(-)
84
14
85
diff --git a/qapi/block-core.json b/qapi/block-core.json
15
diff --git a/include/block/block.h b/include/block/block.h
86
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
87
--- a/qapi/block-core.json
17
--- a/include/block/block.h
88
+++ b/qapi/block-core.json
18
+++ b/include/block/block.h
89
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@ void bdrv_parent_drained_end(BlockDriverState *bs, BdrvChild *ignore);
90
'data': ['commit', 'stream', 'mirror', 'backup'] }
20
void bdrv_drained_begin(BlockDriverState *bs);
91
92
##
93
+# @BlockJobVerb:
94
+#
95
+# Represents command verbs that can be applied to a blockjob.
96
+#
97
+# @cancel: see @block-job-cancel
98
+#
99
+# @pause: see @block-job-pause
100
+#
101
+# @resume: see @block-job-resume
102
+#
103
+# @set-speed: see @block-job-set-speed
104
+#
105
+# @complete: see @block-job-complete
106
+#
107
+# Since: 2.12
108
+##
109
+{ 'enum': 'BlockJobVerb',
110
+ 'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete' ] }
111
+
112
+##
113
# @BlockJobStatus:
114
#
115
# Indicates the present state of a given blockjob in its lifetime.
116
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
117
index XXXXXXX..XXXXXXX 100644
118
--- a/include/block/blockjob.h
119
+++ b/include/block/blockjob.h
120
@@ -XXX,XX +XXX,XX @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
121
* Asynchronously pause the specified job.
122
* Do not allow a resume until a matching call to block_job_user_resume.
123
*/
124
-void block_job_user_pause(BlockJob *job);
125
+void block_job_user_pause(BlockJob *job, Error **errp);
126
21
127
/**
22
/**
128
* block_job_paused:
23
+ * Like bdrv_drained_begin, but recursively begins a quiesced section for
129
@@ -XXX,XX +XXX,XX @@ bool block_job_user_paused(BlockJob *job);
24
+ * exclusive access to all child nodes as well.
130
* Resume the specified job.
25
+ *
131
* Must be paired with a preceding block_job_user_pause.
26
+ * Graph changes are not allowed during a subtree drain section.
132
*/
27
+ */
133
-void block_job_user_resume(BlockJob *job);
28
+void bdrv_subtree_drained_begin(BlockDriverState *bs);
134
+void block_job_user_resume(BlockJob *job, Error **errp);
135
+
29
+
136
+/**
30
+/**
137
+ * block_job_user_cancel:
31
* bdrv_drained_end:
138
+ * @job: The job to be cancelled.
32
*
139
+ *
33
* End a quiescent section started by bdrv_drained_begin().
140
+ * Cancels the specified job, but may refuse to do so if the
34
*/
141
+ * operation isn't currently meaningful.
35
void bdrv_drained_end(BlockDriverState *bs);
36
37
+/**
38
+ * End a quiescent section started by bdrv_subtree_drained_begin().
142
+ */
39
+ */
143
+void block_job_user_cancel(BlockJob *job, Error **errp);
40
+void bdrv_subtree_drained_end(BlockDriverState *bs);
144
41
+
145
/**
42
void bdrv_add_child(BlockDriverState *parent, BlockDriverState *child,
146
* block_job_cancel_sync:
43
Error **errp);
147
diff --git a/blockdev.c b/blockdev.c
44
void bdrv_del_child(BlockDriverState *parent, BdrvChild *child, Error **errp);
45
diff --git a/block/io.c b/block/io.c
148
index XXXXXXX..XXXXXXX 100644
46
index XXXXXXX..XXXXXXX 100644
149
--- a/blockdev.c
47
--- a/block/io.c
150
+++ b/blockdev.c
48
+++ b/block/io.c
151
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_cancel(const char *device,
49
@@ -XXX,XX +XXX,XX @@ typedef struct {
50
BlockDriverState *bs;
51
bool done;
52
bool begin;
53
+ bool recursive;
54
BdrvChild *parent;
55
} BdrvCoDrainData;
56
57
@@ -XXX,XX +XXX,XX @@ static bool bdrv_drain_recurse(BlockDriverState *bs)
58
return waited;
59
}
60
61
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent);
62
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent);
63
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
64
+ BdrvChild *parent);
65
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
66
+ BdrvChild *parent);
67
68
static void bdrv_co_drain_bh_cb(void *opaque)
69
{
70
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
71
72
bdrv_dec_in_flight(bs);
73
if (data->begin) {
74
- bdrv_do_drained_begin(bs, data->parent);
75
+ bdrv_do_drained_begin(bs, data->recursive, data->parent);
76
} else {
77
- bdrv_do_drained_end(bs, data->parent);
78
+ bdrv_do_drained_end(bs, data->recursive, data->parent);
152
}
79
}
153
80
154
trace_qmp_block_job_cancel(job);
81
data->done = true;
155
- block_job_cancel(job);
82
@@ -XXX,XX +XXX,XX @@ static void bdrv_co_drain_bh_cb(void *opaque)
156
+ block_job_user_cancel(job, errp);
157
out:
158
aio_context_release(aio_context);
159
}
83
}
160
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_pause(const char *device, Error **errp)
84
161
AioContext *aio_context;
85
static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
162
BlockJob *job = find_block_job(device, &aio_context, errp);
86
- bool begin, BdrvChild *parent)
163
87
+ bool begin, bool recursive,
164
- if (!job || block_job_user_paused(job)) {
88
+ BdrvChild *parent)
165
+ if (!job) {
89
{
90
BdrvCoDrainData data;
91
92
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
93
.bs = bs,
94
.done = false,
95
.begin = begin,
96
+ .recursive = recursive,
97
.parent = parent,
98
};
99
bdrv_inc_in_flight(bs);
100
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
101
assert(data.done);
102
}
103
104
-static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
105
+static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
106
+ BdrvChild *parent)
107
{
108
+ BdrvChild *child, *next;
109
+
110
if (qemu_in_coroutine()) {
111
- bdrv_co_yield_to_drain(bs, true, parent);
112
+ bdrv_co_yield_to_drain(bs, true, recursive, parent);
166
return;
113
return;
167
}
114
}
168
115
169
trace_qmp_block_job_pause(job);
116
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent)
170
- block_job_user_pause(job);
117
bdrv_parent_drained_begin(bs, parent);
171
+ block_job_user_pause(job, errp);
118
bdrv_drain_invoke(bs, true, false);
172
aio_context_release(aio_context);
119
bdrv_drain_recurse(bs);
120
+
121
+ if (recursive) {
122
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
123
+ bdrv_do_drained_begin(child->bs, true, child);
124
+ }
125
+ }
173
}
126
}
174
127
175
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_resume(const char *device, Error **errp)
128
void bdrv_drained_begin(BlockDriverState *bs)
176
AioContext *aio_context;
129
{
177
BlockJob *job = find_block_job(device, &aio_context, errp);
130
- bdrv_do_drained_begin(bs, NULL);
178
131
+ bdrv_do_drained_begin(bs, false, NULL);
179
- if (!job || !block_job_user_paused(job)) {
132
+}
180
+ if (!job) {
133
+
134
+void bdrv_subtree_drained_begin(BlockDriverState *bs)
135
+{
136
+ bdrv_do_drained_begin(bs, true, NULL);
137
}
138
139
-static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
140
+static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
141
+ BdrvChild *parent)
142
{
143
+ BdrvChild *child, *next;
144
int old_quiesce_counter;
145
146
if (qemu_in_coroutine()) {
147
- bdrv_co_yield_to_drain(bs, false, parent);
148
+ bdrv_co_yield_to_drain(bs, false, recursive, parent);
181
return;
149
return;
182
}
150
}
183
151
assert(bs->quiesce_counter > 0);
184
trace_qmp_block_job_resume(job);
152
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent)
185
- block_job_user_resume(job);
153
if (old_quiesce_counter == 1) {
186
+ block_job_user_resume(job, errp);
154
aio_enable_external(bdrv_get_aio_context(bs));
187
aio_context_release(aio_context);
155
}
156
+
157
+ if (recursive) {
158
+ QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
159
+ bdrv_do_drained_end(child->bs, true, child);
160
+ }
161
+ }
188
}
162
}
189
163
190
diff --git a/blockjob.c b/blockjob.c
164
void bdrv_drained_end(BlockDriverState *bs)
191
index XXXXXXX..XXXXXXX 100644
192
--- a/blockjob.c
193
+++ b/blockjob.c
194
@@ -XXX,XX +XXX,XX @@ bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
195
/* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0},
196
};
197
198
+bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
199
+ /* U, C, R, P, Y, S */
200
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1},
201
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1},
202
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1},
203
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1},
204
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0},
205
+};
206
+
207
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
208
{
165
{
209
BlockJobStatus s0 = job->status;
166
- bdrv_do_drained_end(bs, NULL);
210
@@ -XXX,XX +XXX,XX @@ static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
167
+ bdrv_do_drained_end(bs, false, NULL);
211
job->status = s1;
212
}
213
214
+static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
215
+{
216
+ assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
217
+ trace_block_job_apply_verb(job, qapi_enum_lookup(&BlockJobStatus_lookup,
218
+ job->status),
219
+ qapi_enum_lookup(&BlockJobVerb_lookup, bv),
220
+ BlockJobVerbTable[bv][job->status] ?
221
+ "allowed" : "prohibited");
222
+ if (BlockJobVerbTable[bv][job->status]) {
223
+ return 0;
224
+ }
225
+ error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
226
+ job->id, qapi_enum_lookup(&BlockJobStatus_lookup, job->status),
227
+ qapi_enum_lookup(&BlockJobVerb_lookup, bv));
228
+ return -EPERM;
229
+}
168
+}
230
+
169
+
231
static void block_job_lock(void)
170
+void bdrv_subtree_drained_end(BlockDriverState *bs)
232
{
171
+{
233
qemu_mutex_lock(&block_job_mutex);
172
+ bdrv_do_drained_end(bs, true, NULL);
234
@@ -XXX,XX +XXX,XX @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
235
error_setg(errp, QERR_UNSUPPORTED);
236
return;
237
}
238
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
239
+ return;
240
+ }
241
job->driver->set_speed(job, speed, &local_err);
242
if (local_err) {
243
error_propagate(errp, local_err);
244
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
245
{
246
/* Should not be reachable via external interface for internal jobs */
247
assert(job->id);
248
- if (job->pause_count || job->cancelled ||
249
- !block_job_started(job) || !job->driver->complete) {
250
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
251
+ return;
252
+ }
253
+ if (job->pause_count || job->cancelled || !job->driver->complete) {
254
error_setg(errp, "The active block job '%s' cannot be completed",
255
job->id);
256
return;
257
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
258
job->driver->complete(job, errp);
259
}
173
}
260
174
261
-void block_job_user_pause(BlockJob *job)
175
/*
262
+void block_job_user_pause(BlockJob *job, Error **errp)
263
{
264
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
265
+ return;
266
+ }
267
+ if (job->user_paused) {
268
+ error_setg(errp, "Job is already paused");
269
+ return;
270
+ }
271
job->user_paused = true;
272
block_job_pause(job);
273
}
274
@@ -XXX,XX +XXX,XX @@ bool block_job_user_paused(BlockJob *job)
275
return job->user_paused;
276
}
277
278
-void block_job_user_resume(BlockJob *job)
279
+void block_job_user_resume(BlockJob *job, Error **errp)
280
{
281
- if (job && job->user_paused && job->pause_count > 0) {
282
- block_job_iostatus_reset(job);
283
- job->user_paused = false;
284
- block_job_resume(job);
285
+ assert(job);
286
+ if (!job->user_paused || job->pause_count <= 0) {
287
+ error_setg(errp, "Can't resume a job that was not paused");
288
+ return;
289
+ }
290
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
291
+ return;
292
}
293
+ block_job_iostatus_reset(job);
294
+ job->user_paused = false;
295
+ block_job_resume(job);
296
}
297
298
void block_job_cancel(BlockJob *job)
299
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
300
}
301
}
302
303
+void block_job_user_cancel(BlockJob *job, Error **errp)
304
+{
305
+ if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
306
+ return;
307
+ }
308
+ block_job_cancel(job);
309
+}
310
+
311
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
312
* used with block_job_finish_sync() without the need for (rather nasty)
313
* function pointer casts there. */
314
@@ -XXX,XX +XXX,XX @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err,
315
action, &error_abort);
316
}
317
if (action == BLOCK_ERROR_ACTION_STOP) {
318
+ block_job_pause(job);
319
/* make the pause user visible, which will be resumed from QMP. */
320
- block_job_user_pause(job);
321
+ job->user_paused = true;
322
block_job_iostatus_set_err(job, error);
323
}
324
return action;
325
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
326
index XXXXXXX..XXXXXXX 100644
327
--- a/tests/test-bdrv-drain.c
328
+++ b/tests/test-bdrv-drain.c
329
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
330
{
331
TestBlockJob *s = opaque;
332
333
+ block_job_event_ready(&s->common);
334
while (!s->should_complete) {
335
block_job_sleep_ns(&s->common, 100000);
336
}
337
diff --git a/block/trace-events b/block/trace-events
338
index XXXXXXX..XXXXXXX 100644
339
--- a/block/trace-events
340
+++ b/block/trace-events
341
@@ -XXX,XX +XXX,XX @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
342
343
# blockjob.c
344
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)"
345
+block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
346
347
# block/block-backend.c
348
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"
349
--
176
--
350
2.13.6
177
2.13.6
351
178
352
179
diff view generated by jsdifflib
Deleted patch
1
From: John Snow <jsnow@redhat.com>
2
1
3
Add a new state ABORTING.
4
5
This makes transitions from normative states to error states explicit
6
in the STM, and serves as a disambiguation for which states may complete
7
normally when normal end-states (CONCLUDED) are added in future commits.
8
9
Notably, Paused/Standby jobs do not transition directly to aborting,
10
as they must wake up first and cooperate in their cancellation.
11
12
Transitions:
13
Created -> Aborting: can be cancelled (by the system)
14
Running -> Aborting: can be cancelled or encounter an error
15
Ready -> Aborting: can be cancelled or encounter an error
16
17
Verbs:
18
None. The job must finish cleaning itself up and report its final status.
19
20
+---------+
21
|UNDEFINED|
22
+--+------+
23
|
24
+--v----+
25
+---------+CREATED|
26
| +--+----+
27
| |
28
| +--v----+ +------+
29
+---------+RUNNING<----->PAUSED|
30
| +--+----+ +------+
31
| |
32
| +--v--+ +-------+
33
+---------+READY<------->STANDBY|
34
| +-----+ +-------+
35
|
36
+--v-----+
37
|ABORTING|
38
+--------+
39
40
Signed-off-by: John Snow <jsnow@redhat.com>
41
Reviewed-by: Eric Blake <eblake@redhat.com>
42
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
43
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
44
---
45
qapi/block-core.json | 7 ++++++-
46
blockjob.c | 31 ++++++++++++++++++-------------
47
2 files changed, 24 insertions(+), 14 deletions(-)
48
49
diff --git a/qapi/block-core.json b/qapi/block-core.json
50
index XXXXXXX..XXXXXXX 100644
51
--- a/qapi/block-core.json
52
+++ b/qapi/block-core.json
53
@@ -XXX,XX +XXX,XX @@
54
# @standby: The job is ready, but paused. This is nearly identical to @paused.
55
# The job may return to @ready or otherwise be canceled.
56
#
57
+# @aborting: The job is in the process of being aborted, and will finish with
58
+# an error.
59
+# This status may not be visible to the management process.
60
+#
61
# Since: 2.12
62
##
63
{ 'enum': 'BlockJobStatus',
64
- 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby'] }
65
+ 'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
66
+ 'aborting' ] }
67
68
##
69
# @BlockJobInfo:
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 @@ static QemuMutex block_job_mutex;
75
76
/* BlockJob State Transition Table */
77
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
78
- /* U, C, R, P, Y, S */
79
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0},
80
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0},
81
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0},
82
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0},
83
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1},
84
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0},
85
+ /* U, C, R, P, Y, S, X */
86
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0},
87
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1},
88
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1},
89
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0},
90
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1},
91
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0},
92
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0},
93
};
94
95
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
96
- /* U, C, R, P, Y, S */
97
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1},
98
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1},
99
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1},
100
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1},
101
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0},
102
+ /* U, C, R, P, Y, S, X */
103
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0},
104
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0},
105
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0},
106
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0},
107
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0},
108
};
109
110
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
111
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
112
{
113
assert(job->completed);
114
115
+ if (job->ret || block_job_is_cancelled(job)) {
116
+ block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
117
+ }
118
+
119
if (!job->ret) {
120
if (job->driver->commit) {
121
job->driver->commit(job);
122
--
123
2.13.6
124
125
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
Add a subtree drain version to the existing test cases.
2
2
3
Some jobs upon finalization may need to perform some work that can
4
still fail. If these jobs are part of a transaction, it's important
5
that these callbacks fail the entire transaction.
6
7
We allow for a new callback in addition to commit/abort/clean that
8
allows us the opportunity to have fairly late-breaking failures
9
in the transactional process.
10
11
The expected flow is:
12
13
- All jobs in a transaction converge to the PENDING state,
14
added in a forthcoming commit.
15
- Upon being finalized, either automatically or explicitly
16
by the user, jobs prepare to complete.
17
- If any job fails preparation, all jobs call .abort.
18
- Otherwise, they succeed and call .commit.
19
20
Signed-off-by: John Snow <jsnow@redhat.com>
21
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
22
---
4
---
23
include/block/blockjob_int.h | 10 ++++++++++
5
tests/test-bdrv-drain.c | 27 ++++++++++++++++++++++++++-
24
blockjob.c | 30 +++++++++++++++++++++++++++---
6
1 file changed, 26 insertions(+), 1 deletion(-)
25
2 files changed, 37 insertions(+), 3 deletions(-)
26
7
27
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
28
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
29
--- a/include/block/blockjob_int.h
10
--- a/tests/test-bdrv-drain.c
30
+++ b/include/block/blockjob_int.h
11
+++ b/tests/test-bdrv-drain.c
31
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
12
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
32
void (*complete)(BlockJob *job, Error **errp);
13
enum drain_type {
33
14
BDRV_DRAIN_ALL,
34
/**
15
BDRV_DRAIN,
35
+ * If the callback is not NULL, prepare will be invoked when all the jobs
16
+ BDRV_SUBTREE_DRAIN,
36
+ * belonging to the same transaction complete; or upon this job's completion
17
DRAIN_TYPE_MAX,
37
+ * if it is not in a transaction.
18
};
38
+ *
19
39
+ * This callback will not be invoked if the job has already failed.
20
@@ -XXX,XX +XXX,XX @@ static void do_drain_begin(enum drain_type drain_type, BlockDriverState *bs)
40
+ * If it fails, abort and then clean will be called.
21
switch (drain_type) {
41
+ */
22
case BDRV_DRAIN_ALL: bdrv_drain_all_begin(); break;
42
+ int (*prepare)(BlockJob *job);
23
case BDRV_DRAIN: bdrv_drained_begin(bs); break;
43
+
24
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_begin(bs); break;
44
+ /**
25
default: g_assert_not_reached();
45
* If the callback is not NULL, it will be invoked when all the jobs
46
* belonging to the same transaction complete; or upon this job's
47
* completion if it is not in a transaction. Skipped if NULL.
48
diff --git a/blockjob.c b/blockjob.c
49
index XXXXXXX..XXXXXXX 100644
50
--- a/blockjob.c
51
+++ b/blockjob.c
52
@@ -XXX,XX +XXX,XX @@ static void block_job_update_rc(BlockJob *job)
53
}
26
}
54
}
27
}
55
28
@@ -XXX,XX +XXX,XX @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs)
56
+static int block_job_prepare(BlockJob *job)
29
switch (drain_type) {
30
case BDRV_DRAIN_ALL: bdrv_drain_all_end(); break;
31
case BDRV_DRAIN: bdrv_drained_end(bs); break;
32
+ case BDRV_SUBTREE_DRAIN: bdrv_subtree_drained_end(bs); break;
33
default: g_assert_not_reached();
34
}
35
}
36
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain(void)
37
test_drv_cb_common(BDRV_DRAIN, false);
38
}
39
40
+static void test_drv_cb_drain_subtree(void)
57
+{
41
+{
58
+ if (job->ret == 0 && job->driver->prepare) {
42
+ test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
59
+ job->ret = job->driver->prepare(job);
60
+ }
61
+ return job->ret;
62
+}
43
+}
63
+
44
+
64
static void block_job_commit(BlockJob *job)
45
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
65
{
46
{
66
assert(!job->ret);
47
BlockBackend *blk;
67
@@ -XXX,XX +XXX,XX @@ static void block_job_clean(BlockJob *job)
48
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain(void)
68
}
49
test_quiesce_common(BDRV_DRAIN, false);
69
}
50
}
70
51
71
-static void block_job_completed_single(BlockJob *job)
52
+static void test_quiesce_drain_subtree(void)
72
+static int block_job_completed_single(BlockJob *job)
53
+{
54
+ test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
55
+}
56
+
57
static void test_nested(void)
73
{
58
{
74
assert(job->completed);
59
BlockBackend *blk;
75
60
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
76
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
61
/* XXX bdrv_drain_all() doesn't increase the quiesce_counter */
77
QLIST_REMOVE(job, txn_list);
62
int bs_quiesce = (outer != BDRV_DRAIN_ALL) +
78
block_job_txn_unref(job->txn);
63
(inner != BDRV_DRAIN_ALL);
79
block_job_conclude(job);
64
- int backing_quiesce = 0;
80
+ return 0;
65
+ int backing_quiesce = (outer == BDRV_SUBTREE_DRAIN) +
66
+ (inner == BDRV_SUBTREE_DRAIN);
67
int backing_cb_cnt = (outer != BDRV_DRAIN) +
68
(inner != BDRV_DRAIN);
69
70
@@ -XXX,XX +XXX,XX @@ static void test_blockjob_drain(void)
71
test_blockjob_common(BDRV_DRAIN);
81
}
72
}
82
73
83
static void block_job_cancel_async(BlockJob *job)
74
+static void test_blockjob_drain_subtree(void)
84
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job)
75
+{
85
job->cancelled = true;
76
+ test_blockjob_common(BDRV_SUBTREE_DRAIN);
86
}
77
+}
87
78
+
88
-static void block_job_txn_apply(BlockJobTxn *txn, void fn(BlockJob *))
79
int main(int argc, char **argv)
89
+static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *))
90
{
80
{
91
AioContext *ctx;
81
bdrv_init();
92
BlockJob *job, *next;
82
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
93
+ int rc;
83
94
84
g_test_add_func("/bdrv-drain/driver-cb/drain_all", test_drv_cb_drain_all);
95
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
85
g_test_add_func("/bdrv-drain/driver-cb/drain", test_drv_cb_drain);
96
ctx = blk_get_aio_context(job->blk);
86
+ g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
97
aio_context_acquire(ctx);
87
+ test_drv_cb_drain_subtree);
98
- fn(job);
88
99
+ rc = fn(job);
89
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
100
aio_context_release(ctx);
90
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
101
+ if (rc) {
91
+ g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
102
+ break;
92
+ test_quiesce_drain_subtree);
103
+ }
93
104
}
94
g_test_add_func("/bdrv-drain/nested", test_nested);
105
+ return rc;
95
106
}
96
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
107
97
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
108
static int block_job_finish_sync(BlockJob *job,
98
+ g_test_add_func("/bdrv-drain/blockjob/drain_subtree",
109
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
99
+ test_blockjob_drain_subtree);
110
{
100
111
BlockJobTxn *txn = job->txn;
101
return g_test_run();
112
BlockJob *other_job;
113
+ int rc = 0;
114
+
115
/*
116
* Successful completion, see if there are other running jobs in this
117
* txn.
118
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
119
}
120
assert(other_job->ret == 0);
121
}
122
+
123
+ /* Jobs may require some prep-work to complete without failure */
124
+ rc = block_job_txn_apply(txn, block_job_prepare);
125
+ if (rc) {
126
+ block_job_completed_txn_abort(job);
127
+ return;
128
+ }
129
+
130
/* We are the last completed job, commit the transaction. */
131
block_job_txn_apply(txn, block_job_completed_single);
132
}
102
}
133
--
103
--
134
2.13.6
104
2.13.6
135
105
136
106
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
If bdrv_do_drained_begin/end() are called in coroutine context, they
2
first use a BH to get out of the coroutine context. Call some existing
3
tests again from a coroutine to cover this code path.
2
4
3
Simply apply a function transaction-wide.
4
A few more uses of this in forthcoming patches.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
8
---
6
---
9
blockjob.c | 25 ++++++++++++++++---------
7
tests/test-bdrv-drain.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++
10
1 file changed, 16 insertions(+), 9 deletions(-)
8
1 file changed, 59 insertions(+)
11
9
12
diff --git a/blockjob.c b/blockjob.c
10
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
13
index XXXXXXX..XXXXXXX 100644
11
index XXXXXXX..XXXXXXX 100644
14
--- a/blockjob.c
12
--- a/tests/test-bdrv-drain.c
15
+++ b/blockjob.c
13
+++ b/tests/test-bdrv-drain.c
16
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job)
14
@@ -XXX,XX +XXX,XX @@ static void aio_ret_cb(void *opaque, int ret)
17
job->cancelled = true;
15
*aio_ret = ret;
18
}
16
}
19
17
20
+static void block_job_txn_apply(BlockJobTxn *txn, void fn(BlockJob *))
18
+typedef struct CallInCoroutineData {
19
+ void (*entry)(void);
20
+ bool done;
21
+} CallInCoroutineData;
22
+
23
+static coroutine_fn void call_in_coroutine_entry(void *opaque)
21
+{
24
+{
22
+ AioContext *ctx;
25
+ CallInCoroutineData *data = opaque;
23
+ BlockJob *job, *next;
24
+
26
+
25
+ QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
27
+ data->entry();
26
+ ctx = blk_get_aio_context(job->blk);
28
+ data->done = true;
27
+ aio_context_acquire(ctx);
29
+}
28
+ fn(job);
30
+
29
+ aio_context_release(ctx);
31
+static void call_in_coroutine(void (*entry)(void))
32
+{
33
+ Coroutine *co;
34
+ CallInCoroutineData data = {
35
+ .entry = entry,
36
+ .done = false,
37
+ };
38
+
39
+ co = qemu_coroutine_create(call_in_coroutine_entry, &data);
40
+ qemu_coroutine_enter(co);
41
+ while (!data.done) {
42
+ aio_poll(qemu_get_aio_context(), true);
30
+ }
43
+ }
31
+}
44
+}
32
+
45
+
33
static int block_job_finish_sync(BlockJob *job,
46
enum drain_type {
34
void (*finish)(BlockJob *, Error **errp),
47
BDRV_DRAIN_ALL,
35
Error **errp)
48
BDRV_DRAIN,
36
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
49
@@ -XXX,XX +XXX,XX @@ static void test_drv_cb_drain_subtree(void)
37
50
test_drv_cb_common(BDRV_SUBTREE_DRAIN, true);
38
static void block_job_completed_txn_success(BlockJob *job)
51
}
52
53
+static void test_drv_cb_co_drain(void)
54
+{
55
+ call_in_coroutine(test_drv_cb_drain);
56
+}
57
+
58
+static void test_drv_cb_co_drain_subtree(void)
59
+{
60
+ call_in_coroutine(test_drv_cb_drain_subtree);
61
+}
62
+
63
static void test_quiesce_common(enum drain_type drain_type, bool recursive)
39
{
64
{
40
- AioContext *ctx;
65
BlockBackend *blk;
41
BlockJobTxn *txn = job->txn;
66
@@ -XXX,XX +XXX,XX @@ static void test_quiesce_drain_subtree(void)
42
- BlockJob *other_job, *next;
67
test_quiesce_common(BDRV_SUBTREE_DRAIN, true);
43
+ BlockJob *other_job;
44
/*
45
* Successful completion, see if there are other running jobs in this
46
* txn.
47
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
48
if (!other_job->completed) {
49
return;
50
}
51
- }
52
- /* We are the last completed job, commit the transaction. */
53
- QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
54
- ctx = blk_get_aio_context(other_job->blk);
55
- aio_context_acquire(ctx);
56
assert(other_job->ret == 0);
57
- block_job_completed_single(other_job);
58
- aio_context_release(ctx);
59
}
60
+ /* We are the last completed job, commit the transaction. */
61
+ block_job_txn_apply(txn, block_job_completed_single);
62
}
68
}
63
69
64
/* Assumes the block_job_mutex is held */
70
+static void test_quiesce_co_drain(void)
71
+{
72
+ call_in_coroutine(test_quiesce_drain);
73
+}
74
+
75
+static void test_quiesce_co_drain_subtree(void)
76
+{
77
+ call_in_coroutine(test_quiesce_drain_subtree);
78
+}
79
+
80
static void test_nested(void)
81
{
82
BlockBackend *blk;
83
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
84
g_test_add_func("/bdrv-drain/driver-cb/drain_subtree",
85
test_drv_cb_drain_subtree);
86
87
+ // XXX bdrv_drain_all() doesn't work in coroutine context
88
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain", test_drv_cb_co_drain);
89
+ g_test_add_func("/bdrv-drain/driver-cb/co/drain_subtree",
90
+ test_drv_cb_co_drain_subtree);
91
+
92
+
93
g_test_add_func("/bdrv-drain/quiesce/drain_all", test_quiesce_drain_all);
94
g_test_add_func("/bdrv-drain/quiesce/drain", test_quiesce_drain);
95
g_test_add_func("/bdrv-drain/quiesce/drain_subtree",
96
test_quiesce_drain_subtree);
97
98
+ // XXX bdrv_drain_all() doesn't work in coroutine context
99
+ g_test_add_func("/bdrv-drain/quiesce/co/drain", test_quiesce_co_drain);
100
+ g_test_add_func("/bdrv-drain/quiesce/co/drain_subtree",
101
+ test_quiesce_co_drain_subtree);
102
+
103
g_test_add_func("/bdrv-drain/nested", test_nested);
104
105
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
65
--
106
--
66
2.13.6
107
2.13.6
67
108
68
109
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
Test that drain sections are correctly propagated through the graph.
2
2
3
Add a new state that specifically demarcates when we begin to permanently
4
demolish a job after it has performed all work. This makes the transition
5
explicit in the STM table and highlights conditions under which a job may
6
be demolished.
7
8
Alongside this state, add a new helper command "block_job_decommission",
9
which transitions to the NULL state and puts down our implicit reference.
10
This separates instances in the code for "block_job_unref" which merely
11
undo a matching "block_job_ref" with instances intended to initiate the
12
full destruction of the object.
13
14
This decommission action also sets a number of fields to make sure that
15
block internals or external users that are holding a reference to a job
16
to see when it "finishes" are convinced that the job object is "done."
17
This is necessary, for instance, to do a block_job_cancel_sync on a
18
created object which will not make any progress.
19
20
Now, all jobs must go through block_job_decommission prior to being
21
freed, giving us start-to-finish state machine coverage for jobs.
22
23
Transitions:
24
Created -> Null: Early failure event before the job is started
25
Concluded -> Null: Standard transition.
26
27
Verbs:
28
None. This should not ever be visible to the monitor.
29
30
+---------+
31
|UNDEFINED|
32
+--+------+
33
|
34
+--v----+
35
+---------+CREATED+------------------+
36
| +--+----+ |
37
| | |
38
| +--v----+ +------+ |
39
+---------+RUNNING<----->PAUSED| |
40
| +--+-+--+ +------+ |
41
| | | |
42
| | +------------------+ |
43
| | | |
44
| +--v--+ +-------+ | |
45
+---------+READY<------->STANDBY| | |
46
| +--+--+ +-------+ | |
47
| | | |
48
+--v-----+ +--v------+ | |
49
|ABORTING+--->CONCLUDED<-------------+ |
50
+--------+ +--+------+ |
51
| |
52
+--v-+ |
53
|NULL<---------------------+
54
+----+
55
56
Signed-off-by: John Snow <jsnow@redhat.com>
57
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
3
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
58
---
4
---
59
qapi/block-core.json | 5 ++++-
5
tests/test-bdrv-drain.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++
60
blockjob.c | 50 ++++++++++++++++++++++++++++++++------------------
6
1 file changed, 74 insertions(+)
61
2 files changed, 36 insertions(+), 19 deletions(-)
62
7
63
diff --git a/qapi/block-core.json b/qapi/block-core.json
8
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
64
index XXXXXXX..XXXXXXX 100644
9
index XXXXXXX..XXXXXXX 100644
65
--- a/qapi/block-core.json
10
--- a/tests/test-bdrv-drain.c
66
+++ b/qapi/block-core.json
11
+++ b/tests/test-bdrv-drain.c
67
@@ -XXX,XX +XXX,XX @@
12
@@ -XXX,XX +XXX,XX @@ static void test_nested(void)
68
# @concluded: The job has finished all work. If manual was set to true, the job
13
blk_unref(blk);
69
# will remain in the query list until it is dismissed.
70
#
71
+# @null: The job is in the process of being dismantled. This state should not
72
+# ever be visible externally.
73
+#
74
# Since: 2.12
75
##
76
{ 'enum': 'BlockJobStatus',
77
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
78
- 'aborting', 'concluded' ] }
79
+ 'aborting', 'concluded', 'null' ] }
80
81
##
82
# @BlockJobInfo:
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 @@ static QemuMutex block_job_mutex;
88
89
/* BlockJob State Transition Table */
90
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
91
- /* U, C, R, P, Y, S, X, E */
92
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0},
93
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1, 0},
94
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1},
95
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0},
96
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1},
97
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0},
98
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1},
99
- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0},
100
+ /* U, C, R, P, Y, S, X, E, N */
101
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0},
102
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1, 0, 1},
103
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1, 0},
104
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0},
105
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1, 0},
106
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
107
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1, 0},
108
+ /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 1},
109
+ /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0},
110
};
111
112
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
113
- /* U, C, R, P, Y, S, X, E */
114
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0, 0},
115
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0},
116
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0},
117
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0},
118
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0},
119
+ /* U, C, R, P, Y, S, X, E, N */
120
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
121
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
122
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
123
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0},
124
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0},
125
};
126
127
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
128
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque);
129
void block_job_unref(BlockJob *job)
130
{
131
if (--job->refcnt == 0) {
132
+ assert(job->status == BLOCK_JOB_STATUS_NULL);
133
BlockDriverState *bs = blk_bs(job->blk);
134
QLIST_REMOVE(job, job_list);
135
bs->job = NULL;
136
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
137
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
138
}
14
}
139
15
140
+static void block_job_decommission(BlockJob *job)
16
+static void test_multiparent(void)
141
+{
17
+{
142
+ assert(job);
18
+ BlockBackend *blk_a, *blk_b;
143
+ job->completed = true;
19
+ BlockDriverState *bs_a, *bs_b, *backing;
144
+ job->busy = false;
20
+ BDRVTestState *a_s, *b_s, *backing_s;
145
+ job->paused = false;
21
+
146
+ job->deferred_to_main_loop = true;
22
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
147
+ block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
23
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
148
+ block_job_unref(job);
24
+ &error_abort);
25
+ a_s = bs_a->opaque;
26
+ blk_insert_bs(blk_a, bs_a, &error_abort);
27
+
28
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
29
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
30
+ &error_abort);
31
+ b_s = bs_b->opaque;
32
+ blk_insert_bs(blk_b, bs_b, &error_abort);
33
+
34
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
35
+ backing_s = backing->opaque;
36
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
37
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
38
+
39
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
40
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
41
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
42
+ g_assert_cmpint(a_s->drain_count, ==, 0);
43
+ g_assert_cmpint(b_s->drain_count, ==, 0);
44
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
45
+
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
47
+
48
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
49
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
50
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
51
+ g_assert_cmpint(a_s->drain_count, ==, 1);
52
+ g_assert_cmpint(b_s->drain_count, ==, 1);
53
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
54
+
55
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
56
+
57
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 2);
58
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
59
+ g_assert_cmpint(backing->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(a_s->drain_count, ==, 2);
61
+ g_assert_cmpint(b_s->drain_count, ==, 2);
62
+ g_assert_cmpint(backing_s->drain_count, ==, 2);
63
+
64
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
65
+
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 1);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 1);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 1);
69
+ g_assert_cmpint(a_s->drain_count, ==, 1);
70
+ g_assert_cmpint(b_s->drain_count, ==, 1);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 1);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
74
+
75
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
76
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
77
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
78
+ g_assert_cmpint(a_s->drain_count, ==, 0);
79
+ g_assert_cmpint(b_s->drain_count, ==, 0);
80
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
81
+
82
+ bdrv_unref(backing);
83
+ bdrv_unref(bs_a);
84
+ bdrv_unref(bs_b);
85
+ blk_unref(blk_a);
86
+ blk_unref(blk_b);
149
+}
87
+}
150
+
88
+
151
static void block_job_conclude(BlockJob *job)
89
152
{
90
typedef struct TestBlockJob {
153
block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
91
BlockJob common;
154
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
92
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
155
QLIST_REMOVE(job, txn_list);
93
test_quiesce_co_drain_subtree);
156
block_job_txn_unref(job->txn);
94
157
block_job_conclude(job);
95
g_test_add_func("/bdrv-drain/nested", test_nested);
158
- block_job_unref(job);
96
+ g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
159
+ block_job_decommission(job);
97
160
}
98
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
161
99
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
162
static void block_job_cancel_async(BlockJob *job)
163
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
164
165
block_job_set_speed(job, speed, &local_err);
166
if (local_err) {
167
- block_job_unref(job);
168
+ block_job_early_fail(job);
169
error_propagate(errp, local_err);
170
return NULL;
171
}
172
@@ -XXX,XX +XXX,XX @@ void block_job_pause_all(void)
173
174
void block_job_early_fail(BlockJob *job)
175
{
176
- block_job_unref(job);
177
+ assert(job->status == BLOCK_JOB_STATUS_CREATED);
178
+ block_job_decommission(job);
179
}
180
181
void block_job_completed(BlockJob *job, int ret)
182
--
100
--
183
2.13.6
101
2.13.6
184
102
185
103
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
1
We need to remember how many of the drain sections in which a node is
2
2
were recursive (i.e. subtree drain rather than node drain), so that they
3
For jobs utilizing the new manual workflow, we intend to prohibit
3
can be correctly applied when children are added or removed during the
4
them from modifying the block graph until the management layer provides
4
drained section.
5
an explicit ACK via block-job-finalize to move the process forward.
5
6
6
With this change, it is safe to modify the graph even inside a
7
To distinguish this runstate from "ready" or "waiting," we add a new
7
bdrv_subtree_drained_begin/end() section.
8
"pending" event and status.
8
9
10
For now, the transition from PENDING to CONCLUDED/ABORTING is automatic,
11
but a future commit will add the explicit block-job-finalize step.
12
13
Transitions:
14
Waiting -> Pending: Normal transition.
15
Pending -> Concluded: Normal transition.
16
Pending -> Aborting: Late transactional failures and cancellations.
17
18
Removed Transitions:
19
Waiting -> Concluded: Jobs must go to PENDING first.
20
21
Verbs:
22
Cancel: Can be applied to a pending job.
23
24
+---------+
25
|UNDEFINED|
26
+--+------+
27
|
28
+--v----+
29
+---------+CREATED+-----------------+
30
| +--+----+ |
31
| | |
32
| +--+----+ +------+ |
33
+---------+RUNNING<----->PAUSED| |
34
| +--+-+--+ +------+ |
35
| | | |
36
| | +------------------+ |
37
| | | |
38
| +--v--+ +-------+ | |
39
+---------+READY<------->STANDBY| | |
40
| +--+--+ +-------+ | |
41
| | | |
42
| +--v----+ | |
43
+---------+WAITING<---------------+ |
44
| +--+----+ |
45
| | |
46
| +--v----+ |
47
+---------+PENDING| |
48
| +--+----+ |
49
| | |
50
+--v-----+ +--v------+ |
51
|ABORTING+--->CONCLUDED| |
52
+--------+ +--+------+ |
53
| |
54
+--v-+ |
55
|NULL<--------------------+
56
+----+
57
58
Signed-off-by: John Snow <jsnow@redhat.com>
59
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
60
---
10
---
61
qapi/block-core.json | 31 +++++++++++++++++++++-
11
include/block/block.h | 2 --
62
include/block/blockjob.h | 5 ++++
12
include/block/block_int.h | 5 +++++
63
blockjob.c | 67 +++++++++++++++++++++++++++++++-----------------
13
block.c | 32 +++++++++++++++++++++++++++++---
64
3 files changed, 78 insertions(+), 25 deletions(-)
14
block/io.c | 28 ++++++++++++++++++++++++----
65
15
4 files changed, 58 insertions(+), 9 deletions(-)
66
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
67
index XXXXXXX..XXXXXXX 100644
17
diff --git a/include/block/block.h b/include/block/block.h
68
--- a/qapi/block-core.json
18
index XXXXXXX..XXXXXXX 100644
69
+++ b/qapi/block-core.json
19
--- a/include/block/block.h
70
@@ -XXX,XX +XXX,XX @@
20
+++ b/include/block/block.h
71
# to the waiting state. This status will likely not be visible for
21
@@ -XXX,XX +XXX,XX @@ void bdrv_drained_begin(BlockDriverState *bs);
72
# the last job in a transaction.
22
/**
73
#
23
* Like bdrv_drained_begin, but recursively begins a quiesced section for
74
+# @pending: The job has finished its work, but has finalization steps that it
24
* exclusive access to all child nodes as well.
75
+# needs to make prior to completing. These changes may require
25
- *
76
+# manual intervention by the management process if manual was set
26
- * Graph changes are not allowed during a subtree drain section.
77
+# to true. These changes may still fail.
27
*/
78
+#
28
void bdrv_subtree_drained_begin(BlockDriverState *bs);
79
# @aborting: The job is in the process of being aborted, and will finish with
29
80
# an error. The job will afterwards report that it is @concluded.
30
diff --git a/include/block/block_int.h b/include/block/block_int.h
81
# This status may not be visible to the management process.
31
index XXXXXXX..XXXXXXX 100644
82
@@ -XXX,XX +XXX,XX @@
32
--- a/include/block/block_int.h
83
##
33
+++ b/include/block/block_int.h
84
{ 'enum': 'BlockJobStatus',
34
@@ -XXX,XX +XXX,XX @@ struct BlockDriverState {
85
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
35
86
- 'waiting', 'aborting', 'concluded', 'null' ] }
36
/* Accessed with atomic ops. */
87
+ 'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
37
int quiesce_counter;
88
38
+ int recursive_quiesce_counter;
89
##
39
+
90
# @BlockJobInfo:
40
unsigned int write_gen; /* Current data generation */
91
@@ -XXX,XX +XXX,XX @@
41
92
'speed' : 'int' } }
42
/* Protected by reqs_lock. */
93
43
@@ -XXX,XX +XXX,XX @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child,
94
##
44
int64_t offset, unsigned int bytes, QEMUIOVector *qiov,
95
+# @BLOCK_JOB_PENDING:
45
BdrvRequestFlags flags);
96
+#
46
97
+# Emitted when a block job is awaiting explicit authorization to finalize graph
47
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent);
98
+# changes via @block-job-finalize. If this job is part of a transaction, it will
48
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent);
99
+# not emit this event until the transaction has converged first.
49
+
100
+#
50
int get_tmp_filename(char *filename, int size);
101
+# @type: job type
51
BlockDriver *bdrv_probe_all(const uint8_t *buf, int buf_size,
102
+#
52
const char *filename);
103
+# @id: The job identifier.
53
diff --git a/block.c b/block.c
104
+#
54
index XXXXXXX..XXXXXXX 100644
105
+# Since: 2.12
55
--- a/block.c
106
+#
56
+++ b/block.c
107
+# Example:
57
@@ -XXX,XX +XXX,XX @@ static void bdrv_child_cb_drained_end(BdrvChild *child)
108
+#
58
bdrv_drained_end(bs);
109
+# <- { "event": "BLOCK_JOB_WAITING",
59
}
110
+# "data": { "device": "drive0", "type": "mirror" },
60
111
+# "timestamp": { "seconds": 1265044230, "microseconds": 450486 } }
61
+static void bdrv_child_cb_attach(BdrvChild *child)
112
+#
62
+{
113
+##
63
+ BlockDriverState *bs = child->opaque;
114
+{ 'event': 'BLOCK_JOB_PENDING',
64
+ bdrv_apply_subtree_drain(child, bs);
115
+ 'data': { 'type' : 'BlockJobType',
65
+}
116
+ 'id' : 'str' } }
66
+
117
+
67
+static void bdrv_child_cb_detach(BdrvChild *child)
118
+##
68
+{
119
# @PreallocMode:
69
+ BlockDriverState *bs = child->opaque;
120
#
70
+ bdrv_unapply_subtree_drain(child, bs);
121
# Preallocation mode of QEMU image file
71
+}
122
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
72
+
123
index XXXXXXX..XXXXXXX 100644
73
static int bdrv_child_cb_inactivate(BdrvChild *child)
124
--- a/include/block/blockjob.h
74
{
125
+++ b/include/block/blockjob.h
75
BlockDriverState *bs = child->opaque;
126
@@ -XXX,XX +XXX,XX @@ typedef struct BlockJob {
76
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_file = {
127
/** Current state; See @BlockJobStatus for details. */
77
.inherit_options = bdrv_inherited_options,
128
BlockJobStatus status;
78
.drained_begin = bdrv_child_cb_drained_begin,
129
79
.drained_end = bdrv_child_cb_drained_end,
130
+ /** True if this job should automatically finalize itself */
80
+ .attach = bdrv_child_cb_attach,
131
+ bool auto_finalize;
81
+ .detach = bdrv_child_cb_detach,
132
+
82
.inactivate = bdrv_child_cb_inactivate,
133
/** True if this job should automatically dismiss itself */
134
bool auto_dismiss;
135
136
@@ -XXX,XX +XXX,XX @@ typedef enum BlockJobCreateFlags {
137
BLOCK_JOB_DEFAULT = 0x00,
138
/* BlockJob is not QMP-created and should not send QMP events */
139
BLOCK_JOB_INTERNAL = 0x01,
140
+ /* BlockJob requires manual finalize step */
141
+ BLOCK_JOB_MANUAL_FINALIZE = 0x02,
142
/* BlockJob requires manual dismiss step */
143
BLOCK_JOB_MANUAL_DISMISS = 0x04,
144
} BlockJobCreateFlags;
145
diff --git a/blockjob.c b/blockjob.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/blockjob.c
148
+++ b/blockjob.c
149
@@ -XXX,XX +XXX,XX @@ static QemuMutex block_job_mutex;
150
151
/* BlockJob State Transition Table */
152
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
153
- /* U, C, R, P, Y, S, W, X, E, N */
154
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
155
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 1, 0, 1},
156
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1, 0, 0},
157
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
158
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1, 0, 0},
159
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
160
- /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
161
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
162
- /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
163
- /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
164
+ /* U, C, R, P, Y, S, W, D, X, E, N */
165
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
166
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
167
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
168
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
169
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
170
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
171
+ /* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
172
+ /* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
173
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
174
+ /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
175
+ /* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
176
};
83
};
177
84
178
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
85
@@ -XXX,XX +XXX,XX @@ const BdrvChildRole child_format = {
179
- /* U, C, R, P, Y, S, W, X, E, N */
86
.inherit_options = bdrv_inherited_fmt_options,
180
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 0, 0, 0},
87
.drained_begin = bdrv_child_cb_drained_begin,
181
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
88
.drained_end = bdrv_child_cb_drained_end,
182
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
89
+ .attach = bdrv_child_cb_attach,
183
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0},
90
+ .detach = bdrv_child_cb_detach,
184
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0},
91
.inactivate = bdrv_child_cb_inactivate,
185
- [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
186
+ /* U, C, R, P, Y, S, W, D, X, E, N */
187
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
188
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
189
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
190
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
191
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
192
+ [BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
193
};
92
};
194
93
195
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
94
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_attach(BdrvChild *c)
196
@@ -XXX,XX +XXX,XX @@ static void __attribute__((__constructor__)) block_job_init(void)
95
parent->backing_blocker);
197
96
bdrv_op_unblock(backing_hd, BLOCK_OP_TYPE_BACKUP_TARGET,
198
static void block_job_event_cancelled(BlockJob *job);
97
parent->backing_blocker);
199
static void block_job_event_completed(BlockJob *job, const char *msg);
98
+
200
+static int block_job_event_pending(BlockJob *job);
99
+ bdrv_child_cb_attach(c);
201
static void block_job_enter_cond(BlockJob *job, bool(*fn)(BlockJob *job));
100
}
202
101
203
/* Transactional group of block jobs */
102
static void bdrv_backing_detach(BdrvChild *c)
204
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job)
103
@@ -XXX,XX +XXX,XX @@ static void bdrv_backing_detach(BdrvChild *c)
205
job->cancelled = true;
104
bdrv_op_unblock_all(c->bs, parent->backing_blocker);
206
}
105
error_free(parent->backing_blocker);
207
106
parent->backing_blocker = NULL;
208
-static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *))
107
+
209
+static int block_job_txn_apply(BlockJobTxn *txn, int fn(BlockJob *), bool lock)
108
+ bdrv_child_cb_detach(c);
109
}
110
111
/*
112
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
113
assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs));
114
}
115
if (old_bs) {
116
+ /* Detach first so that the recursive drain sections coming from @child
117
+ * are already gone and we only end the drain sections that came from
118
+ * elsewhere. */
119
+ if (child->role->detach) {
120
+ child->role->detach(child);
121
+ }
122
if (old_bs->quiesce_counter && child->role->drained_end) {
123
for (i = 0; i < old_bs->quiesce_counter; i++) {
124
child->role->drained_end(child);
125
}
126
}
127
- if (child->role->detach) {
128
- child->role->detach(child);
129
- }
130
QLIST_REMOVE(child, next_parent);
131
}
132
133
@@ -XXX,XX +XXX,XX @@ static void bdrv_replace_child_noperm(BdrvChild *child,
134
}
135
}
136
137
+ /* Attach only after starting new drained sections, so that recursive
138
+ * drain sections coming from @child don't get an extra .drained_begin
139
+ * callback. */
140
if (child->role->attach) {
141
child->role->attach(child);
142
}
143
diff --git a/block/io.c b/block/io.c
144
index XXXXXXX..XXXXXXX 100644
145
--- a/block/io.c
146
+++ b/block/io.c
147
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn bdrv_co_yield_to_drain(BlockDriverState *bs,
148
assert(data.done);
149
}
150
151
-static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
152
- BdrvChild *parent)
153
+void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
154
+ BdrvChild *parent)
210
{
155
{
211
AioContext *ctx;
156
BdrvChild *child, *next;
212
BlockJob *job, *next;
157
213
int rc;
158
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_begin(BlockDriverState *bs, bool recursive,
214
159
bdrv_drain_recurse(bs);
215
QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) {
160
216
- ctx = blk_get_aio_context(job->blk);
161
if (recursive) {
217
- aio_context_acquire(ctx);
162
+ bs->recursive_quiesce_counter++;
218
+ if (lock) {
163
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
219
+ ctx = blk_get_aio_context(job->blk);
164
bdrv_do_drained_begin(child->bs, true, child);
220
+ aio_context_acquire(ctx);
165
}
221
+ }
166
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_begin(BlockDriverState *bs)
222
rc = fn(job);
167
bdrv_do_drained_begin(bs, true, NULL);
223
- aio_context_release(ctx);
168
}
224
+ if (lock) {
169
225
+ aio_context_release(ctx);
170
-static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
226
+ }
171
- BdrvChild *parent)
227
if (rc) {
172
+void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
228
break;
173
+ BdrvChild *parent)
229
}
174
{
230
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
175
BdrvChild *child, *next;
176
int old_quiesce_counter;
177
@@ -XXX,XX +XXX,XX @@ static void bdrv_do_drained_end(BlockDriverState *bs, bool recursive,
231
}
178
}
232
179
233
/* Jobs may require some prep-work to complete without failure */
180
if (recursive) {
234
- rc = block_job_txn_apply(txn, block_job_prepare);
181
+ bs->recursive_quiesce_counter--;
235
+ rc = block_job_txn_apply(txn, block_job_prepare, true);
182
QLIST_FOREACH_SAFE(child, &bs->children, next, next) {
236
if (rc) {
183
bdrv_do_drained_end(child->bs, true, child);
237
block_job_completed_txn_abort(job);
184
}
238
return;
185
@@ -XXX,XX +XXX,XX @@ void bdrv_subtree_drained_end(BlockDriverState *bs)
239
}
186
bdrv_do_drained_end(bs, true, NULL);
240
187
}
241
/* We are the last completed job, commit the transaction. */
188
242
- block_job_txn_apply(txn, block_job_completed_single);
189
+void bdrv_apply_subtree_drain(BdrvChild *child, BlockDriverState *new_parent)
243
+ block_job_txn_apply(txn, block_job_event_pending, false);
190
+{
244
+ block_job_txn_apply(txn, block_job_completed_single, true);
191
+ int i;
245
}
192
+
246
193
+ for (i = 0; i < new_parent->recursive_quiesce_counter; i++) {
247
/* Assumes the block_job_mutex is held */
194
+ bdrv_do_drained_begin(child->bs, true, child);
248
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
249
&error_abort);
250
}
251
252
+static int block_job_event_pending(BlockJob *job)
253
+{
254
+ block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
255
+ if (!job->auto_finalize && !block_job_is_internal(job)) {
256
+ qapi_event_send_block_job_pending(job->driver->job_type,
257
+ job->id,
258
+ &error_abort);
259
+ }
195
+ }
260
+ return 0;
196
+}
197
+
198
+void bdrv_unapply_subtree_drain(BdrvChild *child, BlockDriverState *old_parent)
199
+{
200
+ int i;
201
+
202
+ for (i = 0; i < old_parent->recursive_quiesce_counter; i++) {
203
+ bdrv_do_drained_end(child->bs, true, child);
204
+ }
261
+}
205
+}
262
+
206
+
263
/*
207
/*
264
* API for block job drivers and the block layer. These functions are
208
* Wait for pending requests to complete on a single BlockDriverState subtree,
265
* declared in blockjob_int.h.
209
* and suspend block driver's internal I/O until next request arrives.
266
@@ -XXX,XX +XXX,XX @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
267
job->paused = true;
268
job->pause_count = 1;
269
job->refcnt = 1;
270
+ job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
271
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
272
block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
273
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
274
--
210
--
275
2.13.6
211
2.13.6
276
212
277
213
diff view generated by jsdifflib
1
From: John Snow <jsnow@redhat.com>
2
3
add a new state "CONCLUDED" that identifies a job that has ceased all
4
operations. The wording was chosen to avoid any phrasing that might
5
imply success, error, or cancellation. The task has simply ceased all
6
operation and can never again perform any work.
7
8
("finished", "done", and "completed" might all imply success.)
9
10
Transitions:
11
Running -> Concluded: normal completion
12
Ready -> Concluded: normal completion
13
Aborting -> Concluded: error and cancellations
14
15
Verbs:
16
None as of this commit. (a future commit adds 'dismiss')
17
18
+---------+
19
|UNDEFINED|
20
+--+------+
21
|
22
+--v----+
23
+---------+CREATED|
24
| +--+----+
25
| |
26
| +--v----+ +------+
27
+---------+RUNNING<----->PAUSED|
28
| +--+-+--+ +------+
29
| | |
30
| | +------------------+
31
| | |
32
| +--v--+ +-------+ |
33
+---------+READY<------->STANDBY| |
34
| +--+--+ +-------+ |
35
| | |
36
+--v-----+ +--v------+ |
37
|ABORTING+--->CONCLUDED<-------------+
38
+--------+ +---------+
39
40
Signed-off-by: John Snow <jsnow@redhat.com>
41
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
1
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
42
---
2
---
43
qapi/block-core.json | 7 +++++--
3
tests/test-bdrv-drain.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++
44
blockjob.c | 39 ++++++++++++++++++++++++---------------
4
1 file changed, 80 insertions(+)
45
2 files changed, 29 insertions(+), 17 deletions(-)
46
5
47
diff --git a/qapi/block-core.json b/qapi/block-core.json
6
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
48
index XXXXXXX..XXXXXXX 100644
7
index XXXXXXX..XXXXXXX 100644
49
--- a/qapi/block-core.json
8
--- a/tests/test-bdrv-drain.c
50
+++ b/qapi/block-core.json
9
+++ b/tests/test-bdrv-drain.c
51
@@ -XXX,XX +XXX,XX @@
10
@@ -XXX,XX +XXX,XX @@ static void test_multiparent(void)
52
# The job may return to @ready or otherwise be canceled.
11
blk_unref(blk_b);
53
#
54
# @aborting: The job is in the process of being aborted, and will finish with
55
-# an error.
56
+# an error. The job will afterwards report that it is @concluded.
57
# This status may not be visible to the management process.
58
#
59
+# @concluded: The job has finished all work. If manual was set to true, the job
60
+# will remain in the query list until it is dismissed.
61
+#
62
# Since: 2.12
63
##
64
{ 'enum': 'BlockJobStatus',
65
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
66
- 'aborting' ] }
67
+ 'aborting', 'concluded' ] }
68
69
##
70
# @BlockJobInfo:
71
diff --git a/blockjob.c b/blockjob.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/blockjob.c
74
+++ b/blockjob.c
75
@@ -XXX,XX +XXX,XX @@ static QemuMutex block_job_mutex;
76
77
/* BlockJob State Transition Table */
78
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
79
- /* U, C, R, P, Y, S, X */
80
- /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0},
81
- /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1},
82
- /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1},
83
- /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0},
84
- /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1},
85
- /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0},
86
- /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0},
87
+ /* U, C, R, P, Y, S, X, E */
88
+ /* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0},
89
+ /* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 1, 0},
90
+ /* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 1},
91
+ /* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0},
92
+ /* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 1},
93
+ /* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0},
94
+ /* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 1},
95
+ /* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0},
96
};
97
98
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
99
- /* U, C, R, P, Y, S, X */
100
- [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0},
101
- [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0},
102
- [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0},
103
- [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0},
104
- [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0},
105
+ /* U, C, R, P, Y, S, X, E */
106
+ [BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 0, 0},
107
+ [BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0},
108
+ [BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0},
109
+ [BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0},
110
+ [BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0},
111
};
112
113
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
114
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
115
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
116
}
12
}
117
13
118
+static void block_job_conclude(BlockJob *job)
14
+static void test_graph_change(void)
119
+{
15
+{
120
+ block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
16
+ BlockBackend *blk_a, *blk_b;
17
+ BlockDriverState *bs_a, *bs_b, *backing;
18
+ BDRVTestState *a_s, *b_s, *backing_s;
19
+
20
+ blk_a = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
21
+ bs_a = bdrv_new_open_driver(&bdrv_test, "test-node-a", BDRV_O_RDWR,
22
+ &error_abort);
23
+ a_s = bs_a->opaque;
24
+ blk_insert_bs(blk_a, bs_a, &error_abort);
25
+
26
+ blk_b = blk_new(BLK_PERM_ALL, BLK_PERM_ALL);
27
+ bs_b = bdrv_new_open_driver(&bdrv_test, "test-node-b", BDRV_O_RDWR,
28
+ &error_abort);
29
+ b_s = bs_b->opaque;
30
+ blk_insert_bs(blk_b, bs_b, &error_abort);
31
+
32
+ backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort);
33
+ backing_s = backing->opaque;
34
+ bdrv_set_backing_hd(bs_a, backing, &error_abort);
35
+
36
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
37
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
38
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
39
+ g_assert_cmpint(a_s->drain_count, ==, 0);
40
+ g_assert_cmpint(b_s->drain_count, ==, 0);
41
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
42
+
43
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
44
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
45
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_a);
46
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
47
+ do_drain_begin(BDRV_SUBTREE_DRAIN, bs_b);
48
+
49
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
50
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
51
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
52
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
53
+ g_assert_cmpint(a_s->drain_count, ==, 5);
54
+ g_assert_cmpint(b_s->drain_count, ==, 5);
55
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
56
+
57
+ bdrv_set_backing_hd(bs_b, NULL, &error_abort);
58
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 3);
59
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 2);
60
+ g_assert_cmpint(backing->quiesce_counter, ==, 3);
61
+ g_assert_cmpint(a_s->drain_count, ==, 3);
62
+ g_assert_cmpint(b_s->drain_count, ==, 2);
63
+ g_assert_cmpint(backing_s->drain_count, ==, 3);
64
+
65
+ bdrv_set_backing_hd(bs_b, backing, &error_abort);
66
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 5);
67
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 5);
68
+ g_assert_cmpint(backing->quiesce_counter, ==, 5);
69
+ g_assert_cmpint(a_s->drain_count, ==, 5);
70
+ g_assert_cmpint(b_s->drain_count, ==, 5);
71
+ g_assert_cmpint(backing_s->drain_count, ==, 5);
72
+
73
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
74
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_b);
75
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
76
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
77
+ do_drain_end(BDRV_SUBTREE_DRAIN, bs_a);
78
+
79
+ g_assert_cmpint(bs_a->quiesce_counter, ==, 0);
80
+ g_assert_cmpint(bs_b->quiesce_counter, ==, 0);
81
+ g_assert_cmpint(backing->quiesce_counter, ==, 0);
82
+ g_assert_cmpint(a_s->drain_count, ==, 0);
83
+ g_assert_cmpint(b_s->drain_count, ==, 0);
84
+ g_assert_cmpint(backing_s->drain_count, ==, 0);
85
+
86
+ bdrv_unref(backing);
87
+ bdrv_unref(bs_a);
88
+ bdrv_unref(bs_b);
89
+ blk_unref(blk_a);
90
+ blk_unref(blk_b);
121
+}
91
+}
122
+
92
+
123
static void block_job_completed_single(BlockJob *job)
93
124
{
94
typedef struct TestBlockJob {
125
assert(job->completed);
95
BlockJob common;
126
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
96
@@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv)
127
97
128
QLIST_REMOVE(job, txn_list);
98
g_test_add_func("/bdrv-drain/nested", test_nested);
129
block_job_txn_unref(job->txn);
99
g_test_add_func("/bdrv-drain/multiparent", test_multiparent);
130
+ block_job_conclude(job);
100
+ g_test_add_func("/bdrv-drain/graph-change", test_graph_change);
131
block_job_unref(job);
101
132
}
102
g_test_add_func("/bdrv-drain/blockjob/drain_all", test_blockjob_drain_all);
133
103
g_test_add_func("/bdrv-drain/blockjob/drain", test_blockjob_drain);
134
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job, Error **errp)
135
136
void block_job_cancel(BlockJob *job)
137
{
138
- if (block_job_started(job)) {
139
+ if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
140
+ return;
141
+ } else if (block_job_started(job)) {
142
block_job_cancel_async(job);
143
block_job_enter(job);
144
} else {
145
--
104
--
146
2.13.6
105
2.13.6
147
106
148
107
diff view generated by jsdifflib
1
The crypto driver used to create the image file in a callback from the
1
Since commit bde70715, base is the only node that is reopened in
2
crypto subsystem. If we want to implement .bdrv_co_create, this needs to
2
commit_start(). This means that the code, which still involves an
3
go away because that callback will get a reference to an already
3
explicit BlockReopenQueue, can now be simplified by using bdrv_reopen().
4
existing block node.
5
6
Move the image file creation to block_crypto_create_generic().
7
4
8
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
6
Reviewed-by: Fam Zheng <famz@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
---
7
---
12
block/crypto.c | 37 +++++++++++++++++--------------------
8
block/commit.c | 8 +-------
13
1 file changed, 17 insertions(+), 20 deletions(-)
9
1 file changed, 1 insertion(+), 7 deletions(-)
14
10
15
diff --git a/block/crypto.c b/block/crypto.c
11
diff --git a/block/commit.c b/block/commit.c
16
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
17
--- a/block/crypto.c
13
--- a/block/commit.c
18
+++ b/block/crypto.c
14
+++ b/block/commit.c
19
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_read_func(QCryptoBlock *block,
15
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
20
16
const char *filter_node_name, Error **errp)
21
22
struct BlockCryptoCreateData {
23
- const char *filename;
24
- QemuOpts *opts;
25
BlockBackend *blk;
26
uint64_t size;
27
};
28
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
29
Error **errp)
30
{
17
{
31
struct BlockCryptoCreateData *data = opaque;
18
CommitBlockJob *s;
32
- int ret;
19
- BlockReopenQueue *reopen_queue = NULL;
33
20
int orig_base_flags;
34
/* User provided size should reflect amount of space made
21
BlockDriverState *iter;
35
* available to the guest, so we must take account of that
22
BlockDriverState *commit_top_bs = NULL;
36
* which will be used by the crypto header
23
@@ -XXX,XX +XXX,XX @@ void commit_start(const char *job_id, BlockDriverState *bs,
37
*/
24
/* convert base to r/w, if necessary */
38
- data->size += headerlen;
25
orig_base_flags = bdrv_get_flags(base);
39
-
26
if (!(orig_base_flags & BDRV_O_RDWR)) {
40
- qemu_opt_set_number(data->opts, BLOCK_OPT_SIZE, data->size, &error_abort);
27
- reopen_queue = bdrv_reopen_queue(reopen_queue, base, NULL,
41
- ret = bdrv_create_file(data->filename, data->opts, errp);
28
- orig_base_flags | BDRV_O_RDWR);
42
- if (ret < 0) {
43
- return -1;
44
- }
29
- }
45
-
30
-
46
- data->blk = blk_new_open(data->filename, NULL, NULL,
31
- if (reopen_queue) {
47
- BDRV_O_RDWR | BDRV_O_PROTOCOL, errp);
32
- bdrv_reopen_multiple(bdrv_get_aio_context(bs), reopen_queue, &local_err);
48
- if (!data->blk) {
33
+ bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err);
49
- return -1;
34
if (local_err != NULL) {
50
- }
35
error_propagate(errp, local_err);
51
-
36
goto fail;
52
- return 0;
53
+ return blk_truncate(data->blk, data->size + headerlen, PREALLOC_MODE_OFF,
54
+ errp);
55
}
56
57
58
@@ -XXX,XX +XXX,XX @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
59
struct BlockCryptoCreateData data = {
60
.size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
61
BDRV_SECTOR_SIZE),
62
- .opts = opts,
63
- .filename = filename,
64
};
65
QDict *cryptoopts;
66
67
+ /* Parse options */
68
cryptoopts = qemu_opts_to_qdict(opts, NULL);
69
70
create_opts = block_crypto_create_opts_init(format, cryptoopts, errp);
71
@@ -XXX,XX +XXX,XX @@ static int block_crypto_create_generic(QCryptoBlockFormat format,
72
return -1;
73
}
74
75
+ /* Create protocol layer */
76
+ ret = bdrv_create_file(filename, opts, errp);
77
+ if (ret < 0) {
78
+ return ret;
79
+ }
80
+
81
+ data.blk = blk_new_open(filename, NULL, NULL,
82
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
83
+ errp);
84
+ if (!data.blk) {
85
+ return -EINVAL;
86
+ }
87
+
88
+ /* Create format layer */
89
crypto = qcrypto_block_create(create_opts, NULL,
90
block_crypto_init_func,
91
block_crypto_write_func,
92
--
37
--
93
2.13.6
38
2.13.6
94
39
95
40
diff view generated by jsdifflib
Deleted patch
1
The .bdrv_getlength implementation of the crypto block driver asserted
2
that the payload offset isn't after EOF. This is an invalid assertion to
3
make as the image file could be corrupted. Instead, check it and return
4
-EIO if the file is too small for the payload offset.
5
1
6
Zero length images are fine, so trigger -EIO only on offset > len, not
7
on offset >= len as the assertion did before.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
11
---
12
block/crypto.c | 5 ++++-
13
1 file changed, 4 insertions(+), 1 deletion(-)
14
15
diff --git a/block/crypto.c b/block/crypto.c
16
index XXXXXXX..XXXXXXX 100644
17
--- a/block/crypto.c
18
+++ b/block/crypto.c
19
@@ -XXX,XX +XXX,XX @@ static int64_t block_crypto_getlength(BlockDriverState *bs)
20
21
uint64_t offset = qcrypto_block_get_payload_offset(crypto->block);
22
assert(offset < INT64_MAX);
23
- assert(offset < len);
24
+
25
+ if (offset > len) {
26
+ return -EIO;
27
+ }
28
29
len -= offset;
30
31
--
32
2.13.6
33
34
diff view generated by jsdifflib
Deleted patch
1
When you request an image size close to UINT64_MAX, the addition of the
2
crypto header may cause an integer overflow. Catch it instead of
3
silently truncating the image size.
4
1
5
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Daniel P. Berrangé <berrange@redhat.com>
7
---
8
block/crypto.c | 5 +++++
9
1 file changed, 5 insertions(+)
10
11
diff --git a/block/crypto.c b/block/crypto.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/block/crypto.c
14
+++ b/block/crypto.c
15
@@ -XXX,XX +XXX,XX @@ static ssize_t block_crypto_init_func(QCryptoBlock *block,
16
{
17
struct BlockCryptoCreateData *data = opaque;
18
19
+ if (data->size > INT64_MAX || headerlen > INT64_MAX - data->size) {
20
+ error_setg(errp, "The requested file size is too large");
21
+ return -EFBIG;
22
+ }
23
+
24
/* User provided size should reflect amount of space made
25
* available to the guest, so we must take account of that
26
* which will be used by the crypto header
27
--
28
2.13.6
29
30
diff view generated by jsdifflib
Deleted patch
1
Originally we added parallels as a read-only format to qemu-iotests
2
where we did just some tests with a binary image. Since then, write and
3
image creation support has been added to the driver, so we can now
4
enable it in _supported_fmt generic.
5
1
6
The driver doesn't support migration yet, though, so we need to add it
7
to the list of exceptions in 181.
8
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
10
Reviewed-by: Max Reitz <mreitz@redhat.com>
11
Reviewed-by: Jeff Cody <jcody@redhat.com>
12
---
13
tests/qemu-iotests/181 | 2 +-
14
tests/qemu-iotests/check | 1 -
15
2 files changed, 1 insertion(+), 2 deletions(-)
16
17
diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181
18
index XXXXXXX..XXXXXXX 100755
19
--- a/tests/qemu-iotests/181
20
+++ b/tests/qemu-iotests/181
21
@@ -XXX,XX +XXX,XX @@ trap "_cleanup; exit \$status" 0 1 2 3 15
22
23
_supported_fmt generic
24
# Formats that do not support live migration
25
-_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat
26
+_unsupported_fmt qcow vdi vhdx vmdk vpc vvfat parallels
27
_supported_proto generic
28
_supported_os Linux
29
30
diff --git a/tests/qemu-iotests/check b/tests/qemu-iotests/check
31
index XXXXXXX..XXXXXXX 100755
32
--- a/tests/qemu-iotests/check
33
+++ b/tests/qemu-iotests/check
34
@@ -XXX,XX +XXX,XX @@ testlist options
35
36
-parallels)
37
IMGFMT=parallels
38
- IMGFMT_GENERIC=false
39
xpand=false
40
;;
41
42
--
43
2.13.6
44
45
diff view generated by jsdifflib
1
This adds the .bdrv_co_create driver callback to vpc, which
1
The bdrv_reopen*() implementation doesn't like it if the graph is
2
enables image creation over QMP.
2
changed between queuing nodes for reopen and actually reopening them
3
(one of the reasons is that queuing can be recursive).
4
5
So instead of draining the device only in bdrv_reopen_multiple(),
6
require that callers already drained all affected nodes, and assert this
7
in bdrv_reopen_queue().
3
8
4
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
9
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Fam Zheng <famz@redhat.com>
6
---
11
---
7
qapi/block-core.json | 33 ++++++++++-
12
block.c | 23 ++++++++++++++++-------
8
block/vpc.c | 152 ++++++++++++++++++++++++++++++++++++++-------------
13
block/replication.c | 6 ++++++
9
2 files changed, 147 insertions(+), 38 deletions(-)
14
qemu-io-cmds.c | 3 +++
15
3 files changed, 25 insertions(+), 7 deletions(-)
10
16
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
17
diff --git a/block.c b/block.c
12
index XXXXXXX..XXXXXXX 100644
18
index XXXXXXX..XXXXXXX 100644
13
--- a/qapi/block-core.json
19
--- a/block.c
14
+++ b/qapi/block-core.json
20
+++ b/block.c
15
@@ -XXX,XX +XXX,XX @@
21
@@ -XXX,XX +XXX,XX @@ BlockDriverState *bdrv_open(const char *filename, const char *reference,
16
'*block-state-zero': 'bool' } }
22
* returns a pointer to bs_queue, which is either the newly allocated
17
23
* bs_queue, or the existing bs_queue being used.
18
##
24
*
19
+# @BlockdevVpcSubformat:
25
+ * bs must be drained between bdrv_reopen_queue() and bdrv_reopen_multiple().
20
+#
26
*/
21
+# @dynamic: Growing image file
27
static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
22
+# @fixed: Preallocated fixed-size image file
28
BlockDriverState *bs,
23
+#
29
@@ -XXX,XX +XXX,XX @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue,
24
+# Since: 2.12
30
BdrvChild *child;
25
+##
31
QDict *old_options, *explicit_options;
26
+{ 'enum': 'BlockdevVpcSubformat',
32
27
+ 'data': [ 'dynamic', 'fixed' ] }
33
+ /* Make sure that the caller remembered to use a drained section. This is
34
+ * important to avoid graph changes between the recursive queuing here and
35
+ * bdrv_reopen_multiple(). */
36
+ assert(bs->quiesce_counter > 0);
28
+
37
+
29
+##
38
if (bs_queue == NULL) {
30
+# @BlockdevCreateOptionsVpc:
39
bs_queue = g_new0(BlockReopenQueue, 1);
31
+#
40
QSIMPLEQ_INIT(bs_queue);
32
+# Driver specific image creation options for vpc (VHD).
41
@@ -XXX,XX +XXX,XX @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue,
33
+#
42
* If all devices prepare successfully, then the changes are committed
34
+# @file Node to create the image format on
43
* to all devices.
35
+# @size Size of the virtual disk in bytes
44
*
36
+# @subformat vhdx subformat (default: dynamic)
45
+ * All affected nodes must be drained between bdrv_reopen_queue() and
37
+# @force-size Force use of the exact byte size instead of rounding to the
46
+ * bdrv_reopen_multiple().
38
+# next size that can be represented in CHS geometry
47
*/
39
+# (default: false)
48
int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp)
40
+#
49
{
41
+# Since: 2.12
50
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er
42
+##
51
43
+{ 'struct': 'BlockdevCreateOptionsVpc',
52
assert(bs_queue != NULL);
44
+ 'data': { 'file': 'BlockdevRef',
53
45
+ 'size': 'size',
54
- aio_context_release(ctx);
46
+ '*subformat': 'BlockdevVpcSubformat',
55
- bdrv_drain_all_begin();
47
+ '*force-size': 'bool' } }
56
- aio_context_acquire(ctx);
48
+
57
-
49
+##
58
QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) {
50
# @BlockdevCreateNotSupported:
59
+ assert(bs_entry->state.bs->quiesce_counter > 0);
51
#
60
if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) {
52
# This is used for all drivers that don't support creating images.
61
error_propagate(errp, local_err);
53
@@ -XXX,XX +XXX,XX @@
62
goto cleanup;
54
'vdi': 'BlockdevCreateOptionsVdi',
63
@@ -XXX,XX +XXX,XX @@ cleanup:
55
'vhdx': 'BlockdevCreateOptionsVhdx',
56
'vmdk': 'BlockdevCreateNotSupported',
57
- 'vpc': 'BlockdevCreateNotSupported',
58
+ 'vpc': 'BlockdevCreateOptionsVpc',
59
'vvfat': 'BlockdevCreateNotSupported',
60
'vxhs': 'BlockdevCreateNotSupported'
61
} }
62
diff --git a/block/vpc.c b/block/vpc.c
63
index XXXXXXX..XXXXXXX 100644
64
--- a/block/vpc.c
65
+++ b/block/vpc.c
66
@@ -XXX,XX +XXX,XX @@
67
#include "migration/blocker.h"
68
#include "qemu/bswap.h"
69
#include "qemu/uuid.h"
70
+#include "qapi/qmp/qdict.h"
71
+#include "qapi/qobject-input-visitor.h"
72
+#include "qapi/qapi-visit-block-core.h"
73
74
/**************************************************************/
75
76
@@ -XXX,XX +XXX,XX @@ static QemuOptsList vpc_runtime_opts = {
77
}
64
}
78
};
65
g_free(bs_queue);
79
66
80
+static QemuOptsList vpc_create_opts;
67
- bdrv_drain_all_end();
81
+
68
-
82
static uint32_t vpc_checksum(uint8_t* buf, size_t size)
83
{
84
uint32_t res = 0;
85
@@ -XXX,XX +XXX,XX @@ static int create_fixed_disk(BlockBackend *blk, uint8_t *buf,
86
return ret;
69
return ret;
87
}
70
}
88
71
89
-static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
72
@@ -XXX,XX +XXX,XX @@ int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp)
90
- Error **errp)
91
+static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts,
92
+ Error **errp)
93
{
73
{
94
+ BlockdevCreateOptionsVpc *vpc_opts;
74
int ret = -1;
95
+ BlockBackend *blk = NULL;
75
Error *local_err = NULL;
96
+ BlockDriverState *bs = NULL;
76
- BlockReopenQueue *queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
77
+ BlockReopenQueue *queue;
78
79
+ bdrv_subtree_drained_begin(bs);
97
+
80
+
98
uint8_t buf[1024];
81
+ queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags);
99
VHDFooter *footer = (VHDFooter *) buf;
82
ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err);
100
- char *disk_type_param;
83
if (local_err != NULL) {
101
int i;
84
error_propagate(errp, local_err);
102
uint16_t cyls = 0;
85
}
103
uint8_t heads = 0;
104
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
105
int64_t total_size;
106
int disk_type;
107
int ret = -EIO;
108
- bool force_size;
109
- Error *local_err = NULL;
110
- BlockBackend *blk = NULL;
111
112
- /* Read out options */
113
- total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0),
114
- BDRV_SECTOR_SIZE);
115
- disk_type_param = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT);
116
- if (disk_type_param) {
117
- if (!strcmp(disk_type_param, "dynamic")) {
118
- disk_type = VHD_DYNAMIC;
119
- } else if (!strcmp(disk_type_param, "fixed")) {
120
- disk_type = VHD_FIXED;
121
- } else {
122
- error_setg(errp, "Invalid disk type, %s", disk_type_param);
123
- ret = -EINVAL;
124
- goto out;
125
- }
126
- } else {
127
+ assert(opts->driver == BLOCKDEV_DRIVER_VPC);
128
+ vpc_opts = &opts->u.vpc;
129
+
86
+
130
+ /* Validate options and set default values */
87
+ bdrv_subtree_drained_end(bs);
131
+ total_size = vpc_opts->size;
132
+
88
+
133
+ if (!vpc_opts->has_subformat) {
134
+ vpc_opts->subformat = BLOCKDEV_VPC_SUBFORMAT_DYNAMIC;
135
+ }
136
+ switch (vpc_opts->subformat) {
137
+ case BLOCKDEV_VPC_SUBFORMAT_DYNAMIC:
138
disk_type = VHD_DYNAMIC;
139
+ break;
140
+ case BLOCKDEV_VPC_SUBFORMAT_FIXED:
141
+ disk_type = VHD_FIXED;
142
+ break;
143
+ default:
144
+ g_assert_not_reached();
145
}
146
147
- force_size = qemu_opt_get_bool_del(opts, VPC_OPT_FORCE_SIZE, false);
148
-
149
- ret = bdrv_create_file(filename, opts, &local_err);
150
- if (ret < 0) {
151
- error_propagate(errp, local_err);
152
- goto out;
153
+ /* Create BlockBackend to write to the image */
154
+ bs = bdrv_open_blockdev_ref(vpc_opts->file, errp);
155
+ if (bs == NULL) {
156
+ return -EIO;
157
}
158
159
- blk = blk_new_open(filename, NULL, NULL,
160
- BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL,
161
- &local_err);
162
- if (blk == NULL) {
163
- error_propagate(errp, local_err);
164
- ret = -EIO;
165
+ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL);
166
+ ret = blk_insert_bs(blk, bs, errp);
167
+ if (ret < 0) {
168
goto out;
169
}
170
-
171
blk_set_allow_write_beyond_eof(blk, true);
172
173
/*
174
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
175
* we set the geometry to 65535 x 16 x 255 (CxHxS) sectors and use
176
* the image size from the VHD footer to calculate total_sectors.
177
*/
178
- if (force_size) {
179
+ if (vpc_opts->force_size) {
180
/* This will force the use of total_size for sector count, below */
181
cyls = VHD_CHS_MAX_C;
182
heads = VHD_CHS_MAX_H;
183
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
184
memset(buf, 0, 1024);
185
186
memcpy(footer->creator, "conectix", 8);
187
- if (force_size) {
188
+ if (vpc_opts->force_size) {
189
memcpy(footer->creator_app, "qem2", 4);
190
} else {
191
memcpy(footer->creator_app, "qemu", 4);
192
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn vpc_co_create_opts(const char *filename, QemuOpts *opts,
193
194
out:
195
blk_unref(blk);
196
- g_free(disk_type_param);
197
+ bdrv_unref(bs);
198
+ return ret;
199
+}
200
+
201
+static int coroutine_fn vpc_co_create_opts(const char *filename,
202
+ QemuOpts *opts, Error **errp)
203
+{
204
+ BlockdevCreateOptions *create_options = NULL;
205
+ QDict *qdict = NULL;
206
+ QObject *qobj;
207
+ Visitor *v;
208
+ BlockDriverState *bs = NULL;
209
+ Error *local_err = NULL;
210
+ int ret;
211
+
212
+ static const QDictRenames opt_renames[] = {
213
+ { VPC_OPT_FORCE_SIZE, "force-size" },
214
+ { NULL, NULL },
215
+ };
216
+
217
+ /* Parse options and convert legacy syntax */
218
+ qdict = qemu_opts_to_qdict_filtered(opts, NULL, &vpc_create_opts, true);
219
+
220
+ if (!qdict_rename_keys(qdict, opt_renames, errp)) {
221
+ ret = -EINVAL;
222
+ goto fail;
223
+ }
224
+
225
+ /* Create and open the file (protocol layer) */
226
+ ret = bdrv_create_file(filename, opts, &local_err);
227
+ if (ret < 0) {
228
+ error_propagate(errp, local_err);
229
+ goto fail;
230
+ }
231
+
232
+ bs = bdrv_open(filename, NULL, NULL,
233
+ BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, errp);
234
+ if (bs == NULL) {
235
+ ret = -EIO;
236
+ goto fail;
237
+ }
238
+
239
+ /* Now get the QAPI type BlockdevCreateOptions */
240
+ qdict_put_str(qdict, "driver", "vpc");
241
+ qdict_put_str(qdict, "file", bs->node_name);
242
+
243
+ qobj = qdict_crumple(qdict, errp);
244
+ QDECREF(qdict);
245
+ qdict = qobject_to_qdict(qobj);
246
+ if (qdict == NULL) {
247
+ ret = -EINVAL;
248
+ goto fail;
249
+ }
250
+
251
+ v = qobject_input_visitor_new_keyval(QOBJECT(qdict));
252
+ visit_type_BlockdevCreateOptions(v, NULL, &create_options, &local_err);
253
+ visit_free(v);
254
+
255
+ if (local_err) {
256
+ error_propagate(errp, local_err);
257
+ ret = -EINVAL;
258
+ goto fail;
259
+ }
260
+
261
+ /* Silently round up size */
262
+ assert(create_options->driver == BLOCKDEV_DRIVER_VPC);
263
+ create_options->u.vpc.size =
264
+ ROUND_UP(create_options->u.vpc.size, BDRV_SECTOR_SIZE);
265
+
266
+ /* Create the vpc image (format layer) */
267
+ ret = vpc_co_create(create_options, errp);
268
+
269
+fail:
270
+ QDECREF(qdict);
271
+ bdrv_unref(bs);
272
+ qapi_free_BlockdevCreateOptions(create_options);
273
return ret;
89
return ret;
274
}
90
}
275
91
92
diff --git a/block/replication.c b/block/replication.c
93
index XXXXXXX..XXXXXXX 100644
94
--- a/block/replication.c
95
+++ b/block/replication.c
96
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
97
new_secondary_flags = s->orig_secondary_flags;
98
}
99
100
+ bdrv_subtree_drained_begin(s->hidden_disk->bs);
101
+ bdrv_subtree_drained_begin(s->secondary_disk->bs);
276
+
102
+
277
static int vpc_has_zero_init(BlockDriverState *bs)
103
if (orig_hidden_flags != new_hidden_flags) {
278
{
104
reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL,
279
BDRVVPCState *s = bs->opaque;
105
new_hidden_flags);
280
@@ -XXX,XX +XXX,XX @@ static BlockDriver bdrv_vpc = {
106
@@ -XXX,XX +XXX,XX @@ static void reopen_backing_file(BlockDriverState *bs, bool writable,
281
.bdrv_close = vpc_close,
107
reopen_queue, &local_err);
282
.bdrv_reopen_prepare = vpc_reopen_prepare,
108
error_propagate(errp, local_err);
283
.bdrv_child_perm = bdrv_format_default_perms,
109
}
284
+ .bdrv_co_create = vpc_co_create,
110
+
285
.bdrv_co_create_opts = vpc_co_create_opts,
111
+ bdrv_subtree_drained_end(s->hidden_disk->bs);
286
112
+ bdrv_subtree_drained_end(s->secondary_disk->bs);
287
.bdrv_co_preadv = vpc_co_preadv,
113
}
114
115
static void backup_job_cleanup(BlockDriverState *bs)
116
diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c
117
index XXXXXXX..XXXXXXX 100644
118
--- a/qemu-io-cmds.c
119
+++ b/qemu-io-cmds.c
120
@@ -XXX,XX +XXX,XX @@ static int reopen_f(BlockBackend *blk, int argc, char **argv)
121
opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL;
122
qemu_opts_reset(&reopen_opts);
123
124
+ bdrv_subtree_drained_begin(bs);
125
brq = bdrv_reopen_queue(NULL, bs, opts, flags);
126
bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err);
127
+ bdrv_subtree_drained_end(bs);
128
+
129
if (local_err) {
130
error_report_err(local_err);
131
} else {
288
--
132
--
289
2.13.6
133
2.13.6
290
134
291
135
diff view generated by jsdifflib