1
The following changes since commit 411ad78115ebeb3411cf4b7622784b93dfabe259:
1
The following changes since commit 19b599f7664b2ebfd0f405fb79c14dd241557452:
2
2
3
Merge remote-tracking branch 'remotes/stefanberger/tags/pull-tpm-2017-12-15-1' into staging (2017-12-17 15:27:41 +0000)
3
Merge remote-tracking branch 'remotes/armbru/tags/pull-error-2018-08-27-v2' into staging (2018-08-27 16:44:20 +0100)
4
4
5
are available in the Git repository at:
5
are available in the Git repository at:
6
6
7
git://github.com/stefanha/qemu.git tags/block-pull-request
7
https://git.xanclic.moe/XanClic/qemu.git tags/pull-block-2018-08-31
8
8
9
for you to fetch changes up to 585426c518958aa768564596091474be786aae51:
9
for you to fetch changes up to 40954cc7831c4f95f9ce6402ae3d6761f44f31ff:
10
10
11
qemu-iotests: add 203 savevm with IOThreads test (2017-12-18 13:12:53 +0000)
11
jobs: remove job_defer_to_main_loop (2018-08-31 16:11:27 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches:
15
- (Block) job exit refactoring, part 1
16
(removing job_defer_to_main_loop())
17
- Locking fix for the file-posix block driver
18
- test-bdrv-drain leak fix
14
19
15
----------------------------------------------------------------
20
----------------------------------------------------------------
21
Fam Zheng (1):
22
file-posix: Skip effectiveless OFD lock operations
16
23
17
Mao Zhongyi (4):
24
John Snow (9):
18
hw/block/nvme: Convert to realize
25
jobs: change start callback to run callback
19
hw/block: Fix the return type
26
jobs: canonize Error object
20
hw/block: Use errp directly rather than local_err
27
jobs: add exit shim
21
dev-storage: Fix the unusual function name
28
block/commit: utilize job_exit shim
29
block/mirror: utilize job_exit shim
30
jobs: utilize job_exit shim
31
block/backup: make function variables consistently named
32
jobs: remove ret argument to job_completed; privatize it
33
jobs: remove job_defer_to_main_loop
22
34
23
Mark Kanda (2):
35
Marc-André Lureau (1):
24
virtio-blk: make queue size configurable
36
tests: fix bdrv-drain leak
25
virtio-blk: reject configs with logical block size > physical block
26
size
27
37
28
Paolo Bonzini (1):
38
include/qemu/job.h | 70 ++++++++++++++++-----------------
29
block: avoid recursive AioContext acquire in bdrv_inactivate_all()
39
block/backup.c | 81 ++++++++++++++++-----------------------
30
40
block/commit.c | 29 +++++---------
31
Stefan Hajnoczi (16):
41
block/create.c | 19 +++------
32
coroutine: simplify co_aio_sleep_ns() prototype
42
block/file-posix.c | 41 +++++++++++++++-----
33
qdev: drop unused #include "sysemu/iothread.h"
43
block/mirror.c | 39 ++++++++-----------
34
blockdev: hold AioContext for bdrv_unref() in
44
block/stream.c | 29 ++++++--------
35
external_snapshot_clean()
45
job-qmp.c | 5 ++-
36
block: don't keep AioContext acquired after
46
job.c | 73 ++++++++++++-----------------------
37
external_snapshot_prepare()
47
tests/test-bdrv-drain.c | 14 +++----
38
block: don't keep AioContext acquired after drive_backup_prepare()
48
tests/test-blockjob-txn.c | 25 +++++-------
39
block: don't keep AioContext acquired after blockdev_backup_prepare()
49
tests/test-blockjob.c | 17 ++++----
40
block: don't keep AioContext acquired after
50
trace-events | 2 +-
41
internal_snapshot_prepare()
51
13 files changed, 192 insertions(+), 252 deletions(-)
42
block: drop unused BlockDirtyBitmapState->aio_context field
43
iothread: add iothread_by_id() API
44
blockdev: add x-blockdev-set-iothread testing command
45
qemu-iotests: add 202 external snapshots IOThread test
46
docs: mark nested AioContext locking as a legacy API
47
blockdev: add x-blockdev-set-iothread force boolean
48
iotests: add VM.add_object()
49
iothread: fix iothread_stop() race condition
50
qemu-iotests: add 203 savevm with IOThreads test
51
52
docs/devel/multiple-iothreads.txt | 7 +-
53
qapi/block-core.json | 40 ++++++
54
hw/block/dataplane/virtio-blk.h | 2 +-
55
include/hw/block/block.h | 4 +-
56
include/hw/virtio/virtio-blk.h | 1 +
57
include/qemu/coroutine.h | 6 +-
58
include/sysemu/iothread.h | 4 +-
59
block.c | 14 ++-
60
block/null.c | 3 +-
61
block/sheepdog.c | 3 +-
62
blockdev.c | 259 +++++++++++++++++++++++++++-----------
63
hw/block/block.c | 15 ++-
64
hw/block/dataplane/virtio-blk.c | 12 +-
65
hw/block/fdc.c | 17 +--
66
hw/block/nvme.c | 23 ++--
67
hw/block/virtio-blk.c | 35 ++++--
68
hw/core/qdev-properties-system.c | 1 -
69
hw/ide/qdev.c | 12 +-
70
hw/scsi/scsi-disk.c | 13 +-
71
hw/usb/dev-storage.c | 29 ++---
72
iothread.c | 27 +++-
73
util/qemu-coroutine-sleep.c | 4 +-
74
tests/qemu-iotests/202 | 95 ++++++++++++++
75
tests/qemu-iotests/202.out | 11 ++
76
tests/qemu-iotests/203 | 59 +++++++++
77
tests/qemu-iotests/203.out | 6 +
78
tests/qemu-iotests/group | 2 +
79
tests/qemu-iotests/iotests.py | 5 +
80
28 files changed, 532 insertions(+), 177 deletions(-)
81
create mode 100755 tests/qemu-iotests/202
82
create mode 100644 tests/qemu-iotests/202.out
83
create mode 100755 tests/qemu-iotests/203
84
create mode 100644 tests/qemu-iotests/203.out
85
52
86
--
53
--
87
2.14.3
54
2.17.1
88
55
89
56
diff view generated by jsdifflib
Deleted patch
1
The AioContext pointer argument to co_aio_sleep_ns() is only used for
2
the sleep timer. It does not affect where the caller coroutine is
3
resumed.
4
1
5
Due to changes to coroutine and AIO APIs it is now possible to drop the
6
AioContext pointer argument. This is safe to do since no caller has
7
specific requirements for which AioContext the timer must run in.
8
9
This patch drops the AioContext pointer argument and renames the
10
function to simplify the API.
11
12
Reported-by: Paolo Bonzini <pbonzini@redhat.com>
13
Reported-by: Eric Blake <eblake@redhat.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Reviewed-by: Eric Blake <eblake@redhat.com>
16
Message-id: 20171109102652.6360-1-stefanha@redhat.com
17
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
18
---
19
include/qemu/coroutine.h | 6 +-----
20
block/null.c | 3 +--
21
block/sheepdog.c | 3 +--
22
util/qemu-coroutine-sleep.c | 4 ++--
23
4 files changed, 5 insertions(+), 11 deletions(-)
24
25
diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h
26
index XXXXXXX..XXXXXXX 100644
27
--- a/include/qemu/coroutine.h
28
+++ b/include/qemu/coroutine.h
29
@@ -XXX,XX +XXX,XX @@ void qemu_co_rwlock_unlock(CoRwlock *lock);
30
31
/**
32
* Yield the coroutine for a given duration
33
- *
34
- * Behaves similarly to co_sleep_ns(), but the sleeping coroutine will be
35
- * resumed when using aio_poll().
36
*/
37
-void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
38
- int64_t ns);
39
+void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns);
40
41
/**
42
* Yield until a file descriptor becomes readable
43
diff --git a/block/null.c b/block/null.c
44
index XXXXXXX..XXXXXXX 100644
45
--- a/block/null.c
46
+++ b/block/null.c
47
@@ -XXX,XX +XXX,XX @@ static coroutine_fn int null_co_common(BlockDriverState *bs)
48
BDRVNullState *s = bs->opaque;
49
50
if (s->latency_ns) {
51
- co_aio_sleep_ns(bdrv_get_aio_context(bs), QEMU_CLOCK_REALTIME,
52
- s->latency_ns);
53
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, s->latency_ns);
54
}
55
return 0;
56
}
57
diff --git a/block/sheepdog.c b/block/sheepdog.c
58
index XXXXXXX..XXXXXXX 100644
59
--- a/block/sheepdog.c
60
+++ b/block/sheepdog.c
61
@@ -XXX,XX +XXX,XX @@ static coroutine_fn void reconnect_to_sdog(void *opaque)
62
if (s->fd < 0) {
63
DPRINTF("Wait for connection to be established\n");
64
error_report_err(local_err);
65
- co_aio_sleep_ns(bdrv_get_aio_context(s->bs), QEMU_CLOCK_REALTIME,
66
- 1000000000ULL);
67
+ qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL);
68
}
69
};
70
71
diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c
72
index XXXXXXX..XXXXXXX 100644
73
--- a/util/qemu-coroutine-sleep.c
74
+++ b/util/qemu-coroutine-sleep.c
75
@@ -XXX,XX +XXX,XX @@ static void co_sleep_cb(void *opaque)
76
aio_co_wake(sleep_cb->co);
77
}
78
79
-void coroutine_fn co_aio_sleep_ns(AioContext *ctx, QEMUClockType type,
80
- int64_t ns)
81
+void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns)
82
{
83
+ AioContext *ctx = qemu_get_current_aio_context();
84
CoSleepCB sleep_cb = {
85
.co = qemu_coroutine_self(),
86
};
87
--
88
2.14.3
89
90
diff view generated by jsdifflib
1
From: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
1
From: Fam Zheng <famz@redhat.com>
2
2
3
Cc: John Snow <jsnow@redhat.com>
3
If we know we've already locked the bytes, don't do it again; similarly
4
Cc: Kevin Wolf <kwolf@redhat.com>
4
don't unlock a byte if we haven't locked it. This doesn't change the
5
Cc: Max Reitz <mreitz@redhat.com>
5
behavior, but fixes a corner case explained below.
6
Cc: Keith Busch <keith.busch@intel.com>
7
Cc: Stefan Hajnoczi <stefanha@redhat.com>
8
Cc: "Michael S. Tsirkin" <mst@redhat.com>
9
Cc: Paolo Bonzini <pbonzini@redhat.com>
10
Cc: Gerd Hoffmann <kraxel@redhat.com>
11
Cc: Markus Armbruster <armbru@redhat.com>
12
6
13
Signed-off-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
7
Libvirt had an error handling bug that an image can get its (ownership,
14
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
8
file mode, SELinux) permissions changed (RHBZ 1584982) by mistake behind
15
Message-id: e77848d3735ba590f23ffbf8094379c646c33d79.1511317952.git.maozy.fnst@cn.fujitsu.com
9
QEMU. Specifically, an image in use by Libvirt VM has:
16
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
11
$ ls -lhZ b.img
12
-rw-r--r--. qemu qemu system_u:object_r:svirt_image_t:s0:c600,c690 b.img
13
14
Trying to attach it a second time won't work because of image locking.
15
And after the error, it becomes:
16
17
$ ls -lhZ b.img
18
-rw-r--r--. root root system_u:object_r:virt_image_t:s0 b.img
19
20
Then, we won't be able to do OFD lock operations with the existing fd.
21
In other words, the code such as in blk_detach_dev:
22
23
blk_set_perm(blk, 0, BLK_PERM_ALL, &error_abort);
24
25
can abort() QEMU, out of environmental changes.
26
27
This patch is an easy fix to this and the change is regardlessly
28
reasonable, so do it.
29
30
Signed-off-by: Fam Zheng <famz@redhat.com>
31
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
17
---
32
---
18
hw/block/fdc.c | 17 ++++++-----------
33
block/file-posix.c | 41 +++++++++++++++++++++++++++++++----------
19
hw/block/nvme.c | 7 ++-----
34
1 file changed, 31 insertions(+), 10 deletions(-)
20
hw/block/virtio-blk.c | 18 ++++++------------
21
hw/ide/qdev.c | 12 ++++--------
22
hw/scsi/scsi-disk.c | 13 ++++---------
23
hw/usb/dev-storage.c | 9 +++------
24
6 files changed, 25 insertions(+), 51 deletions(-)
25
35
26
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
36
diff --git a/block/file-posix.c b/block/file-posix.c
27
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100644
28
--- a/hw/block/fdc.c
38
--- a/block/file-posix.c
29
+++ b/hw/block/fdc.c
39
+++ b/block/file-posix.c
30
@@ -XXX,XX +XXX,XX @@ static void fd_revalidate(FDrive *drv)
40
@@ -XXX,XX +XXX,XX @@ typedef enum {
31
static void fd_change_cb(void *opaque, bool load, Error **errp)
41
* file; if @unlock == true, also unlock the unneeded bytes.
42
* @shared_perm_lock_bits is the mask of all permissions that are NOT shared.
43
*/
44
-static int raw_apply_lock_bytes(int fd,
45
+static int raw_apply_lock_bytes(BDRVRawState *s, int fd,
46
uint64_t perm_lock_bits,
47
uint64_t shared_perm_lock_bits,
48
bool unlock, Error **errp)
32
{
49
{
33
FDrive *drive = opaque;
50
int ret;
34
- Error *local_err = NULL;
51
int i;
35
52
+ uint64_t locked_perm, locked_shared_perm;
36
if (!load) {
53
+
37
blk_set_perm(drive->blk, 0, BLK_PERM_ALL, &error_abort);
54
+ if (s) {
38
} else {
55
+ locked_perm = s->perm;
39
- blkconf_apply_backend_options(drive->conf,
56
+ locked_shared_perm = ~s->shared_perm & BLK_PERM_ALL;
40
- blk_is_read_only(drive->blk), false,
57
+ } else {
41
- &local_err);
58
+ /*
42
- if (local_err) {
59
+ * We don't have the previous bits, just lock/unlock for each of the
43
- error_propagate(errp, local_err);
60
+ * requested bits.
44
+ if (!blkconf_apply_backend_options(drive->conf,
61
+ */
45
+ blk_is_read_only(drive->blk), false,
62
+ if (unlock) {
46
+ errp)) {
63
+ locked_perm = BLK_PERM_ALL;
47
return;
64
+ locked_shared_perm = BLK_PERM_ALL;
65
+ } else {
66
+ locked_perm = 0;
67
+ locked_shared_perm = 0;
68
+ }
69
+ }
70
71
PERM_FOREACH(i) {
72
int off = RAW_LOCK_PERM_BASE + i;
73
- if (perm_lock_bits & (1ULL << i)) {
74
+ uint64_t bit = (1ULL << i);
75
+ if ((perm_lock_bits & bit) && !(locked_perm & bit)) {
76
ret = qemu_lock_fd(fd, off, 1, false);
77
if (ret) {
78
error_setg(errp, "Failed to lock byte %d", off);
79
return ret;
80
}
81
- } else if (unlock) {
82
+ } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) {
83
ret = qemu_unlock_fd(fd, off, 1);
84
if (ret) {
85
error_setg(errp, "Failed to unlock byte %d", off);
86
@@ -XXX,XX +XXX,XX @@ static int raw_apply_lock_bytes(int fd,
87
}
88
PERM_FOREACH(i) {
89
int off = RAW_LOCK_SHARED_BASE + i;
90
- if (shared_perm_lock_bits & (1ULL << i)) {
91
+ uint64_t bit = (1ULL << i);
92
+ if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) {
93
ret = qemu_lock_fd(fd, off, 1, false);
94
if (ret) {
95
error_setg(errp, "Failed to lock byte %d", off);
96
return ret;
97
}
98
- } else if (unlock) {
99
+ } else if (unlock && (locked_shared_perm & bit) &&
100
+ !(shared_perm_lock_bits & bit)) {
101
ret = qemu_unlock_fd(fd, off, 1);
102
if (ret) {
103
error_setg(errp, "Failed to unlock byte %d", off);
104
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
105
106
switch (op) {
107
case RAW_PL_PREPARE:
108
- ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm,
109
+ ret = raw_apply_lock_bytes(s, s->lock_fd, s->perm | new_perm,
110
~s->shared_perm | ~new_shared,
111
false, errp);
112
if (!ret) {
113
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
114
op = RAW_PL_ABORT;
115
/* fall through to unlock bytes. */
116
case RAW_PL_ABORT:
117
- raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm,
118
+ raw_apply_lock_bytes(s, s->lock_fd, s->perm, ~s->shared_perm,
119
true, &local_err);
120
if (local_err) {
121
/* Theoretically the above call only unlocks bytes and it cannot
122
@@ -XXX,XX +XXX,XX @@ static int raw_handle_perm_lock(BlockDriverState *bs,
48
}
123
}
124
break;
125
case RAW_PL_COMMIT:
126
- raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared,
127
+ raw_apply_lock_bytes(s, s->lock_fd, new_perm, ~new_shared,
128
true, &local_err);
129
if (local_err) {
130
/* Theoretically the above call only unlocks bytes and it cannot
131
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
132
shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE;
133
134
/* Step one: Take locks */
135
- result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp);
136
+ result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp);
137
if (result < 0) {
138
goto out_close;
49
}
139
}
50
@@ -XXX,XX +XXX,XX @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
140
@@ -XXX,XX +XXX,XX @@ raw_co_create(BlockdevCreateOptions *options, Error **errp)
51
FloppyDrive *dev = FLOPPY_DRIVE(qdev);
52
FloppyBus *bus = FLOPPY_BUS(qdev->parent_bus);
53
FDrive *drive;
54
- Error *local_err = NULL;
55
int ret;
56
57
if (dev->unit == -1) {
58
@@ -XXX,XX +XXX,XX @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp)
59
dev->conf.rerror = BLOCKDEV_ON_ERROR_AUTO;
60
dev->conf.werror = BLOCKDEV_ON_ERROR_AUTO;
61
62
- blkconf_apply_backend_options(&dev->conf, blk_is_read_only(dev->conf.blk),
63
- false, &local_err);
64
- if (local_err) {
65
- error_propagate(errp, local_err);
66
+ if (!blkconf_apply_backend_options(&dev->conf,
67
+ blk_is_read_only(dev->conf.blk),
68
+ false, errp)) {
69
return;
70
}
141
}
71
142
72
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
143
out_unlock:
73
index XXXXXXX..XXXXXXX 100644
144
- raw_apply_lock_bytes(fd, 0, 0, true, &local_err);
74
--- a/hw/block/nvme.c
145
+ raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err);
75
+++ b/hw/block/nvme.c
146
if (local_err) {
76
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
147
/* The above call should not fail, and if it does, that does
77
int i;
148
* not mean the whole creation operation has failed. So
78
int64_t bs_size;
79
uint8_t *pci_conf;
80
- Error *local_err = NULL;
81
82
if (!n->conf.blk) {
83
error_setg(errp, "drive property not set");
84
@@ -XXX,XX +XXX,XX @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp)
85
return;
86
}
87
blkconf_blocksizes(&n->conf);
88
- blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
89
- false, &local_err);
90
- if (local_err) {
91
- error_propagate(errp, local_err);
92
+ if (!blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
93
+ false, errp)) {
94
return;
95
}
96
97
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
98
index XXXXXXX..XXXXXXX 100644
99
--- a/hw/block/virtio-blk.c
100
+++ b/hw/block/virtio-blk.c
101
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
102
VirtIODevice *vdev = VIRTIO_DEVICE(dev);
103
VirtIOBlock *s = VIRTIO_BLK(dev);
104
VirtIOBlkConf *conf = &s->conf;
105
- Error *err = NULL;
106
unsigned i;
107
108
if (!conf->conf.blk) {
109
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
110
}
111
112
blkconf_serial(&conf->conf, &conf->serial);
113
- blkconf_apply_backend_options(&conf->conf,
114
- blk_is_read_only(conf->conf.blk), true,
115
- &err);
116
- if (err) {
117
- error_propagate(errp, err);
118
+ if (!blkconf_apply_backend_options(&conf->conf,
119
+ blk_is_read_only(conf->conf.blk), true,
120
+ errp)) {
121
return;
122
}
123
s->original_wce = blk_enable_write_cache(conf->conf.blk);
124
- blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, &err);
125
- if (err) {
126
- error_propagate(errp, err);
127
+ if (!blkconf_geometry(&conf->conf, NULL, 65535, 255, 255, errp)) {
128
return;
129
}
130
+
131
blkconf_blocksizes(&conf->conf);
132
133
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
134
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
135
for (i = 0; i < conf->num_queues; i++) {
136
virtio_add_queue(vdev, 128, virtio_blk_handle_output);
137
}
138
- virtio_blk_data_plane_create(vdev, conf, &s->dataplane, &err);
139
- if (err != NULL) {
140
- error_propagate(errp, err);
141
+ if (!virtio_blk_data_plane_create(vdev, conf, &s->dataplane, errp)) {
142
virtio_cleanup(vdev);
143
return;
144
}
145
diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c
146
index XXXXXXX..XXXXXXX 100644
147
--- a/hw/ide/qdev.c
148
+++ b/hw/ide/qdev.c
149
@@ -XXX,XX +XXX,XX @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
150
{
151
IDEBus *bus = DO_UPCAST(IDEBus, qbus, dev->qdev.parent_bus);
152
IDEState *s = bus->ifs + dev->unit;
153
- Error *err = NULL;
154
int ret;
155
156
if (!dev->conf.blk) {
157
@@ -XXX,XX +XXX,XX @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp)
158
159
blkconf_serial(&dev->conf, &dev->serial);
160
if (kind != IDE_CD) {
161
- blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, &err);
162
- if (err) {
163
- error_propagate(errp, err);
164
+ if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255,
165
+ errp)) {
166
return;
167
}
168
}
169
- blkconf_apply_backend_options(&dev->conf, kind == IDE_CD, kind != IDE_CD,
170
- &err);
171
- if (err) {
172
- error_propagate(errp, err);
173
+ if (!blkconf_apply_backend_options(&dev->conf, kind == IDE_CD,
174
+ kind != IDE_CD, errp)) {
175
return;
176
}
177
178
diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c
179
index XXXXXXX..XXXXXXX 100644
180
--- a/hw/scsi/scsi-disk.c
181
+++ b/hw/scsi/scsi-disk.c
182
@@ -XXX,XX +XXX,XX @@ static void scsi_disk_unit_attention_reported(SCSIDevice *dev)
183
static void scsi_realize(SCSIDevice *dev, Error **errp)
184
{
185
SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev);
186
- Error *err = NULL;
187
188
if (!s->qdev.conf.blk) {
189
error_setg(errp, "drive property not set");
190
@@ -XXX,XX +XXX,XX @@ static void scsi_realize(SCSIDevice *dev, Error **errp)
191
}
192
193
if (dev->type == TYPE_DISK) {
194
- blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, &err);
195
- if (err) {
196
- error_propagate(errp, err);
197
+ if (!blkconf_geometry(&dev->conf, NULL, 65535, 255, 255, errp)) {
198
return;
199
}
200
}
201
- blkconf_apply_backend_options(&dev->conf,
202
- blk_is_read_only(s->qdev.conf.blk),
203
- dev->type == TYPE_DISK, &err);
204
- if (err) {
205
- error_propagate(errp, err);
206
+ if (!blkconf_apply_backend_options(&dev->conf,
207
+ blk_is_read_only(s->qdev.conf.blk),
208
+ dev->type == TYPE_DISK, errp)) {
209
return;
210
}
211
212
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
213
index XXXXXXX..XXXXXXX 100644
214
--- a/hw/usb/dev-storage.c
215
+++ b/hw/usb/dev-storage.c
216
@@ -XXX,XX +XXX,XX @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
217
MSDState *s = USB_STORAGE_DEV(dev);
218
BlockBackend *blk = s->conf.blk;
219
SCSIDevice *scsi_dev;
220
- Error *err = NULL;
221
222
if (!blk) {
223
error_setg(errp, "drive property not set");
224
@@ -XXX,XX +XXX,XX @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
225
226
blkconf_serial(&s->conf, &dev->serial);
227
blkconf_blocksizes(&s->conf);
228
- blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, &err);
229
- if (err) {
230
- error_propagate(errp, err);
231
+ if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true,
232
+ errp)) {
233
return;
234
}
235
236
@@ -XXX,XX +XXX,XX @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
237
&usb_msd_scsi_info_storage, NULL);
238
scsi_dev = scsi_bus_legacy_add_drive(&s->bus, blk, 0, !!s->removable,
239
s->conf.bootindex, dev->serial,
240
- &err);
241
+ errp);
242
blk_unref(blk);
243
if (!scsi_dev) {
244
- error_propagate(errp, err);
245
return;
246
}
247
usb_msd_handle_reset(dev);
248
--
149
--
249
2.14.3
150
2.17.1
250
151
251
152
diff view generated by jsdifflib
1
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
1
From: Marc-André Lureau <marcandre.lureau@redhat.com>
2
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
2
3
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Spotted by ASAN:
4
Message-id: 20171206144550.22295-6-stefanha@redhat.com
4
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
=================================================================
6
==5378==ERROR: LeakSanitizer: detected memory leaks
7
8
Direct leak of 65536 byte(s) in 1 object(s) allocated from:
9
#0 0x7f788f83bc48 in malloc (/lib64/libasan.so.5+0xeec48)
10
#1 0x7f788c9923c5 in g_malloc (/lib64/libglib-2.0.so.0+0x523c5)
11
#2 0x5622a1fe37bc in coroutine_trampoline /home/elmarco/src/qq/util/coroutine-ucontext.c:116
12
#3 0x7f788a15d75f in __correctly_grouped_prefixwc (/lib64/libc.so.6+0x4c75f)
13
14
(Broken in commit 4c8158e359d.)
15
16
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
17
Message-id: 20180809114417.28718-3-marcandre.lureau@redhat.com
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
---
19
---
7
blockdev.c | 47 +++++++++++++++++++++++++++++++----------------
20
tests/test-bdrv-drain.c | 1 +
8
1 file changed, 31 insertions(+), 16 deletions(-)
21
1 file changed, 1 insertion(+)
9
22
10
diff --git a/blockdev.c b/blockdev.c
23
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
11
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
12
--- a/blockdev.c
25
--- a/tests/test-bdrv-drain.c
13
+++ b/blockdev.c
26
+++ b/tests/test-bdrv-drain.c
14
@@ -XXX,XX +XXX,XX @@ struct BlkActionState {
27
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_co_delete_by_drain(void *opaque)
15
typedef struct InternalSnapshotState {
16
BlkActionState common;
17
BlockDriverState *bs;
18
- AioContext *aio_context;
19
QEMUSnapshotInfo sn;
20
bool created;
21
} InternalSnapshotState;
22
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_prepare(BlkActionState *common,
23
qemu_timeval tv;
24
BlockdevSnapshotInternal *internal;
25
InternalSnapshotState *state;
26
+ AioContext *aio_context;
27
int ret1;
28
29
g_assert(common->action->type ==
30
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_prepare(BlkActionState *common,
31
return;
32
}
28
}
33
29
34
- /* AioContext is released in .clean() */
30
dbdd->done = true;
35
- state->aio_context = bdrv_get_aio_context(bs);
31
+ g_free(buffer);
36
- aio_context_acquire(state->aio_context);
37
+ aio_context = bdrv_get_aio_context(bs);
38
+ aio_context_acquire(aio_context);
39
40
state->bs = bs;
41
+
42
+ /* Paired with .clean() */
43
bdrv_drained_begin(bs);
44
45
if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) {
46
- return;
47
+ goto out;
48
}
49
50
if (bdrv_is_read_only(bs)) {
51
error_setg(errp, "Device '%s' is read only", device);
52
- return;
53
+ goto out;
54
}
55
56
if (!bdrv_can_snapshot(bs)) {
57
error_setg(errp, "Block format '%s' used by device '%s' "
58
"does not support internal snapshots",
59
bs->drv->format_name, device);
60
- return;
61
+ goto out;
62
}
63
64
if (!strlen(name)) {
65
error_setg(errp, "Name is empty");
66
- return;
67
+ goto out;
68
}
69
70
/* check whether a snapshot with name exist */
71
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_prepare(BlkActionState *common,
72
&local_err);
73
if (local_err) {
74
error_propagate(errp, local_err);
75
- return;
76
+ goto out;
77
} else if (ret) {
78
error_setg(errp,
79
"Snapshot with name '%s' already exists on device '%s'",
80
name, device);
81
- return;
82
+ goto out;
83
}
84
85
/* 3. take the snapshot */
86
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_prepare(BlkActionState *common,
87
error_setg_errno(errp, -ret1,
88
"Failed to create snapshot '%s' on device '%s'",
89
name, device);
90
- return;
91
+ goto out;
92
}
93
94
/* 4. succeed, mark a snapshot is created */
95
state->created = true;
96
+
97
+out:
98
+ aio_context_release(aio_context);
99
}
32
}
100
33
101
static void internal_snapshot_abort(BlkActionState *common)
34
/**
102
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(BlkActionState *common)
103
DO_UPCAST(InternalSnapshotState, common, common);
104
BlockDriverState *bs = state->bs;
105
QEMUSnapshotInfo *sn = &state->sn;
106
+ AioContext *aio_context;
107
Error *local_error = NULL;
108
109
if (!state->created) {
110
return;
111
}
112
113
+ aio_context = bdrv_get_aio_context(state->bs);
114
+ aio_context_acquire(aio_context);
115
+
116
if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) {
117
error_reportf_err(local_error,
118
"Failed to delete snapshot with id '%s' and "
119
@@ -XXX,XX +XXX,XX @@ static void internal_snapshot_abort(BlkActionState *common)
120
sn->id_str, sn->name,
121
bdrv_get_device_name(bs));
122
}
123
+
124
+ aio_context_release(aio_context);
125
}
126
127
static void internal_snapshot_clean(BlkActionState *common)
128
{
129
InternalSnapshotState *state = DO_UPCAST(InternalSnapshotState,
130
common, common);
131
+ AioContext *aio_context;
132
133
- if (state->aio_context) {
134
- if (state->bs) {
135
- bdrv_drained_end(state->bs);
136
- }
137
- aio_context_release(state->aio_context);
138
+ if (!state->bs) {
139
+ return;
140
}
141
+
142
+ aio_context = bdrv_get_aio_context(state->bs);
143
+ aio_context_acquire(aio_context);
144
+
145
+ bdrv_drained_end(state->bs);
146
+
147
+ aio_context_release(aio_context);
148
}
149
150
/* external snapshot private data */
151
--
35
--
152
2.14.3
36
2.17.1
153
37
154
38
diff view generated by jsdifflib
1
It is not necessary to hold AioContext across transactions anymore since
1
From: John Snow <jsnow@redhat.com>
2
bdrv_drained_begin/end() is used to keep the nodes quiesced. In fact,
2
3
using the AioContext lock for this purpose was always buggy.
3
Presently we codify the entry point for a job as the "start" callback,
4
4
but a more apt name would be "run" to clarify the idea that when this
5
This patch reduces the scope of AioContext locked regions. This is not
5
function returns we consider the job to have "finished," except for
6
just a cleanup but also fixes hangs that occur in BDRV_POLL_WHILE()
6
any cleanup which occurs in separate callbacks later.
7
because it is unware of recursive locking and does not release the
7
8
AioContext the necessary number of times to allow progress to be made.
8
As part of this clarification, change the signature to include an error
9
9
object and a return code. The error ptr is not yet used, and the return
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
code while captured, will be overwritten by actions in the job_completed
11
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
11
function.
12
Reviewed-by: Eric Blake <eblake@redhat.com>
12
13
Message-id: 20171206144550.22295-3-stefanha@redhat.com
13
Signed-off-by: John Snow <jsnow@redhat.com>
14
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Message-id: 20180830015734.19765-2-jsnow@redhat.com
16
Reviewed-by: Jeff Cody <jcody@redhat.com>
17
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
18
---
16
blockdev.c | 71 ++++++++++++++++++++++++++++++++++++++++++--------------------
19
include/qemu/job.h | 2 +-
17
1 file changed, 48 insertions(+), 23 deletions(-)
20
block/backup.c | 7 ++++---
18
21
block/commit.c | 7 ++++---
19
diff --git a/blockdev.c b/blockdev.c
22
block/create.c | 8 +++++---
20
index XXXXXXX..XXXXXXX 100644
23
block/mirror.c | 10 ++++++----
21
--- a/blockdev.c
24
block/stream.c | 7 ++++---
22
+++ b/blockdev.c
25
job.c | 6 +++---
23
@@ -XXX,XX +XXX,XX @@ typedef struct ExternalSnapshotState {
26
tests/test-bdrv-drain.c | 7 ++++---
24
BlkActionState common;
27
tests/test-blockjob-txn.c | 16 ++++++++--------
25
BlockDriverState *old_bs;
28
tests/test-blockjob.c | 7 ++++---
26
BlockDriverState *new_bs;
29
10 files changed, 43 insertions(+), 34 deletions(-)
27
- AioContext *aio_context;
30
28
bool overlay_appended;
31
diff --git a/include/qemu/job.h b/include/qemu/job.h
29
} ExternalSnapshotState;
32
index XXXXXXX..XXXXXXX 100644
30
33
--- a/include/qemu/job.h
31
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
34
+++ b/include/qemu/job.h
32
ExternalSnapshotState *state =
35
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
33
DO_UPCAST(ExternalSnapshotState, common, common);
36
JobType job_type;
34
TransactionAction *action = common->action;
37
35
+ AioContext *aio_context;
38
/** Mandatory: Entrypoint for the Coroutine. */
36
39
- CoroutineEntry *start;
37
/* 'blockdev-snapshot' and 'blockdev-snapshot-sync' have similar
40
+ int coroutine_fn (*run)(Job *job, Error **errp);
38
* purpose but a different set of parameters */
41
39
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
42
/**
40
return;
43
* If the callback is not NULL, it will be invoked when the job transitions
44
diff --git a/block/backup.c b/block/backup.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/block/backup.c
47
+++ b/block/backup.c
48
@@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
49
bdrv_dirty_iter_free(dbi);
50
}
51
52
-static void coroutine_fn backup_run(void *opaque)
53
+static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
54
{
55
- BackupBlockJob *job = opaque;
56
+ BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job);
57
BackupCompleteData *data;
58
BlockDriverState *bs = blk_bs(job->common.blk);
59
int64_t offset, nb_clusters;
60
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn backup_run(void *opaque)
61
data = g_malloc(sizeof(*data));
62
data->ret = ret;
63
job_defer_to_main_loop(&job->common.job, backup_complete, data);
64
+ return ret;
65
}
66
67
static const BlockJobDriver backup_job_driver = {
68
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver backup_job_driver = {
69
.free = block_job_free,
70
.user_resume = block_job_user_resume,
71
.drain = block_job_drain,
72
- .start = backup_run,
73
+ .run = backup_run,
74
.commit = backup_commit,
75
.abort = backup_abort,
76
.clean = backup_clean,
77
diff --git a/block/commit.c b/block/commit.c
78
index XXXXXXX..XXXXXXX 100644
79
--- a/block/commit.c
80
+++ b/block/commit.c
81
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
82
bdrv_unref(top);
83
}
84
85
-static void coroutine_fn commit_run(void *opaque)
86
+static int coroutine_fn commit_run(Job *job, Error **errp)
87
{
88
- CommitBlockJob *s = opaque;
89
+ CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
90
CommitCompleteData *data;
91
int64_t offset;
92
uint64_t delay_ns = 0;
93
@@ -XXX,XX +XXX,XX @@ out:
94
data = g_malloc(sizeof(*data));
95
data->ret = ret;
96
job_defer_to_main_loop(&s->common.job, commit_complete, data);
97
+ return ret;
98
}
99
100
static const BlockJobDriver commit_job_driver = {
101
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
102
.free = block_job_free,
103
.user_resume = block_job_user_resume,
104
.drain = block_job_drain,
105
- .start = commit_run,
106
+ .run = commit_run,
107
},
108
};
109
110
diff --git a/block/create.c b/block/create.c
111
index XXXXXXX..XXXXXXX 100644
112
--- a/block/create.c
113
+++ b/block/create.c
114
@@ -XXX,XX +XXX,XX @@ static void blockdev_create_complete(Job *job, void *opaque)
115
job_completed(job, s->ret, s->err);
116
}
117
118
-static void coroutine_fn blockdev_create_run(void *opaque)
119
+static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
120
{
121
- BlockdevCreateJob *s = opaque;
122
+ BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
123
124
job_progress_set_remaining(&s->common, 1);
125
s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
126
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn blockdev_create_run(void *opaque)
127
128
qapi_free_BlockdevCreateOptions(s->opts);
129
job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL);
130
+
131
+ return s->ret;
132
}
133
134
static const JobDriver blockdev_create_job_driver = {
135
.instance_size = sizeof(BlockdevCreateJob),
136
.job_type = JOB_TYPE_CREATE,
137
- .start = blockdev_create_run,
138
+ .run = blockdev_create_run,
139
};
140
141
void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options,
142
diff --git a/block/mirror.c b/block/mirror.c
143
index XXXXXXX..XXXXXXX 100644
144
--- a/block/mirror.c
145
+++ b/block/mirror.c
146
@@ -XXX,XX +XXX,XX @@ static int mirror_flush(MirrorBlockJob *s)
147
return ret;
148
}
149
150
-static void coroutine_fn mirror_run(void *opaque)
151
+static int coroutine_fn mirror_run(Job *job, Error **errp)
152
{
153
- MirrorBlockJob *s = opaque;
154
+ MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
155
MirrorExitData *data;
156
BlockDriverState *bs = s->mirror_top_bs->backing->bs;
157
BlockDriverState *target_bs = blk_bs(s->target);
158
@@ -XXX,XX +XXX,XX @@ immediate_exit:
159
if (need_drain) {
160
bdrv_drained_begin(bs);
41
}
161
}
42
43
- /* Acquire AioContext now so any threads operating on old_bs stop */
44
- state->aio_context = bdrv_get_aio_context(state->old_bs);
45
- aio_context_acquire(state->aio_context);
46
+ aio_context = bdrv_get_aio_context(state->old_bs);
47
+ aio_context_acquire(aio_context);
48
+
162
+
49
+ /* Paired with .clean() */
163
job_defer_to_main_loop(&s->common.job, mirror_exit, data);
50
bdrv_drained_begin(state->old_bs);
164
+ return ret;
51
165
}
52
if (!bdrv_is_inserted(state->old_bs)) {
166
53
error_setg(errp, QERR_DEVICE_HAS_NO_MEDIUM, device);
167
static void mirror_complete(Job *job, Error **errp)
54
- return;
168
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
55
+ goto out;
169
.free = block_job_free,
170
.user_resume = block_job_user_resume,
171
.drain = block_job_drain,
172
- .start = mirror_run,
173
+ .run = mirror_run,
174
.pause = mirror_pause,
175
.complete = mirror_complete,
176
},
177
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
178
.free = block_job_free,
179
.user_resume = block_job_user_resume,
180
.drain = block_job_drain,
181
- .start = mirror_run,
182
+ .run = mirror_run,
183
.pause = mirror_pause,
184
.complete = mirror_complete,
185
},
186
diff --git a/block/stream.c b/block/stream.c
187
index XXXXXXX..XXXXXXX 100644
188
--- a/block/stream.c
189
+++ b/block/stream.c
190
@@ -XXX,XX +XXX,XX @@ out:
191
g_free(data);
192
}
193
194
-static void coroutine_fn stream_run(void *opaque)
195
+static int coroutine_fn stream_run(Job *job, Error **errp)
196
{
197
- StreamBlockJob *s = opaque;
198
+ StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
199
StreamCompleteData *data;
200
BlockBackend *blk = s->common.blk;
201
BlockDriverState *bs = blk_bs(blk);
202
@@ -XXX,XX +XXX,XX @@ out:
203
data = g_malloc(sizeof(*data));
204
data->ret = ret;
205
job_defer_to_main_loop(&s->common.job, stream_complete, data);
206
+ return ret;
207
}
208
209
static const BlockJobDriver stream_job_driver = {
210
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
211
.instance_size = sizeof(StreamBlockJob),
212
.job_type = JOB_TYPE_STREAM,
213
.free = block_job_free,
214
- .start = stream_run,
215
+ .run = stream_run,
216
.user_resume = block_job_user_resume,
217
.drain = block_job_drain,
218
},
219
diff --git a/job.c b/job.c
220
index XXXXXXX..XXXXXXX 100644
221
--- a/job.c
222
+++ b/job.c
223
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque)
224
{
225
Job *job = opaque;
226
227
- assert(job && job->driver && job->driver->start);
228
+ assert(job && job->driver && job->driver->run);
229
job_pause_point(job);
230
- job->driver->start(job);
231
+ job->ret = job->driver->run(job, NULL);
232
}
233
234
235
void job_start(Job *job)
236
{
237
assert(job && !job_started(job) && job->paused &&
238
- job->driver && job->driver->start);
239
+ job->driver && job->driver->run);
240
job->co = qemu_coroutine_create(job_co_entry, job);
241
job->pause_count--;
242
job->busy = true;
243
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
244
index XXXXXXX..XXXXXXX 100644
245
--- a/tests/test-bdrv-drain.c
246
+++ b/tests/test-bdrv-drain.c
247
@@ -XXX,XX +XXX,XX @@ static void test_job_completed(Job *job, void *opaque)
248
job_completed(job, 0, NULL);
249
}
250
251
-static void coroutine_fn test_job_start(void *opaque)
252
+static int coroutine_fn test_job_run(Job *job, Error **errp)
253
{
254
- TestBlockJob *s = opaque;
255
+ TestBlockJob *s = container_of(job, TestBlockJob, common.job);
256
257
job_transition_to_ready(&s->common.job);
258
while (!s->should_complete) {
259
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn test_job_start(void *opaque)
56
}
260
}
57
261
58
if (bdrv_op_is_blocked(state->old_bs,
262
job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
59
BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) {
263
+ return 0;
60
- return;
264
}
61
+ goto out;
265
62
}
266
static void test_job_complete(Job *job, Error **errp)
63
267
@@ -XXX,XX +XXX,XX @@ BlockJobDriver test_job_driver = {
64
if (!bdrv_is_read_only(state->old_bs)) {
268
.free = block_job_free,
65
if (bdrv_flush(state->old_bs)) {
269
.user_resume = block_job_user_resume,
66
error_setg(errp, QERR_IO_ERROR);
270
.drain = block_job_drain,
67
- return;
271
- .start = test_job_start,
68
+ goto out;
272
+ .run = test_job_run,
273
.complete = test_job_complete,
274
},
275
};
276
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
277
index XXXXXXX..XXXXXXX 100644
278
--- a/tests/test-blockjob-txn.c
279
+++ b/tests/test-blockjob-txn.c
280
@@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque)
281
bdrv_unref(bs);
282
}
283
284
-static void coroutine_fn test_block_job_run(void *opaque)
285
+static int coroutine_fn test_block_job_run(Job *job, Error **errp)
286
{
287
- TestBlockJob *s = opaque;
288
- BlockJob *job = &s->common;
289
+ TestBlockJob *s = container_of(job, TestBlockJob, common.job);
290
291
while (s->iterations--) {
292
if (s->use_timer) {
293
- job_sleep_ns(&job->job, 0);
294
+ job_sleep_ns(job, 0);
295
} else {
296
- job_yield(&job->job);
297
+ job_yield(job);
298
}
299
300
- if (job_is_cancelled(&job->job)) {
301
+ if (job_is_cancelled(job)) {
302
break;
69
}
303
}
70
}
304
}
71
305
72
if (!bdrv_is_first_non_filter(state->old_bs)) {
306
- job_defer_to_main_loop(&job->job, test_block_job_complete,
73
error_setg(errp, QERR_FEATURE_DISABLED, "snapshot");
307
+ job_defer_to_main_loop(job, test_block_job_complete,
74
- return;
308
(void *)(intptr_t)s->rc);
75
+ goto out;
309
+ return s->rc;
76
}
310
}
77
311
78
if (action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_SNAPSHOT_SYNC) {
312
typedef struct {
79
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
313
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
80
314
.free = block_job_free,
81
if (node_name && !snapshot_node_name) {
315
.user_resume = block_job_user_resume,
82
error_setg(errp, "New snapshot node name missing");
316
.drain = block_job_drain,
83
- return;
317
- .start = test_block_job_run,
84
+ goto out;
318
+ .run = test_block_job_run,
85
}
319
},
86
320
};
87
if (snapshot_node_name &&
321
88
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
322
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
89
error_setg(errp, "New snapshot node name already in use");
323
index XXXXXXX..XXXXXXX 100644
90
- return;
324
--- a/tests/test-blockjob.c
91
+ goto out;
325
+++ b/tests/test-blockjob.c
92
}
326
@@ -XXX,XX +XXX,XX @@ static void cancel_job_complete(Job *job, Error **errp)
93
327
s->should_complete = true;
94
flags = state->old_bs->open_flags;
328
}
95
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
329
96
int64_t size = bdrv_getlength(state->old_bs);
330
-static void coroutine_fn cancel_job_start(void *opaque)
97
if (size < 0) {
331
+static int coroutine_fn cancel_job_run(Job *job, Error **errp)
98
error_setg_errno(errp, -size, "bdrv_getlength failed");
332
{
99
- return;
333
- CancelJob *s = opaque;
100
+ goto out;
334
+ CancelJob *s = container_of(job, CancelJob, common.job);
101
}
335
102
bdrv_img_create(new_image_file, format,
336
while (!s->should_complete) {
103
state->old_bs->filename,
337
if (job_is_cancelled(&s->common.job)) {
104
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
338
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn cancel_job_start(void *opaque)
105
NULL, size, flags, false, &local_err);
339
106
if (local_err) {
340
defer:
107
error_propagate(errp, local_err);
341
job_defer_to_main_loop(&s->common.job, cancel_job_completed, s);
108
- return;
342
+ return 0;
109
+ goto out;
343
}
110
}
344
111
}
345
static const BlockJobDriver test_cancel_driver = {
112
346
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
113
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
347
.free = block_job_free,
114
errp);
348
.user_resume = block_job_user_resume,
115
/* We will manually add the backing_hd field to the bs later */
349
.drain = block_job_drain,
116
if (!state->new_bs) {
350
- .start = cancel_job_start,
117
- return;
351
+ .run = cancel_job_run,
118
+ goto out;
352
.complete = cancel_job_complete,
119
}
353
},
120
354
};
121
if (bdrv_has_blk(state->new_bs)) {
122
error_setg(errp, "The snapshot is already in use");
123
- return;
124
+ goto out;
125
}
126
127
if (bdrv_op_is_blocked(state->new_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
128
errp)) {
129
- return;
130
+ goto out;
131
}
132
133
if (state->new_bs->backing != NULL) {
134
error_setg(errp, "The snapshot already has a backing image");
135
- return;
136
+ goto out;
137
}
138
139
if (!state->new_bs->drv->supports_backing) {
140
error_setg(errp, "The snapshot does not support backing images");
141
- return;
142
+ goto out;
143
}
144
145
- bdrv_set_aio_context(state->new_bs, state->aio_context);
146
+ bdrv_set_aio_context(state->new_bs, aio_context);
147
148
/* This removes our old bs and adds the new bs. This is an operation that
149
* can fail, so we need to do it in .prepare; undoing it for abort is
150
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
151
bdrv_append(state->new_bs, state->old_bs, &local_err);
152
if (local_err) {
153
error_propagate(errp, local_err);
154
- return;
155
+ goto out;
156
}
157
state->overlay_appended = true;
158
+
159
+out:
160
+ aio_context_release(aio_context);
161
}
162
163
static void external_snapshot_commit(BlkActionState *common)
164
{
165
ExternalSnapshotState *state =
166
DO_UPCAST(ExternalSnapshotState, common, common);
167
+ AioContext *aio_context;
168
+
169
+ aio_context = bdrv_get_aio_context(state->old_bs);
170
+ aio_context_acquire(aio_context);
171
172
/* We don't need (or want) to use the transactional
173
* bdrv_reopen_multiple() across all the entries at once, because we
174
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_commit(BlkActionState *common)
175
bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR,
176
NULL);
177
}
178
+
179
+ aio_context_release(aio_context);
180
}
181
182
static void external_snapshot_abort(BlkActionState *common)
183
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_abort(BlkActionState *common)
184
DO_UPCAST(ExternalSnapshotState, common, common);
185
if (state->new_bs) {
186
if (state->overlay_appended) {
187
+ AioContext *aio_context;
188
+
189
+ aio_context = bdrv_get_aio_context(state->old_bs);
190
+ aio_context_acquire(aio_context);
191
+
192
bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd()
193
close state->old_bs; we need it */
194
bdrv_set_backing_hd(state->new_bs, NULL, &error_abort);
195
bdrv_replace_node(state->new_bs, state->old_bs, &error_abort);
196
bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */
197
+
198
+ aio_context_release(aio_context);
199
}
200
}
201
}
202
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(BlkActionState *common)
203
{
204
ExternalSnapshotState *state =
205
DO_UPCAST(ExternalSnapshotState, common, common);
206
- if (state->aio_context) {
207
- bdrv_drained_end(state->old_bs);
208
- bdrv_unref(state->new_bs);
209
- aio_context_release(state->aio_context);
210
+ AioContext *aio_context;
211
+
212
+ if (!state->old_bs) {
213
+ return;
214
}
215
+
216
+ aio_context = bdrv_get_aio_context(state->old_bs);
217
+ aio_context_acquire(aio_context);
218
+
219
+ bdrv_drained_end(state->old_bs);
220
+ bdrv_unref(state->new_bs);
221
+
222
+ aio_context_release(aio_context);
223
}
224
225
typedef struct DriveBackupState {
226
--
355
--
227
2.14.3
356
2.17.1
228
357
229
358
diff view generated by jsdifflib
1
This test case will prevent future regressions with savevm and
1
From: John Snow <jsnow@redhat.com>
2
IOThreads.
2
3
3
Jobs presently use both an Error object in the case of the create job,
4
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
and char strings in the case of generic errors elsewhere.
5
6
Unify the two paths as just j->err, and remove the extra argument from
7
job_completed. The integer error code for job_completed is kept for now,
8
to be removed shortly in a separate patch.
9
10
Signed-off-by: John Snow <jsnow@redhat.com>
11
Message-id: 20180830015734.19765-3-jsnow@redhat.com
12
[mreitz: Dropped a superfluous g_strdup()]
5
Reviewed-by: Eric Blake <eblake@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Message-id: 20171207201320.19284-7-stefanha@redhat.com
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
15
---
9
tests/qemu-iotests/203 | 59 ++++++++++++++++++++++++++++++++++++++++++++++
16
include/qemu/job.h | 14 ++++++++------
10
tests/qemu-iotests/203.out | 6 +++++
17
block/backup.c | 2 +-
11
tests/qemu-iotests/group | 1 +
18
block/commit.c | 2 +-
12
3 files changed, 66 insertions(+)
19
block/create.c | 5 ++---
13
create mode 100755 tests/qemu-iotests/203
20
block/mirror.c | 2 +-
14
create mode 100644 tests/qemu-iotests/203.out
21
block/stream.c | 2 +-
15
22
job-qmp.c | 5 +++--
16
diff --git a/tests/qemu-iotests/203 b/tests/qemu-iotests/203
23
job.c | 18 ++++++------------
17
new file mode 100755
24
tests/test-bdrv-drain.c | 2 +-
18
index XXXXXXX..XXXXXXX
25
tests/test-blockjob-txn.c | 2 +-
19
--- /dev/null
26
tests/test-blockjob.c | 2 +-
20
+++ b/tests/qemu-iotests/203
27
11 files changed, 26 insertions(+), 30 deletions(-)
21
@@ -XXX,XX +XXX,XX @@
28
22
+#!/usr/bin/env python
29
diff --git a/include/qemu/job.h b/include/qemu/job.h
23
+#
30
index XXXXXXX..XXXXXXX 100644
24
+# Copyright (C) 2017 Red Hat, Inc.
31
--- a/include/qemu/job.h
25
+#
32
+++ b/include/qemu/job.h
26
+# This program is free software; you can redistribute it and/or modify
33
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
27
+# it under the terms of the GNU General Public License as published by
34
/** Estimated progress_current value at the completion of the job */
28
+# the Free Software Foundation; either version 2 of the License, or
35
int64_t progress_total;
29
+# (at your option) any later version.
36
30
+#
37
- /** Error string for a failed job (NULL if, and only if, job->ret == 0) */
31
+# This program is distributed in the hope that it will be useful,
38
- char *error;
32
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
39
-
33
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
40
/** ret code passed to job_completed. */
34
+# GNU General Public License for more details.
41
int ret;
35
+#
42
36
+# You should have received a copy of the GNU General Public License
43
+ /**
37
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
44
+ * Error object for a failed job.
38
+#
45
+ * If job->ret is nonzero and an error object was not set, it will be set
39
+# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
46
+ * to strerror(-job->ret) during job_completed.
40
+#
47
+ */
41
+# Check that QMP 'migrate' with multiple drives on a single IOThread completes
48
+ Error *err;
42
+# successfully. This particular command triggered a hang in the source QEMU
43
+# process due to recursive AioContext locking in bdrv_invalidate_all() and
44
+# BDRV_POLL_WHILE().
45
+
49
+
46
+import iotests
50
/** The completion function that will be called when the job completes. */
47
+
51
BlockCompletionFunc *cb;
48
+iotests.verify_image_format(supported_fmts=['qcow2'])
52
49
+iotests.verify_platform(['linux'])
53
@@ -XXX,XX +XXX,XX @@ void job_transition_to_ready(Job *job);
50
+
54
/**
51
+with iotests.FilePath('disk0.img') as disk0_img_path, \
55
* @job: The job being completed.
52
+ iotests.FilePath('disk1.img') as disk1_img_path, \
56
* @ret: The status code.
53
+ iotests.VM() as vm:
57
- * @error: The error message for a failing job (only with @ret < 0). If @ret is
54
+
58
- * negative, but NULL is given for @error, strerror() is used.
55
+ img_size = '10M'
59
*
56
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
60
* Marks @job as completed. If @ret is non-zero, the job transaction it is part
57
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
61
* of is aborted. If @ret is zero, the job moves into the WAITING state. If it
58
+
62
* is the last job to complete in its transaction, all jobs in the transaction
59
+ iotests.log('Launching VM...')
63
* move from WAITING to PENDING.
60
+ (vm.add_object('iothread,id=iothread0')
64
*/
61
+ .add_drive(disk0_img_path, 'node-name=drive0-node', interface='none')
65
-void job_completed(Job *job, int ret, Error *error);
62
+ .add_drive(disk1_img_path, 'node-name=drive1-node', interface='none')
66
+void job_completed(Job *job, int ret);
63
+ .launch())
67
64
+
68
/** Asynchronously complete the specified @job. */
65
+ iotests.log('Setting IOThreads...')
69
void job_complete(Job *job, Error **errp);
66
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
70
diff --git a/block/backup.c b/block/backup.c
67
+ node_name='drive0-node', iothread='iothread0',
71
index XXXXXXX..XXXXXXX 100644
68
+ force=True))
72
--- a/block/backup.c
69
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
73
+++ b/block/backup.c
70
+ node_name='drive1-node', iothread='iothread0',
74
@@ -XXX,XX +XXX,XX @@ static void backup_complete(Job *job, void *opaque)
71
+ force=True))
75
{
72
+
76
BackupCompleteData *data = opaque;
73
+ iotests.log('Starting migration...')
77
74
+ iotests.log(vm.qmp('migrate', uri='exec:cat >/dev/null'))
78
- job_completed(job, data->ret, NULL);
75
+ while True:
79
+ job_completed(job, data->ret);
76
+ vm.get_qmp_event(wait=60.0)
80
g_free(data);
77
+ result = vm.qmp('query-migrate')
81
}
78
+ status = result.get('return', {}).get('status', None)
82
79
+ if status == 'completed':
83
diff --git a/block/commit.c b/block/commit.c
80
+ break
84
index XXXXXXX..XXXXXXX 100644
81
diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out
85
--- a/block/commit.c
82
new file mode 100644
86
+++ b/block/commit.c
83
index XXXXXXX..XXXXXXX
87
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
84
--- /dev/null
88
* bdrv_set_backing_hd() to fail. */
85
+++ b/tests/qemu-iotests/203.out
89
block_job_remove_all_bdrv(bjob);
86
@@ -XXX,XX +XXX,XX @@
90
87
+Launching VM...
91
- job_completed(job, ret, NULL);
88
+Setting IOThreads...
92
+ job_completed(job, ret);
89
+{u'return': {}}
93
g_free(data);
90
+{u'return': {}}
94
91
+Starting migration...
95
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
92
+{u'return': {}}
96
diff --git a/block/create.c b/block/create.c
93
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
97
index XXXXXXX..XXXXXXX 100644
94
index XXXXXXX..XXXXXXX 100644
98
--- a/block/create.c
95
--- a/tests/qemu-iotests/group
99
+++ b/block/create.c
96
+++ b/tests/qemu-iotests/group
100
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevCreateJob {
97
@@ -XXX,XX +XXX,XX @@
101
BlockDriver *drv;
98
198 rw auto
102
BlockdevCreateOptions *opts;
99
200 rw auto
103
int ret;
100
202 rw auto quick
104
- Error *err;
101
+203 rw auto
105
} BlockdevCreateJob;
106
107
static void blockdev_create_complete(Job *job, void *opaque)
108
{
109
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
110
111
- job_completed(job, s->ret, s->err);
112
+ job_completed(job, s->ret);
113
}
114
115
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
116
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
117
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
118
119
job_progress_set_remaining(&s->common, 1);
120
- s->ret = s->drv->bdrv_co_create(s->opts, &s->err);
121
+ s->ret = s->drv->bdrv_co_create(s->opts, errp);
122
job_progress_update(&s->common, 1);
123
124
qapi_free_BlockdevCreateOptions(s->opts);
125
diff --git a/block/mirror.c b/block/mirror.c
126
index XXXXXXX..XXXXXXX 100644
127
--- a/block/mirror.c
128
+++ b/block/mirror.c
129
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
130
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
131
132
bs_opaque->job = NULL;
133
- job_completed(job, data->ret, NULL);
134
+ job_completed(job, data->ret);
135
136
g_free(data);
137
bdrv_drained_end(src);
138
diff --git a/block/stream.c b/block/stream.c
139
index XXXXXXX..XXXXXXX 100644
140
--- a/block/stream.c
141
+++ b/block/stream.c
142
@@ -XXX,XX +XXX,XX @@ out:
143
}
144
145
g_free(s->backing_file_str);
146
- job_completed(job, data->ret, NULL);
147
+ job_completed(job, data->ret);
148
g_free(data);
149
}
150
151
diff --git a/job-qmp.c b/job-qmp.c
152
index XXXXXXX..XXXXXXX 100644
153
--- a/job-qmp.c
154
+++ b/job-qmp.c
155
@@ -XXX,XX +XXX,XX @@ static JobInfo *job_query_single(Job *job, Error **errp)
156
.status = job->status,
157
.current_progress = job->progress_current,
158
.total_progress = job->progress_total,
159
- .has_error = !!job->error,
160
- .error = g_strdup(job->error),
161
+ .has_error = !!job->err,
162
+ .error = job->err ? \
163
+ g_strdup(error_get_pretty(job->err)) : NULL,
164
};
165
166
return info;
167
diff --git a/job.c b/job.c
168
index XXXXXXX..XXXXXXX 100644
169
--- a/job.c
170
+++ b/job.c
171
@@ -XXX,XX +XXX,XX @@ void job_unref(Job *job)
172
173
QLIST_REMOVE(job, job_list);
174
175
- g_free(job->error);
176
+ error_free(job->err);
177
g_free(job->id);
178
g_free(job);
179
}
180
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque)
181
182
assert(job && job->driver && job->driver->run);
183
job_pause_point(job);
184
- job->ret = job->driver->run(job, NULL);
185
+ job->ret = job->driver->run(job, &job->err);
186
}
187
188
189
@@ -XXX,XX +XXX,XX @@ static void job_update_rc(Job *job)
190
job->ret = -ECANCELED;
191
}
192
if (job->ret) {
193
- if (!job->error) {
194
- job->error = g_strdup(strerror(-job->ret));
195
+ if (!job->err) {
196
+ error_setg(&job->err, "%s", strerror(-job->ret));
197
}
198
job_state_transition(job, JOB_STATUS_ABORTING);
199
}
200
@@ -XXX,XX +XXX,XX @@ static void job_completed_txn_success(Job *job)
201
}
202
}
203
204
-void job_completed(Job *job, int ret, Error *error)
205
+void job_completed(Job *job, int ret)
206
{
207
assert(job && job->txn && !job_is_completed(job));
208
209
job->ret = ret;
210
- if (error) {
211
- assert(job->ret < 0);
212
- job->error = g_strdup(error_get_pretty(error));
213
- error_free(error);
214
- }
215
-
216
job_update_rc(job);
217
trace_job_completed(job, ret, job->ret);
218
if (job->ret) {
219
@@ -XXX,XX +XXX,XX @@ void job_cancel(Job *job, bool force)
220
}
221
job_cancel_async(job, force);
222
if (!job_started(job)) {
223
- job_completed(job, -ECANCELED, NULL);
224
+ job_completed(job, -ECANCELED);
225
} else if (job->deferred_to_main_loop) {
226
job_completed_txn_abort(job);
227
} else {
228
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
229
index XXXXXXX..XXXXXXX 100644
230
--- a/tests/test-bdrv-drain.c
231
+++ b/tests/test-bdrv-drain.c
232
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
233
234
static void test_job_completed(Job *job, void *opaque)
235
{
236
- job_completed(job, 0, NULL);
237
+ job_completed(job, 0);
238
}
239
240
static int coroutine_fn test_job_run(Job *job, Error **errp)
241
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
242
index XXXXXXX..XXXXXXX 100644
243
--- a/tests/test-blockjob-txn.c
244
+++ b/tests/test-blockjob-txn.c
245
@@ -XXX,XX +XXX,XX @@ static void test_block_job_complete(Job *job, void *opaque)
246
rc = -ECANCELED;
247
}
248
249
- job_completed(job, rc, NULL);
250
+ job_completed(job, rc);
251
bdrv_unref(bs);
252
}
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 void cancel_job_completed(Job *job, void *opaque)
259
{
260
CancelJob *s = opaque;
261
s->completed = true;
262
- job_completed(job, 0, NULL);
263
+ job_completed(job, 0);
264
}
265
266
static void cancel_job_complete(Job *job, Error **errp)
102
--
267
--
103
2.14.3
268
2.17.1
104
269
105
270
diff view generated by jsdifflib
1
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
2
3
Reviewed-by: Eric Blake <eblake@redhat.com>
3
All jobs do the same thing when they leave their running loop:
4
Message-id: 20171206144550.22295-5-stefanha@redhat.com
4
- Store the return code in a structure
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
- wait to receive this structure in the main thread
6
- signal job completion via job_completed
7
8
Few jobs do anything beyond exactly this. Consolidate this exit
9
logic for a net reduction in SLOC.
10
11
More seriously, when we utilize job_defer_to_main_loop_bh to call
12
a function that calls job_completed, job_finalize_single will run
13
in a context where it has recursively taken the aio_context lock,
14
which can cause hangs if it puts down a reference that causes a flush.
15
16
You can observe this in practice by looking at mirror_exit's careful
17
placement of job_completed and bdrv_unref calls.
18
19
If we centralize job exiting, we can signal job completion from outside
20
of the aio_context, which should allow for job cleanup code to run with
21
only one lock, which makes cleanup callbacks less tricky to write.
22
23
Signed-off-by: John Snow <jsnow@redhat.com>
24
Reviewed-by: Max Reitz <mreitz@redhat.com>
25
Message-id: 20180830015734.19765-4-jsnow@redhat.com
26
Reviewed-by: Jeff Cody <jcody@redhat.com>
27
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
---
28
---
7
blockdev.c | 44 ++++++++++++++++++++++++++++++++++----------
29
include/qemu/job.h | 11 +++++++++++
8
1 file changed, 34 insertions(+), 10 deletions(-)
30
job.c | 18 ++++++++++++++++++
31
2 files changed, 29 insertions(+)
9
32
10
diff --git a/blockdev.c b/blockdev.c
33
diff --git a/include/qemu/job.h b/include/qemu/job.h
11
index XXXXXXX..XXXXXXX 100644
34
index XXXXXXX..XXXXXXX 100644
12
--- a/blockdev.c
35
--- a/include/qemu/job.h
13
+++ b/blockdev.c
36
+++ b/include/qemu/job.h
14
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevBackupState {
37
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
15
BlkActionState common;
38
*/
16
BlockDriverState *bs;
39
void (*drain)(Job *job);
17
BlockJob *job;
40
18
- AioContext *aio_context;
41
+ /**
19
} BlockdevBackupState;
42
+ * If the callback is not NULL, exit will be invoked from the main thread
20
43
+ * when the job's coroutine has finished, but before transactional
21
static BlockJob *do_blockdev_backup(BlockdevBackup *backup, BlockJobTxn *txn,
44
+ * convergence; before @prepare or @abort.
22
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
45
+ *
23
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
46
+ * FIXME TODO: This callback is only temporary to transition remaining jobs
24
BlockdevBackup *backup;
47
+ * to prepare/commit/abort/clean callbacks and will be removed before 3.1.
25
BlockDriverState *bs, *target;
48
+ * is released.
26
+ AioContext *aio_context;
49
+ */
27
Error *local_err = NULL;
50
+ void (*exit)(Job *job);
28
29
assert(common->action->type == TRANSACTION_ACTION_KIND_BLOCKDEV_BACKUP);
30
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
31
return;
32
}
33
34
- /* AioContext is released in .clean() */
35
- state->aio_context = bdrv_get_aio_context(bs);
36
- if (state->aio_context != bdrv_get_aio_context(target)) {
37
- state->aio_context = NULL;
38
+ aio_context = bdrv_get_aio_context(bs);
39
+ if (aio_context != bdrv_get_aio_context(target)) {
40
error_setg(errp, "Backup between two IO threads is not implemented");
41
return;
42
}
43
- aio_context_acquire(state->aio_context);
44
+ aio_context_acquire(aio_context);
45
state->bs = bs;
46
+
51
+
47
+ /* Paired with .clean() */
52
/**
48
bdrv_drained_begin(state->bs);
53
* If the callback is not NULL, prepare will be invoked when all the jobs
49
54
* belonging to the same transaction complete; or upon this job's completion
50
state->job = do_blockdev_backup(backup, common->block_job_txn, &local_err);
55
diff --git a/job.c b/job.c
51
if (local_err) {
56
index XXXXXXX..XXXXXXX 100644
52
error_propagate(errp, local_err);
57
--- a/job.c
53
- return;
58
+++ b/job.c
54
+ goto out;
59
@@ -XXX,XX +XXX,XX @@ void job_drain(Job *job)
55
}
56
+
57
+out:
58
+ aio_context_release(aio_context);
59
}
60
61
static void blockdev_backup_commit(BlkActionState *common)
62
{
63
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
64
+ AioContext *aio_context;
65
+
66
+ aio_context = bdrv_get_aio_context(state->bs);
67
+ aio_context_acquire(aio_context);
68
+
69
assert(state->job);
70
block_job_start(state->job);
71
+
72
+ aio_context_release(aio_context);
73
}
74
75
static void blockdev_backup_abort(BlkActionState *common)
76
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_abort(BlkActionState *common)
77
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
78
79
if (state->job) {
80
+ AioContext *aio_context;
81
+
82
+ aio_context = bdrv_get_aio_context(state->bs);
83
+ aio_context_acquire(aio_context);
84
+
85
block_job_cancel_sync(state->job);
86
+
87
+ aio_context_release(aio_context);
88
}
60
}
89
}
61
}
90
62
91
static void blockdev_backup_clean(BlkActionState *common)
63
+static void job_exit(void *opaque)
92
{
64
+{
93
BlockdevBackupState *state = DO_UPCAST(BlockdevBackupState, common, common);
65
+ Job *job = (Job *)opaque;
94
+ AioContext *aio_context;
66
+ AioContext *aio_context = job->aio_context;
95
96
- if (state->aio_context) {
97
- bdrv_drained_end(state->bs);
98
- aio_context_release(state->aio_context);
99
+ if (!state->bs) {
100
+ return;
101
}
102
+
67
+
103
+ aio_context = bdrv_get_aio_context(state->bs);
68
+ if (job->driver->exit) {
104
+ aio_context_acquire(aio_context);
69
+ aio_context_acquire(aio_context);
105
+
70
+ job->driver->exit(job);
106
+ bdrv_drained_end(state->bs);
71
+ aio_context_release(aio_context);
107
+
72
+ }
108
+ aio_context_release(aio_context);
73
+ job_completed(job, job->ret);
74
+}
75
76
/**
77
* All jobs must allow a pause point before entering their job proper. This
78
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque)
79
assert(job && job->driver && job->driver->run);
80
job_pause_point(job);
81
job->ret = job->driver->run(job, &job->err);
82
+ if (!job->deferred_to_main_loop) {
83
+ job->deferred_to_main_loop = true;
84
+ aio_bh_schedule_oneshot(qemu_get_aio_context(),
85
+ job_exit,
86
+ job);
87
+ }
109
}
88
}
110
89
111
typedef struct BlockDirtyBitmapState {
90
112
--
91
--
113
2.14.3
92
2.17.1
114
93
115
94
diff view generated by jsdifflib
1
From: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
The function name of usb_msd_{realize,unrealize}_*,
3
Change the manual deferment to commit_complete into the implicit
4
usb_msd_class_initfn_* are unusual. Rename it to
4
callback to job_exit, renaming commit_complete to commit_exit.
5
usb_msd_*_{realize,unrealize}, usb_msd_class_*_initfn.
6
5
7
Cc: Gerd Hoffmann <kraxel@redhat.com>
6
This conversion does change the timing of when job_completed is
7
called to after the bdrv_replace_node and bdrv_unref calls, which
8
could have implications for bjob->blk which will now be put down
9
after this cleanup.
8
10
9
Signed-off-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
11
Kevin highlights that we did not take any permissions for that backend
10
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
12
at job creation time, so it is safe to reorder these operations.
11
Message-id: 11e6003433abce35f3f4970e1acc71ee92dbcf51.1511317952.git.maozy.fnst@cn.fujitsu.com
13
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
14
Signed-off-by: John Snow <jsnow@redhat.com>
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Message-id: 20180830015734.19765-5-jsnow@redhat.com
17
Reviewed-by: Jeff Cody <jcody@redhat.com>
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
19
---
14
hw/usb/dev-storage.c | 20 ++++++++++----------
20
block/commit.c | 22 +++++-----------------
15
1 file changed, 10 insertions(+), 10 deletions(-)
21
1 file changed, 5 insertions(+), 17 deletions(-)
16
22
17
diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c
23
diff --git a/block/commit.c b/block/commit.c
18
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
19
--- a/hw/usb/dev-storage.c
25
--- a/block/commit.c
20
+++ b/hw/usb/dev-storage.c
26
+++ b/block/commit.c
21
@@ -XXX,XX +XXX,XX @@ static void usb_msd_unrealize_storage(USBDevice *dev, Error **errp)
27
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base,
22
object_unref(OBJECT(&s->bus));
28
return 0;
23
}
29
}
24
30
25
-static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
31
-typedef struct {
26
+static void usb_msd_storage_realize(USBDevice *dev, Error **errp)
32
- int ret;
33
-} CommitCompleteData;
34
-
35
-static void commit_complete(Job *job, void *opaque)
36
+static void commit_exit(Job *job)
27
{
37
{
28
MSDState *s = USB_STORAGE_DEV(dev);
38
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
29
BlockBackend *blk = s->conf.blk;
39
BlockJob *bjob = &s->common;
30
@@ -XXX,XX +XXX,XX @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)
40
- CommitCompleteData *data = opaque;
31
s->scsi_dev = scsi_dev;
41
BlockDriverState *top = blk_bs(s->top);
42
BlockDriverState *base = blk_bs(s->base);
43
BlockDriverState *commit_top_bs = s->commit_top_bs;
44
- int ret = data->ret;
45
bool remove_commit_top_bs = false;
46
47
/* Make sure commit_top_bs and top stay around until bdrv_replace_node() */
48
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
49
* the normal backing chain can be restored. */
50
blk_unref(s->base);
51
52
- if (!job_is_cancelled(job) && ret == 0) {
53
+ if (!job_is_cancelled(job) && job->ret == 0) {
54
/* success */
55
- ret = bdrv_drop_intermediate(s->commit_top_bs, base,
56
- s->backing_file_str);
57
+ job->ret = bdrv_drop_intermediate(s->commit_top_bs, base,
58
+ s->backing_file_str);
59
} else {
60
/* XXX Can (or should) we somehow keep 'consistent read' blocked even
61
* after the failed/cancelled commit job is gone? If we already wrote
62
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
63
* bdrv_set_backing_hd() to fail. */
64
block_job_remove_all_bdrv(bjob);
65
66
- job_completed(job, ret);
67
- g_free(data);
68
-
69
/* If bdrv_drop_intermediate() didn't already do that, remove the commit
70
* filter driver from the backing chain. Do this as the final step so that
71
* the 'consistent read' permission can be granted. */
72
@@ -XXX,XX +XXX,XX @@ static void commit_complete(Job *job, void *opaque)
73
static int coroutine_fn commit_run(Job *job, Error **errp)
74
{
75
CommitBlockJob *s = container_of(job, CommitBlockJob, common.job);
76
- CommitCompleteData *data;
77
int64_t offset;
78
uint64_t delay_ns = 0;
79
int ret = 0;
80
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn commit_run(Job *job, Error **errp)
81
out:
82
qemu_vfree(buf);
83
84
- data = g_malloc(sizeof(*data));
85
- data->ret = ret;
86
- job_defer_to_main_loop(&s->common.job, commit_complete, data);
87
return ret;
32
}
88
}
33
89
34
-static void usb_msd_unrealize_bot(USBDevice *dev, Error **errp)
90
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_job_driver = {
35
+static void usb_msd_bot_unrealize(USBDevice *dev, Error **errp)
91
.user_resume = block_job_user_resume,
36
{
92
.drain = block_job_drain,
37
MSDState *s = USB_STORAGE_DEV(dev);
93
.run = commit_run,
38
94
+ .exit = commit_exit,
39
object_unref(OBJECT(&s->bus));
95
},
40
}
41
42
-static void usb_msd_realize_bot(USBDevice *dev, Error **errp)
43
+static void usb_msd_bot_realize(USBDevice *dev, Error **errp)
44
{
45
MSDState *s = USB_STORAGE_DEV(dev);
46
DeviceState *d = DEVICE(dev);
47
@@ -XXX,XX +XXX,XX @@ static void usb_msd_class_initfn_common(ObjectClass *klass, void *data)
48
dc->vmsd = &vmstate_usb_msd;
49
}
50
51
-static void usb_msd_class_initfn_storage(ObjectClass *klass, void *data)
52
+static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data)
53
{
54
DeviceClass *dc = DEVICE_CLASS(klass);
55
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
56
57
- uc->realize = usb_msd_realize_storage;
58
+ uc->realize = usb_msd_storage_realize;
59
uc->unrealize = usb_msd_unrealize_storage;
60
dc->props = msd_properties;
61
}
62
@@ -XXX,XX +XXX,XX @@ static void usb_msd_instance_init(Object *obj)
63
object_property_set_int(obj, -1, "bootindex", NULL);
64
}
65
66
-static void usb_msd_class_initfn_bot(ObjectClass *klass, void *data)
67
+static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data)
68
{
69
USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
70
71
- uc->realize = usb_msd_realize_bot;
72
- uc->unrealize = usb_msd_unrealize_bot;
73
+ uc->realize = usb_msd_bot_realize;
74
+ uc->unrealize = usb_msd_bot_unrealize;
75
uc->attached_settable = true;
76
}
77
78
static const TypeInfo msd_info = {
79
.name = "usb-storage",
80
.parent = TYPE_USB_STORAGE,
81
- .class_init = usb_msd_class_initfn_storage,
82
+ .class_init = usb_msd_class_storage_initfn,
83
.instance_init = usb_msd_instance_init,
84
};
96
};
85
97
86
static const TypeInfo bot_info = {
87
.name = "usb-bot",
88
.parent = TYPE_USB_STORAGE,
89
- .class_init = usb_msd_class_initfn_bot,
90
+ .class_init = usb_msd_class_bot_initfn,
91
};
92
93
static void usb_msd_register_types(void)
94
--
98
--
95
2.14.3
99
2.17.1
96
100
97
101
diff view generated by jsdifflib
1
From: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
When the function no success value to transmit, it usually make the
3
Change the manual deferment to mirror_exit into the implicit
4
function return void. It has turned out not to be a success, because
4
callback to job_exit and the mirror_exit callback.
5
it means that the extra local_err variable and error_propagate() will
6
be needed. It leads to cumbersome code, therefore, transmit success/
7
failure in the return value is worth.
8
5
9
So fix the return type of blkconf_apply_backend_options(),
6
This does change the order of some bdrv_unref calls and job_completed,
10
blkconf_geometry() and virtio_blk_data_plane_create() to avoid it.
7
but thanks to the new context in which we call .exit, this is safe to
8
defer the possible flushing of any nodes to the job_finalize_single
9
cleanup stage.
11
10
12
Cc: John Snow <jsnow@redhat.com>
11
Signed-off-by: John Snow <jsnow@redhat.com>
13
Cc: Kevin Wolf <kwolf@redhat.com>
12
Message-id: 20180830015734.19765-6-jsnow@redhat.com
14
Cc: Max Reitz <mreitz@redhat.com>
13
Reviewed-by: Max Reitz <mreitz@redhat.com>
15
Cc: Stefan Hajnoczi <stefanha@redhat.com>
14
Reviewed-by: Jeff Cody <jcody@redhat.com>
15
Signed-off-by: Max Reitz <mreitz@redhat.com>
16
---
17
block/mirror.c | 29 +++++++++++------------------
18
1 file changed, 11 insertions(+), 18 deletions(-)
16
19
17
Signed-off-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
20
diff --git a/block/mirror.c b/block/mirror.c
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Message-id: ac0edc1fc70c4457e5cec94405eb7d1f89f9c2c1.1511317952.git.maozy.fnst@cn.fujitsu.com
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
---
22
hw/block/dataplane/virtio-blk.h | 2 +-
23
include/hw/block/block.h | 4 ++--
24
hw/block/block.c | 15 +++++++++------
25
hw/block/dataplane/virtio-blk.c | 12 +++++++-----
26
4 files changed, 19 insertions(+), 14 deletions(-)
27
28
diff --git a/hw/block/dataplane/virtio-blk.h b/hw/block/dataplane/virtio-blk.h
29
index XXXXXXX..XXXXXXX 100644
21
index XXXXXXX..XXXXXXX 100644
30
--- a/hw/block/dataplane/virtio-blk.h
22
--- a/block/mirror.c
31
+++ b/hw/block/dataplane/virtio-blk.h
23
+++ b/block/mirror.c
32
@@ -XXX,XX +XXX,XX @@
24
@@ -XXX,XX +XXX,XX @@ static void mirror_wait_for_all_io(MirrorBlockJob *s)
33
34
typedef struct VirtIOBlockDataPlane VirtIOBlockDataPlane;
35
36
-void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
37
+bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
38
VirtIOBlockDataPlane **dataplane,
39
Error **errp);
40
void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s);
41
diff --git a/include/hw/block/block.h b/include/hw/block/block.h
42
index XXXXXXX..XXXXXXX 100644
43
--- a/include/hw/block/block.h
44
+++ b/include/hw/block/block.h
45
@@ -XXX,XX +XXX,XX @@ static inline unsigned int get_physical_block_exp(BlockConf *conf)
46
/* Configuration helpers */
47
48
void blkconf_serial(BlockConf *conf, char **serial);
49
-void blkconf_geometry(BlockConf *conf, int *trans,
50
+bool blkconf_geometry(BlockConf *conf, int *trans,
51
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
52
Error **errp);
53
void blkconf_blocksizes(BlockConf *conf);
54
-void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
55
+bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
56
bool resizable, Error **errp);
57
58
/* Hard disk geometry */
59
diff --git a/hw/block/block.c b/hw/block/block.c
60
index XXXXXXX..XXXXXXX 100644
61
--- a/hw/block/block.c
62
+++ b/hw/block/block.c
63
@@ -XXX,XX +XXX,XX @@ void blkconf_blocksizes(BlockConf *conf)
64
}
25
}
65
}
26
}
66
27
67
-void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
28
-typedef struct {
68
+bool blkconf_apply_backend_options(BlockConf *conf, bool readonly,
29
- int ret;
69
bool resizable, Error **errp)
30
-} MirrorExitData;
31
-
32
-static void mirror_exit(Job *job, void *opaque)
33
+static void mirror_exit(Job *job)
70
{
34
{
71
BlockBackend *blk = conf->blk;
35
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
72
@@ -XXX,XX +XXX,XX @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
36
BlockJob *bjob = &s->common;
73
37
- MirrorExitData *data = opaque;
74
ret = blk_set_perm(blk, perm, shared_perm, errp);
38
MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque;
75
if (ret < 0) {
39
AioContext *replace_aio_context = NULL;
76
- return;
40
BlockDriverState *src = s->mirror_top_bs->backing->bs;
77
+ return false;
41
BlockDriverState *target_bs = blk_bs(s->target);
78
}
42
BlockDriverState *mirror_top_bs = s->mirror_top_bs;
79
43
Error *local_err = NULL;
80
switch (conf->wce) {
44
+ int ret = job->ret;
81
@@ -XXX,XX +XXX,XX @@ void blkconf_apply_backend_options(BlockConf *conf, bool readonly,
45
82
46
bdrv_release_dirty_bitmap(src, s->dirty_bitmap);
83
blk_set_enable_write_cache(blk, wce);
47
84
blk_set_on_error(blk, rerror, werror);
48
- /* Make sure that the source BDS doesn't go away before we called
85
+
49
- * job_completed(). */
86
+ return true;
50
+ /* Make sure that the source BDS doesn't go away during bdrv_replace_node,
87
}
51
+ * before we can call bdrv_drained_end */
88
52
bdrv_ref(src);
89
-void blkconf_geometry(BlockConf *conf, int *ptrans,
53
bdrv_ref(mirror_top_bs);
90
+bool blkconf_geometry(BlockConf *conf, int *ptrans,
54
bdrv_ref(target_bs);
91
unsigned cyls_max, unsigned heads_max, unsigned secs_max,
55
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
92
Error **errp)
56
bdrv_set_backing_hd(target_bs, backing, &local_err);
93
{
57
if (local_err) {
94
@@ -XXX,XX +XXX,XX @@ void blkconf_geometry(BlockConf *conf, int *ptrans,
58
error_report_err(local_err);
95
if (conf->cyls || conf->heads || conf->secs) {
59
- data->ret = -EPERM;
96
if (conf->cyls < 1 || conf->cyls > cyls_max) {
60
+ ret = -EPERM;
97
error_setg(errp, "cyls must be between 1 and %u", cyls_max);
61
}
98
- return;
99
+ return false;
100
}
101
if (conf->heads < 1 || conf->heads > heads_max) {
102
error_setg(errp, "heads must be between 1 and %u", heads_max);
103
- return;
104
+ return false;
105
}
106
if (conf->secs < 1 || conf->secs > secs_max) {
107
error_setg(errp, "secs must be between 1 and %u", secs_max);
108
- return;
109
+ return false;
110
}
62
}
111
}
63
}
112
+ return true;
64
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
113
}
65
aio_context_acquire(replace_aio_context);
114
diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c
66
}
115
index XXXXXXX..XXXXXXX 100644
67
116
--- a/hw/block/dataplane/virtio-blk.c
68
- if (s->should_complete && data->ret == 0) {
117
+++ b/hw/block/dataplane/virtio-blk.c
69
+ if (s->should_complete && ret == 0) {
118
@@ -XXX,XX +XXX,XX @@ static void notify_guest_bh(void *opaque)
70
BlockDriverState *to_replace = src;
119
}
71
if (s->to_replace) {
120
72
to_replace = s->to_replace;
121
/* Context: QEMU global mutex held */
73
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
122
-void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
74
bdrv_drained_end(target_bs);
123
+bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
75
if (local_err) {
124
VirtIOBlockDataPlane **dataplane,
76
error_report_err(local_err);
125
Error **errp)
77
- data->ret = -EPERM;
126
{
78
+ ret = -EPERM;
127
@@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
128
error_setg(errp,
129
"device is incompatible with iothread "
130
"(transport does not support notifiers)");
131
- return;
132
+ return false;
133
}
134
if (!virtio_device_ioeventfd_enabled(vdev)) {
135
error_setg(errp, "ioeventfd is required for iothread");
136
- return;
137
+ return false;
138
}
139
140
/* If dataplane is (re-)enabled while the guest is running there could
141
@@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
142
*/
143
if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
144
error_prepend(errp, "cannot start virtio-blk dataplane: ");
145
- return;
146
+ return false;
147
}
79
}
148
}
80
}
149
/* Don't try if transport does not support notifiers. */
81
if (s->to_replace) {
150
if (!virtio_device_ioeventfd_enabled(vdev)) {
82
@@ -XXX,XX +XXX,XX @@ static void mirror_exit(Job *job, void *opaque)
151
- return;
83
blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort);
152
+ return false;
84
85
bs_opaque->job = NULL;
86
- job_completed(job, data->ret);
87
88
- g_free(data);
89
bdrv_drained_end(src);
90
bdrv_unref(mirror_top_bs);
91
bdrv_unref(src);
92
+
93
+ job->ret = ret;
94
}
95
96
static void mirror_throttle(MirrorBlockJob *s)
97
@@ -XXX,XX +XXX,XX @@ static int mirror_flush(MirrorBlockJob *s)
98
static int coroutine_fn mirror_run(Job *job, Error **errp)
99
{
100
MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job);
101
- MirrorExitData *data;
102
BlockDriverState *bs = s->mirror_top_bs->backing->bs;
103
BlockDriverState *target_bs = blk_bs(s->target);
104
bool need_drain = true;
105
@@ -XXX,XX +XXX,XX @@ immediate_exit:
106
g_free(s->in_flight_bitmap);
107
bdrv_dirty_iter_free(s->dbi);
108
109
- data = g_malloc(sizeof(*data));
110
- data->ret = ret;
111
-
112
if (need_drain) {
113
bdrv_drained_begin(bs);
153
}
114
}
154
115
155
s = g_new0(VirtIOBlockDataPlane, 1);
116
- job_defer_to_main_loop(&s->common.job, mirror_exit, data);
156
@@ -XXX,XX +XXX,XX @@ void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
117
return ret;
157
s->batch_notify_vqs = bitmap_new(conf->num_queues);
158
159
*dataplane = s;
160
+
161
+ return true;
162
}
118
}
163
119
164
/* Context: QEMU global mutex held */
120
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver mirror_job_driver = {
121
.user_resume = block_job_user_resume,
122
.drain = block_job_drain,
123
.run = mirror_run,
124
+ .exit = mirror_exit,
125
.pause = mirror_pause,
126
.complete = mirror_complete,
127
},
128
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver commit_active_job_driver = {
129
.user_resume = block_job_user_resume,
130
.drain = block_job_drain,
131
.run = mirror_run,
132
+ .exit = mirror_exit,
133
.pause = mirror_pause,
134
.complete = mirror_complete,
135
},
165
--
136
--
166
2.14.3
137
2.17.1
167
138
168
139
diff view generated by jsdifflib
1
From: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
Convert nvme_init() to realize and rename it to nvme_realize().
3
Utilize the job_exit shim by not calling job_defer_to_main_loop, and
4
4
where applicable, converting the deferred callback into the job_exit
5
Cc: John Snow <jsnow@redhat.com>
5
callback.
6
Cc: Keith Busch <keith.busch@intel.com>
6
7
Cc: Kevin Wolf <kwolf@redhat.com>
7
This converts backup, stream, create, and the unit tests all at once.
8
Cc: Max Reitz <mreitz@redhat.com>
8
Most of these jobs do not see any changes to the order in which they
9
Cc: Markus Armbruster <armbru@redhat.com>
9
clean up their resources, except the test-blockjob-txn test, which
10
10
now puts down its bs before job_completed is called.
11
Signed-off-by: Mao Zhongyi <maozy.fnst@cn.fujitsu.com>
11
12
Message-id: 2882e72d795e04cbe2120f569d551aef2467ac60.1511317952.git.maozy.fnst@cn.fujitsu.com
12
This is safe for the same reason the reordering in the mirror job is
13
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
safe, because job_completed no longer runs under two locks, making
14
the unref safe even if it causes a flush.
15
16
Signed-off-by: John Snow <jsnow@redhat.com>
17
Reviewed-by: Max Reitz <mreitz@redhat.com>
18
Message-id: 20180830015734.19765-7-jsnow@redhat.com
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
20
---
15
hw/block/nvme.c | 18 ++++++++++--------
21
block/backup.c | 16 ----------------
16
1 file changed, 10 insertions(+), 8 deletions(-)
22
block/create.c | 14 +++-----------
17
23
block/stream.c | 22 +++++++---------------
18
diff --git a/hw/block/nvme.c b/hw/block/nvme.c
24
tests/test-bdrv-drain.c | 6 ------
19
index XXXXXXX..XXXXXXX 100644
25
tests/test-blockjob-txn.c | 11 ++---------
20
--- a/hw/block/nvme.c
26
tests/test-blockjob.c | 10 ++++------
21
+++ b/hw/block/nvme.c
27
6 files changed, 16 insertions(+), 63 deletions(-)
22
@@ -XXX,XX +XXX,XX @@ static const MemoryRegionOps nvme_cmb_ops = {
28
29
diff --git a/block/backup.c b/block/backup.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/block/backup.c
32
+++ b/block/backup.c
33
@@ -XXX,XX +XXX,XX @@ static BlockErrorAction backup_error_action(BackupBlockJob *job,
34
}
35
}
36
37
-typedef struct {
38
- int ret;
39
-} BackupCompleteData;
40
-
41
-static void backup_complete(Job *job, void *opaque)
42
-{
43
- BackupCompleteData *data = opaque;
44
-
45
- job_completed(job, data->ret);
46
- g_free(data);
47
-}
48
-
49
static bool coroutine_fn yield_and_check(BackupBlockJob *job)
50
{
51
uint64_t delay_ns;
52
@@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
53
static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
54
{
55
BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job);
56
- BackupCompleteData *data;
57
BlockDriverState *bs = blk_bs(job->common.blk);
58
int64_t offset, nb_clusters;
59
int ret = 0;
60
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
61
qemu_co_rwlock_unlock(&job->flush_rwlock);
62
hbitmap_free(job->copy_bitmap);
63
64
- data = g_malloc(sizeof(*data));
65
- data->ret = ret;
66
- job_defer_to_main_loop(&job->common.job, backup_complete, data);
67
return ret;
68
}
69
70
diff --git a/block/create.c b/block/create.c
71
index XXXXXXX..XXXXXXX 100644
72
--- a/block/create.c
73
+++ b/block/create.c
74
@@ -XXX,XX +XXX,XX @@ typedef struct BlockdevCreateJob {
75
Job common;
76
BlockDriver *drv;
77
BlockdevCreateOptions *opts;
78
- int ret;
79
} BlockdevCreateJob;
80
81
-static void blockdev_create_complete(Job *job, void *opaque)
82
-{
83
- BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
84
-
85
- job_completed(job, s->ret);
86
-}
87
-
88
static int coroutine_fn blockdev_create_run(Job *job, Error **errp)
89
{
90
BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common);
91
+ int ret;
92
93
job_progress_set_remaining(&s->common, 1);
94
- s->ret = s->drv->bdrv_co_create(s->opts, errp);
95
+ ret = s->drv->bdrv_co_create(s->opts, errp);
96
job_progress_update(&s->common, 1);
97
98
qapi_free_BlockdevCreateOptions(s->opts);
99
- job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL);
100
101
- return s->ret;
102
+ return ret;
103
}
104
105
static const JobDriver blockdev_create_job_driver = {
106
diff --git a/block/stream.c b/block/stream.c
107
index XXXXXXX..XXXXXXX 100644
108
--- a/block/stream.c
109
+++ b/block/stream.c
110
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_populate(BlockBackend *blk,
111
return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ);
112
}
113
114
-typedef struct {
115
- int ret;
116
-} StreamCompleteData;
117
-
118
-static void stream_complete(Job *job, void *opaque)
119
+static void stream_exit(Job *job)
120
{
121
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
122
BlockJob *bjob = &s->common;
123
- StreamCompleteData *data = opaque;
124
BlockDriverState *bs = blk_bs(bjob->blk);
125
BlockDriverState *base = s->base;
126
Error *local_err = NULL;
127
+ int ret = job->ret;
128
129
- if (!job_is_cancelled(job) && bs->backing && data->ret == 0) {
130
+ if (!job_is_cancelled(job) && bs->backing && ret == 0) {
131
const char *base_id = NULL, *base_fmt = NULL;
132
if (base) {
133
base_id = s->backing_file_str;
134
@@ -XXX,XX +XXX,XX @@ static void stream_complete(Job *job, void *opaque)
135
base_fmt = base->drv->format_name;
136
}
137
}
138
- data->ret = bdrv_change_backing_file(bs, base_id, base_fmt);
139
+ ret = bdrv_change_backing_file(bs, base_id, base_fmt);
140
bdrv_set_backing_hd(bs, base, &local_err);
141
if (local_err) {
142
error_report_err(local_err);
143
- data->ret = -EPERM;
144
+ ret = -EPERM;
145
goto out;
146
}
147
}
148
@@ -XXX,XX +XXX,XX @@ out:
149
}
150
151
g_free(s->backing_file_str);
152
- job_completed(job, data->ret);
153
- g_free(data);
154
+ job->ret = ret;
155
}
156
157
static int coroutine_fn stream_run(Job *job, Error **errp)
158
{
159
StreamBlockJob *s = container_of(job, StreamBlockJob, common.job);
160
- StreamCompleteData *data;
161
BlockBackend *blk = s->common.blk;
162
BlockDriverState *bs = blk_bs(blk);
163
BlockDriverState *base = s->base;
164
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn stream_run(Job *job, Error **errp)
165
166
out:
167
/* Modify backing chain and close BDSes in main loop */
168
- data = g_malloc(sizeof(*data));
169
- data->ret = ret;
170
- job_defer_to_main_loop(&s->common.job, stream_complete, data);
171
return ret;
172
}
173
174
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver stream_job_driver = {
175
.job_type = JOB_TYPE_STREAM,
176
.free = block_job_free,
177
.run = stream_run,
178
+ .exit = stream_exit,
179
.user_resume = block_job_user_resume,
180
.drain = block_job_drain,
181
},
182
diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c
183
index XXXXXXX..XXXXXXX 100644
184
--- a/tests/test-bdrv-drain.c
185
+++ b/tests/test-bdrv-drain.c
186
@@ -XXX,XX +XXX,XX @@ typedef struct TestBlockJob {
187
bool should_complete;
188
} TestBlockJob;
189
190
-static void test_job_completed(Job *job, void *opaque)
191
-{
192
- job_completed(job, 0);
193
-}
194
-
195
static int coroutine_fn test_job_run(Job *job, Error **errp)
196
{
197
TestBlockJob *s = container_of(job, TestBlockJob, common.job);
198
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_job_run(Job *job, Error **errp)
199
job_pause_point(&s->common.job);
200
}
201
202
- job_defer_to_main_loop(&s->common.job, test_job_completed, NULL);
203
return 0;
204
}
205
206
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
207
index XXXXXXX..XXXXXXX 100644
208
--- a/tests/test-blockjob-txn.c
209
+++ b/tests/test-blockjob-txn.c
210
@@ -XXX,XX +XXX,XX @@ typedef struct {
211
int *result;
212
} TestBlockJob;
213
214
-static void test_block_job_complete(Job *job, void *opaque)
215
+static void test_block_job_exit(Job *job)
216
{
217
BlockJob *bjob = container_of(job, BlockJob, job);
218
BlockDriverState *bs = blk_bs(bjob->blk);
219
- int rc = (intptr_t)opaque;
220
221
- if (job_is_cancelled(job)) {
222
- rc = -ECANCELED;
223
- }
224
-
225
- job_completed(job, rc);
226
bdrv_unref(bs);
227
}
228
229
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn test_block_job_run(Job *job, Error **errp)
230
}
231
}
232
233
- job_defer_to_main_loop(job, test_block_job_complete,
234
- (void *)(intptr_t)s->rc);
235
return s->rc;
236
}
237
238
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_block_job_driver = {
239
.user_resume = block_job_user_resume,
240
.drain = block_job_drain,
241
.run = test_block_job_run,
242
+ .exit = test_block_job_exit,
23
},
243
},
24
};
244
};
25
245
26
-static int nvme_init(PCIDevice *pci_dev)
246
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
27
+static void nvme_realize(PCIDevice *pci_dev, Error **errp)
247
index XXXXXXX..XXXXXXX 100644
28
{
248
--- a/tests/test-blockjob.c
29
NvmeCtrl *n = NVME(pci_dev);
249
+++ b/tests/test-blockjob.c
30
NvmeIdCtrl *id = &n->id_ctrl;
250
@@ -XXX,XX +XXX,XX @@ typedef struct CancelJob {
31
@@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev)
251
bool completed;
32
Error *local_err = NULL;
252
} CancelJob;
33
253
34
if (!n->conf.blk) {
254
-static void cancel_job_completed(Job *job, void *opaque)
35
- return -1;
255
+static void cancel_job_exit(Job *job)
36
+ error_setg(errp, "drive property not set");
256
{
37
+ return;
257
- CancelJob *s = opaque;
38
}
258
+ CancelJob *s = container_of(job, CancelJob, common.job);
39
259
s->completed = true;
40
bs_size = blk_getlength(n->conf.blk);
260
- job_completed(job, 0);
41
if (bs_size < 0) {
261
}
42
- return -1;
262
43
+ error_setg(errp, "could not get backing file size");
263
static void cancel_job_complete(Job *job, Error **errp)
44
+ return;
264
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp)
45
}
265
46
266
while (!s->should_complete) {
47
blkconf_serial(&n->conf, &n->serial);
267
if (job_is_cancelled(&s->common.job)) {
48
if (!n->serial) {
268
- goto defer;
49
- return -1;
269
+ return 0;
50
+ error_setg(errp, "serial property not set");
270
}
51
+ return;
271
52
}
272
if (!job_is_ready(&s->common.job) && s->should_converge) {
53
blkconf_blocksizes(&n->conf);
273
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn cancel_job_run(Job *job, Error **errp)
54
blkconf_apply_backend_options(&n->conf, blk_is_read_only(n->conf.blk),
274
job_sleep_ns(&s->common.job, 100000);
55
false, &local_err);
275
}
56
if (local_err) {
276
57
- error_report_err(local_err);
277
- defer:
58
- return -1;
278
- job_defer_to_main_loop(&s->common.job, cancel_job_completed, s);
59
+ error_propagate(errp, local_err);
279
return 0;
60
+ return;
280
}
61
}
281
62
282
@@ -XXX,XX +XXX,XX @@ static const BlockJobDriver test_cancel_driver = {
63
pci_conf = pci_dev->config;
283
.user_resume = block_job_user_resume,
64
@@ -XXX,XX +XXX,XX @@ static int nvme_init(PCIDevice *pci_dev)
284
.drain = block_job_drain,
65
cpu_to_le64(n->ns_size >>
285
.run = cancel_job_run,
66
id_ns->lbaf[NVME_ID_NS_FLBAS_INDEX(ns->id_ns.flbas)].ds);
286
+ .exit = cancel_job_exit,
67
}
287
.complete = cancel_job_complete,
68
- return 0;
288
},
69
}
289
};
70
71
static void nvme_exit(PCIDevice *pci_dev)
72
@@ -XXX,XX +XXX,XX @@ static void nvme_class_init(ObjectClass *oc, void *data)
73
DeviceClass *dc = DEVICE_CLASS(oc);
74
PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc);
75
76
- pc->init = nvme_init;
77
+ pc->realize = nvme_realize;
78
pc->exit = nvme_exit;
79
pc->class_id = PCI_CLASS_STORAGE_EXPRESS;
80
pc->vendor_id = PCI_VENDOR_ID_INTEL;
81
--
290
--
82
2.14.3
291
2.17.1
83
292
84
293
diff view generated by jsdifflib
Deleted patch
1
Commit 1351d1ec89eabebc9fdff20451a62c413d7accc1 ("qdev: drop iothread
2
property type") forgot to remove this include.
3
1
4
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Message-id: 20171205133954.31006-1-stefanha@redhat.com
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
8
hw/core/qdev-properties-system.c | 1 -
9
1 file changed, 1 deletion(-)
10
11
diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c
12
index XXXXXXX..XXXXXXX 100644
13
--- a/hw/core/qdev-properties-system.c
14
+++ b/hw/core/qdev-properties-system.c
15
@@ -XXX,XX +XXX,XX @@
16
#include "qapi/visitor.h"
17
#include "chardev/char-fe.h"
18
#include "sysemu/tpm_backend.h"
19
-#include "sysemu/iothread.h"
20
21
static void get_pointer(Object *obj, Visitor *v, Property *prop,
22
char *(*print)(void *ptr),
23
--
24
2.14.3
25
26
diff view generated by jsdifflib
Deleted patch
1
bdrv_unref() requires the AioContext lock because bdrv_flush() uses
2
BDRV_POLL_WHILE(), which assumes the AioContext is currently held. If
3
BDRV_POLL_WHILE() runs without AioContext held the
4
pthread_mutex_unlock() call in aio_context_release() fails.
5
1
6
This patch moves bdrv_unref() into the AioContext locked region to solve
7
the following pthread_mutex_unlock() failure:
8
9
#0 0x00007f566181969b in raise () at /lib64/libc.so.6
10
#1 0x00007f566181b3b1 in abort () at /lib64/libc.so.6
11
#2 0x00005592cd590458 in error_exit (err=<optimized out>, msg=msg@entry=0x5592cdaf6d60 <__func__.23977> "qemu_mutex_unlock") at util/qemu-thread-posix.c:36
12
#3 0x00005592cd96e738 in qemu_mutex_unlock (mutex=mutex@entry=0x5592ce9505e0) at util/qemu-thread-posix.c:96
13
#4 0x00005592cd969b69 in aio_context_release (ctx=ctx@entry=0x5592ce950580) at util/async.c:507
14
#5 0x00005592cd8ead78 in bdrv_flush (bs=bs@entry=0x5592cfa87210) at block/io.c:2478
15
#6 0x00005592cd89df30 in bdrv_close (bs=0x5592cfa87210) at block.c:3207
16
#7 0x00005592cd89df30 in bdrv_delete (bs=0x5592cfa87210) at block.c:3395
17
#8 0x00005592cd89df30 in bdrv_unref (bs=0x5592cfa87210) at block.c:4418
18
#9 0x00005592cd6b7f86 in qmp_transaction (dev_list=<optimized out>, has_props=<optimized out>, props=<optimized out>, errp=errp@entry=0x7ffe4a1fc9d8) at blockdev.c:2308
19
20
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
21
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
22
Reviewed-by: Eric Blake <eblake@redhat.com>
23
Message-id: 20171206144550.22295-2-stefanha@redhat.com
24
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
25
---
26
blockdev.c | 2 +-
27
1 file changed, 1 insertion(+), 1 deletion(-)
28
29
diff --git a/blockdev.c b/blockdev.c
30
index XXXXXXX..XXXXXXX 100644
31
--- a/blockdev.c
32
+++ b/blockdev.c
33
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(BlkActionState *common)
34
DO_UPCAST(ExternalSnapshotState, common, common);
35
if (state->aio_context) {
36
bdrv_drained_end(state->old_bs);
37
- aio_context_release(state->aio_context);
38
bdrv_unref(state->new_bs);
39
+ aio_context_release(state->aio_context);
40
}
41
}
42
43
--
44
2.14.3
45
46
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
BDRV_POLL_WHILE() does not support recursive AioContext locking. It
3
Rename opaque_job to job to be consistent with other job implementations.
4
only releases the AioContext lock once regardless of how many times the
4
Rename 'job', the BackupBlockJob object, to 's' to also be consistent.
5
caller has acquired it. This results in a hang since the IOThread does
6
not make progress while the AioContext is still locked.
7
5
8
The following steps trigger the hang:
6
Suggested-by: Eric Blake <eblake@redhat.com>
7
Signed-off-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-id: 20180830015734.19765-8-jsnow@redhat.com
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
block/backup.c | 62 +++++++++++++++++++++++++-------------------------
13
1 file changed, 31 insertions(+), 31 deletions(-)
9
14
10
$ qemu-system-x86_64 -M accel=kvm -m 1G -cpu host \
15
diff --git a/block/backup.c b/block/backup.c
11
-object iothread,id=iothread0 \
12
-device virtio-scsi-pci,iothread=iothread0 \
13
-drive if=none,id=drive0,file=test.img,format=raw \
14
-device scsi-hd,drive=drive0 \
15
-drive if=none,id=drive1,file=test.img,format=raw \
16
-device scsi-hd,drive=drive1
17
$ qemu-system-x86_64 ...same options... \
18
-incoming tcp::1234
19
(qemu) migrate tcp:127.0.0.1:1234
20
...hang...
21
22
Tested-by: Stefan Hajnoczi <stefanha@redhat.com>
23
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
24
Reviewed-by: Eric Blake <eblake@redhat.com>
25
Message-id: 20171207201320.19284-2-stefanha@redhat.com
26
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
27
---
28
block.c | 14 +++++++++++---
29
1 file changed, 11 insertions(+), 3 deletions(-)
30
31
diff --git a/block.c b/block.c
32
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
33
--- a/block.c
17
--- a/block/backup.c
34
+++ b/block.c
18
+++ b/block/backup.c
35
@@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void)
19
@@ -XXX,XX +XXX,XX @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job)
36
BdrvNextIterator it;
20
bdrv_dirty_iter_free(dbi);
21
}
22
23
-static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
24
+static int coroutine_fn backup_run(Job *job, Error **errp)
25
{
26
- BackupBlockJob *job = container_of(opaque_job, BackupBlockJob, common.job);
27
- BlockDriverState *bs = blk_bs(job->common.blk);
28
+ BackupBlockJob *s = container_of(job, BackupBlockJob, common.job);
29
+ BlockDriverState *bs = blk_bs(s->common.blk);
30
int64_t offset, nb_clusters;
37
int ret = 0;
31
int ret = 0;
38
int pass;
32
39
+ GSList *aio_ctxs = NULL, *ctx;
33
- QLIST_INIT(&job->inflight_reqs);
40
34
- qemu_co_rwlock_init(&job->flush_rwlock);
41
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
35
+ QLIST_INIT(&s->inflight_reqs);
42
- aio_context_acquire(bdrv_get_aio_context(bs));
36
+ qemu_co_rwlock_init(&s->flush_rwlock);
43
+ AioContext *aio_context = bdrv_get_aio_context(bs);
37
44
+
38
- nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size);
45
+ if (!g_slist_find(aio_ctxs, aio_context)) {
39
- job_progress_set_remaining(&job->common.job, job->len);
46
+ aio_ctxs = g_slist_prepend(aio_ctxs, aio_context);
40
+ nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size);
47
+ aio_context_acquire(aio_context);
41
+ job_progress_set_remaining(job, s->len);
48
+ }
42
43
- job->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
44
- if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
45
- backup_incremental_init_copy_bitmap(job);
46
+ s->copy_bitmap = hbitmap_alloc(nb_clusters, 0);
47
+ if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
48
+ backup_incremental_init_copy_bitmap(s);
49
} else {
50
- hbitmap_set(job->copy_bitmap, 0, nb_clusters);
51
+ hbitmap_set(s->copy_bitmap, 0, nb_clusters);
49
}
52
}
50
53
51
/* We do two passes of inactivation. The first pass calls to drivers'
54
52
@@ -XXX,XX +XXX,XX @@ int bdrv_inactivate_all(void)
55
- job->before_write.notify = backup_before_write_notify;
56
- bdrv_add_before_write_notifier(bs, &job->before_write);
57
+ s->before_write.notify = backup_before_write_notify;
58
+ bdrv_add_before_write_notifier(bs, &s->before_write);
59
60
- if (job->sync_mode == MIRROR_SYNC_MODE_NONE) {
61
+ if (s->sync_mode == MIRROR_SYNC_MODE_NONE) {
62
/* All bits are set in copy_bitmap to allow any cluster to be copied.
63
* This does not actually require them to be copied. */
64
- while (!job_is_cancelled(&job->common.job)) {
65
+ while (!job_is_cancelled(job)) {
66
/* Yield until the job is cancelled. We just let our before_write
67
* notify callback service CoW requests. */
68
- job_yield(&job->common.job);
69
+ job_yield(job);
70
}
71
- } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
72
- ret = backup_run_incremental(job);
73
+ } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) {
74
+ ret = backup_run_incremental(s);
75
} else {
76
/* Both FULL and TOP SYNC_MODE's require copying.. */
77
- for (offset = 0; offset < job->len;
78
- offset += job->cluster_size) {
79
+ for (offset = 0; offset < s->len;
80
+ offset += s->cluster_size) {
81
bool error_is_read;
82
int alloced = 0;
83
84
- if (yield_and_check(job)) {
85
+ if (yield_and_check(s)) {
86
break;
87
}
88
89
- if (job->sync_mode == MIRROR_SYNC_MODE_TOP) {
90
+ if (s->sync_mode == MIRROR_SYNC_MODE_TOP) {
91
int i;
92
int64_t n;
93
94
/* Check to see if these blocks are already in the
95
* backing file. */
96
97
- for (i = 0; i < job->cluster_size;) {
98
+ for (i = 0; i < s->cluster_size;) {
99
/* bdrv_is_allocated() only returns true/false based
100
* on the first set of sectors it comes across that
101
* are are all in the same state.
102
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
103
* needed but at some point that is always the case. */
104
alloced =
105
bdrv_is_allocated(bs, offset + i,
106
- job->cluster_size - i, &n);
107
+ s->cluster_size - i, &n);
108
i += n;
109
110
if (alloced || n == 0) {
111
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn backup_run(Job *opaque_job, Error **errp)
112
if (alloced < 0) {
113
ret = alloced;
114
} else {
115
- ret = backup_do_cow(job, offset, job->cluster_size,
116
+ ret = backup_do_cow(s, offset, s->cluster_size,
117
&error_is_read, false);
118
}
119
if (ret < 0) {
120
/* Depending on error action, fail now or retry cluster */
121
BlockErrorAction action =
122
- backup_error_action(job, error_is_read, -ret);
123
+ backup_error_action(s, error_is_read, -ret);
124
if (action == BLOCK_ERROR_ACTION_REPORT) {
125
break;
126
} else {
127
- offset -= job->cluster_size;
128
+ offset -= s->cluster_size;
129
continue;
130
}
131
}
132
}
53
}
133
}
54
134
55
out:
135
- notifier_with_return_remove(&job->before_write);
56
- for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
136
+ notifier_with_return_remove(&s->before_write);
57
- aio_context_release(bdrv_get_aio_context(bs));
137
58
+ for (ctx = aio_ctxs; ctx != NULL; ctx = ctx->next) {
138
/* wait until pending backup_do_cow() calls have completed */
59
+ AioContext *aio_context = ctx->data;
139
- qemu_co_rwlock_wrlock(&job->flush_rwlock);
60
+ aio_context_release(aio_context);
140
- qemu_co_rwlock_unlock(&job->flush_rwlock);
61
}
141
- hbitmap_free(job->copy_bitmap);
62
+ g_slist_free(aio_ctxs);
142
+ qemu_co_rwlock_wrlock(&s->flush_rwlock);
143
+ qemu_co_rwlock_unlock(&s->flush_rwlock);
144
+ hbitmap_free(s->copy_bitmap);
63
145
64
return ret;
146
return ret;
65
}
147
}
66
--
148
--
67
2.14.3
149
2.17.1
68
150
69
151
diff view generated by jsdifflib
1
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
2
3
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Jobs are now expected to return their retcode on the stack, from the
4
Message-id: 20171206144550.22295-4-stefanha@redhat.com
4
.run callback, so we can remove that argument.
5
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
6
job_cancel does not need to set -ECANCELED because job_completed will
7
update the return code itself if the job was canceled.
8
9
While we're here, make job_completed static to job.c and remove it from
10
job.h; move the documentation of return code to the .run() callback and
11
to the job->ret property, accordingly.
12
13
Signed-off-by: John Snow <jsnow@redhat.com>
14
Message-id: 20180830015734.19765-9-jsnow@redhat.com
15
Reviewed-by: Max Reitz <mreitz@redhat.com>
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
---
17
---
7
blockdev.c | 42 ++++++++++++++++++++++++++++++++++--------
18
include/qemu/job.h | 28 +++++++++++++++-------------
8
1 file changed, 34 insertions(+), 8 deletions(-)
19
job.c | 11 ++++++-----
20
trace-events | 2 +-
21
3 files changed, 22 insertions(+), 19 deletions(-)
9
22
10
diff --git a/blockdev.c b/blockdev.c
23
diff --git a/include/qemu/job.h b/include/qemu/job.h
11
index XXXXXXX..XXXXXXX 100644
24
index XXXXXXX..XXXXXXX 100644
12
--- a/blockdev.c
25
--- a/include/qemu/job.h
13
+++ b/blockdev.c
26
+++ b/include/qemu/job.h
14
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_clean(BlkActionState *common)
27
@@ -XXX,XX +XXX,XX @@ typedef struct Job {
15
typedef struct DriveBackupState {
28
/** Estimated progress_current value at the completion of the job */
16
BlkActionState common;
29
int64_t progress_total;
17
BlockDriverState *bs;
30
18
- AioContext *aio_context;
31
- /** ret code passed to job_completed. */
19
BlockJob *job;
32
+ /**
20
} DriveBackupState;
33
+ * Return code from @run and/or @prepare callback(s).
21
34
+ * Not final until the job has reached the CONCLUDED status.
22
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
35
+ * 0 on success, -errno on failure.
23
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
36
+ */
24
BlockDriverState *bs;
37
int ret;
25
DriveBackup *backup;
38
26
+ AioContext *aio_context;
39
/**
27
Error *local_err = NULL;
40
@@ -XXX,XX +XXX,XX @@ struct JobDriver {
28
41
/** Enum describing the operation */
29
assert(common->action->type == TRANSACTION_ACTION_KIND_DRIVE_BACKUP);
42
JobType job_type;
30
@@ -XXX,XX +XXX,XX @@ static void drive_backup_prepare(BlkActionState *common, Error **errp)
43
31
return;
44
- /** Mandatory: Entrypoint for the Coroutine. */
32
}
45
+ /**
33
46
+ * Mandatory: Entrypoint for the Coroutine.
34
- /* AioContext is released in .clean() */
47
+ *
35
- state->aio_context = bdrv_get_aio_context(bs);
48
+ * This callback will be invoked when moving from CREATED to RUNNING.
36
- aio_context_acquire(state->aio_context);
49
+ *
37
+ aio_context = bdrv_get_aio_context(bs);
50
+ * If this callback returns nonzero, the job transaction it is part of is
38
+ aio_context_acquire(aio_context);
51
+ * aborted. If it returns zero, the job moves into the WAITING state. If it
39
+
52
+ * is the last job to complete in its transaction, all jobs in the
40
+ /* Paired with .clean() */
53
+ * transaction move from WAITING to PENDING.
41
bdrv_drained_begin(bs);
54
+ */
42
+
55
int coroutine_fn (*run)(Job *job, Error **errp);
43
state->bs = bs;
56
44
57
/**
45
state->job = do_drive_backup(backup, common->block_job_txn, &local_err);
58
@@ -XXX,XX +XXX,XX @@ void job_early_fail(Job *job);
46
if (local_err) {
59
/** Moves the @job from RUNNING to READY */
47
error_propagate(errp, local_err);
60
void job_transition_to_ready(Job *job);
48
- return;
61
49
+ goto out;
62
-/**
50
}
63
- * @job: The job being completed.
51
+
64
- * @ret: The status code.
52
+out:
65
- *
53
+ aio_context_release(aio_context);
66
- * Marks @job as completed. If @ret is non-zero, the job transaction it is part
54
}
67
- * of is aborted. If @ret is zero, the job moves into the WAITING state. If it
55
68
- * is the last job to complete in its transaction, all jobs in the transaction
56
static void drive_backup_commit(BlkActionState *common)
69
- * move from WAITING to PENDING.
57
{
70
- */
58
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
71
-void job_completed(Job *job, int ret);
59
+ AioContext *aio_context;
72
-
60
+
73
/** Asynchronously complete the specified @job. */
61
+ aio_context = bdrv_get_aio_context(state->bs);
74
void job_complete(Job *job, Error **errp);
62
+ aio_context_acquire(aio_context);
75
63
+
76
diff --git a/job.c b/job.c
64
assert(state->job);
77
index XXXXXXX..XXXXXXX 100644
65
block_job_start(state->job);
78
--- a/job.c
66
+
79
+++ b/job.c
67
+ aio_context_release(aio_context);
80
@@ -XXX,XX +XXX,XX @@ void job_drain(Job *job)
68
}
69
70
static void drive_backup_abort(BlkActionState *common)
71
@@ -XXX,XX +XXX,XX @@ static void drive_backup_abort(BlkActionState *common)
72
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
73
74
if (state->job) {
75
+ AioContext *aio_context;
76
+
77
+ aio_context = bdrv_get_aio_context(state->bs);
78
+ aio_context_acquire(aio_context);
79
+
80
block_job_cancel_sync(state->job);
81
+
82
+ aio_context_release(aio_context);
83
}
81
}
84
}
82
}
85
83
86
static void drive_backup_clean(BlkActionState *common)
84
+static void job_completed(Job *job);
85
+
86
static void job_exit(void *opaque)
87
{
87
{
88
DriveBackupState *state = DO_UPCAST(DriveBackupState, common, common);
88
Job *job = (Job *)opaque;
89
+ AioContext *aio_context;
89
@@ -XXX,XX +XXX,XX @@ static void job_exit(void *opaque)
90
90
job->driver->exit(job);
91
- if (state->aio_context) {
91
aio_context_release(aio_context);
92
- bdrv_drained_end(state->bs);
93
- aio_context_release(state->aio_context);
94
+ if (!state->bs) {
95
+ return;
96
}
92
}
97
+
93
- job_completed(job, job->ret);
98
+ aio_context = bdrv_get_aio_context(state->bs);
94
+ job_completed(job);
99
+ aio_context_acquire(aio_context);
100
+
101
+ bdrv_drained_end(state->bs);
102
+
103
+ aio_context_release(aio_context);
104
}
95
}
105
96
106
typedef struct BlockdevBackupState {
97
/**
98
@@ -XXX,XX +XXX,XX @@ static void job_completed_txn_success(Job *job)
99
}
100
}
101
102
-void job_completed(Job *job, int ret)
103
+static void job_completed(Job *job)
104
{
105
assert(job && job->txn && !job_is_completed(job));
106
107
- job->ret = ret;
108
job_update_rc(job);
109
- trace_job_completed(job, ret, job->ret);
110
+ trace_job_completed(job, job->ret);
111
if (job->ret) {
112
job_completed_txn_abort(job);
113
} else {
114
@@ -XXX,XX +XXX,XX @@ void job_cancel(Job *job, bool force)
115
}
116
job_cancel_async(job, force);
117
if (!job_started(job)) {
118
- job_completed(job, -ECANCELED);
119
+ job_completed(job);
120
} else if (job->deferred_to_main_loop) {
121
job_completed_txn_abort(job);
122
} else {
123
diff --git a/trace-events b/trace-events
124
index XXXXXXX..XXXXXXX 100644
125
--- a/trace-events
126
+++ b/trace-events
127
@@ -XXX,XX +XXX,XX @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe
128
# job.c
129
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)"
130
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
131
-job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
132
+job_completed(void *job, int ret) "job %p ret %d"
133
134
# job-qmp.c
135
qmp_job_cancel(void *job) "job %p"
107
--
136
--
108
2.14.3
137
2.17.1
109
138
110
139
diff view generated by jsdifflib
1
The dirty bitmap actions in qmp_transaction have not used AioContext
1
From: John Snow <jsnow@redhat.com>
2
since the dirty bitmap locking discipline was introduced in commit
3
2119882c7eb7e2c612b24fc0c8d86f5887d6f1c3 ("block: introduce
4
dirty_bitmap_mutex"). Remove the unused field.
5
2
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
3
Now that the job infrastructure is handling the job_completed call for
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
4
all implemented jobs, we can remove the interface that allowed jobs to
8
Reviewed-by: Eric Blake <eblake@redhat.com>
5
schedule their own completion.
9
Message-id: 20171206144550.22295-7-stefanha@redhat.com
6
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Signed-off-by: John Snow <jsnow@redhat.com>
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Message-id: 20180830015734.19765-10-jsnow@redhat.com
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
11
---
12
blockdev.c | 13 -------------
12
include/qemu/job.h | 17 -----------------
13
1 file changed, 13 deletions(-)
13
job.c | 40 ++--------------------------------------
14
2 files changed, 2 insertions(+), 55 deletions(-)
14
15
15
diff --git a/blockdev.c b/blockdev.c
16
diff --git a/include/qemu/job.h b/include/qemu/job.h
16
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
17
--- a/blockdev.c
18
--- a/include/qemu/job.h
18
+++ b/blockdev.c
19
+++ b/include/qemu/job.h
19
@@ -XXX,XX +XXX,XX @@ typedef struct BlockDirtyBitmapState {
20
@@ -XXX,XX +XXX,XX @@ void job_finalize(Job *job, Error **errp);
20
BlkActionState common;
21
*/
21
BdrvDirtyBitmap *bitmap;
22
void job_dismiss(Job **job, Error **errp);
22
BlockDriverState *bs;
23
23
- AioContext *aio_context;
24
-typedef void JobDeferToMainLoopFn(Job *job, void *opaque);
24
HBitmap *backup;
25
-
25
bool prepared;
26
-/**
26
} BlockDirtyBitmapState;
27
- * @job: The job
27
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common,
28
- * @fn: The function to run in the main loop
28
}
29
- * @opaque: The opaque value that is passed to @fn
29
30
- *
30
bdrv_clear_dirty_bitmap(state->bitmap, &state->backup);
31
- * This function must be called by the main job coroutine just before it
31
- /* AioContext is released in .clean() */
32
- * returns. @fn is executed in the main loop with the job AioContext acquired.
33
- *
34
- * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses
35
- * bdrv_drain_all() in the main loop.
36
- *
37
- * The @job AioContext is held while @fn executes.
38
- */
39
-void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque);
40
-
41
/**
42
* Synchronously finishes the given @job. If @finish is given, it is called to
43
* trigger completion or cancellation of the job.
44
diff --git a/job.c b/job.c
45
index XXXXXXX..XXXXXXX 100644
46
--- a/job.c
47
+++ b/job.c
48
@@ -XXX,XX +XXX,XX @@ static void coroutine_fn job_co_entry(void *opaque)
49
assert(job && job->driver && job->driver->run);
50
job_pause_point(job);
51
job->ret = job->driver->run(job, &job->err);
52
- if (!job->deferred_to_main_loop) {
53
- job->deferred_to_main_loop = true;
54
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
55
- job_exit,
56
- job);
57
- }
58
+ job->deferred_to_main_loop = true;
59
+ aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job);
32
}
60
}
33
61
34
static void block_dirty_bitmap_clear_abort(BlkActionState *common)
62
35
@@ -XXX,XX +XXX,XX @@ static void block_dirty_bitmap_clear_commit(BlkActionState *common)
63
@@ -XXX,XX +XXX,XX @@ void job_complete(Job *job, Error **errp)
36
hbitmap_free(state->backup);
64
job->driver->complete(job, errp);
37
}
65
}
38
66
39
-static void block_dirty_bitmap_clear_clean(BlkActionState *common)
67
-
68
-typedef struct {
69
- Job *job;
70
- JobDeferToMainLoopFn *fn;
71
- void *opaque;
72
-} JobDeferToMainLoopData;
73
-
74
-static void job_defer_to_main_loop_bh(void *opaque)
40
-{
75
-{
41
- BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState,
76
- JobDeferToMainLoopData *data = opaque;
42
- common, common);
77
- Job *job = data->job;
78
- AioContext *aio_context = job->aio_context;
43
-
79
-
44
- if (state->aio_context) {
80
- aio_context_acquire(aio_context);
45
- aio_context_release(state->aio_context);
81
- data->fn(data->job, data->opaque);
46
- }
82
- aio_context_release(aio_context);
83
-
84
- g_free(data);
47
-}
85
-}
48
-
86
-
49
static void abort_prepare(BlkActionState *common, Error **errp)
87
-void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque)
88
-{
89
- JobDeferToMainLoopData *data = g_malloc(sizeof(*data));
90
- data->job = job;
91
- data->fn = fn;
92
- data->opaque = opaque;
93
- job->deferred_to_main_loop = true;
94
-
95
- aio_bh_schedule_oneshot(qemu_get_aio_context(),
96
- job_defer_to_main_loop_bh, data);
97
-}
98
-
99
int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp)
50
{
100
{
51
error_setg(errp, "Transaction aborted using Abort action");
101
Error *local_err = NULL;
52
@@ -XXX,XX +XXX,XX @@ static const BlkActionOps actions[] = {
53
.prepare = block_dirty_bitmap_clear_prepare,
54
.commit = block_dirty_bitmap_clear_commit,
55
.abort = block_dirty_bitmap_clear_abort,
56
- .clean = block_dirty_bitmap_clear_clean,
57
}
58
};
59
60
--
102
--
61
2.14.3
103
2.17.1
62
104
63
105
diff view generated by jsdifflib
Deleted patch
1
Encapsulate IOThread QOM object lookup so that callers don't need to
2
know how and where IOThread objects live.
3
1
4
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
6
Reviewed-by: Eric Blake <eblake@redhat.com>
7
Message-id: 20171206144550.22295-8-stefanha@redhat.com
8
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
9
---
10
include/sysemu/iothread.h | 1 +
11
iothread.c | 7 +++++++
12
2 files changed, 8 insertions(+)
13
14
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
15
index XXXXXXX..XXXXXXX 100644
16
--- a/include/sysemu/iothread.h
17
+++ b/include/sysemu/iothread.h
18
@@ -XXX,XX +XXX,XX @@ typedef struct {
19
OBJECT_CHECK(IOThread, obj, TYPE_IOTHREAD)
20
21
char *iothread_get_id(IOThread *iothread);
22
+IOThread *iothread_by_id(const char *id);
23
AioContext *iothread_get_aio_context(IOThread *iothread);
24
void iothread_stop_all(void);
25
GMainContext *iothread_get_g_main_context(IOThread *iothread);
26
diff --git a/iothread.c b/iothread.c
27
index XXXXXXX..XXXXXXX 100644
28
--- a/iothread.c
29
+++ b/iothread.c
30
@@ -XXX,XX +XXX,XX @@ void iothread_destroy(IOThread *iothread)
31
{
32
object_unparent(OBJECT(iothread));
33
}
34
+
35
+/* Lookup IOThread by its id. Only finds user-created objects, not internal
36
+ * iothread_create() objects. */
37
+IOThread *iothread_by_id(const char *id)
38
+{
39
+ return IOTHREAD(object_resolve_path_type(id, TYPE_IOTHREAD, NULL));
40
+}
41
--
42
2.14.3
43
44
diff view generated by jsdifflib
Deleted patch
1
Currently there is no easy way for iotests to ensure that a BDS is bound
2
to a particular IOThread. Normally the virtio-blk device calls
3
blk_set_aio_context() when dataplane is enabled during guest driver
4
initialization. This never happens in iotests since -machine
5
accel=qtest means there is no guest activity (including device driver
6
initialization).
7
1
8
This patch adds a QMP command to explicitly assign IOThreads in test
9
cases. See qapi/block-core.json for a description of the command.
10
11
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
12
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
13
Reviewed-by: Eric Blake <eblake@redhat.com>
14
Message-id: 20171206144550.22295-9-stefanha@redhat.com
15
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
16
---
17
qapi/block-core.json | 36 ++++++++++++++++++++++++++++++++++++
18
blockdev.c | 41 +++++++++++++++++++++++++++++++++++++++++
19
2 files changed, 77 insertions(+)
20
21
diff --git a/qapi/block-core.json b/qapi/block-core.json
22
index XXXXXXX..XXXXXXX 100644
23
--- a/qapi/block-core.json
24
+++ b/qapi/block-core.json
25
@@ -XXX,XX +XXX,XX @@
26
'data' : { 'parent': 'str',
27
'*child': 'str',
28
'*node': 'str' } }
29
+
30
+##
31
+# @x-blockdev-set-iothread:
32
+#
33
+# Move @node and its children into the @iothread. If @iothread is null then
34
+# move @node and its children into the main loop.
35
+#
36
+# The node must not be attached to a BlockBackend.
37
+#
38
+# @node-name: the name of the block driver node
39
+#
40
+# @iothread: the name of the IOThread object or null for the main loop
41
+#
42
+# Note: this command is experimental and intended for test cases that need
43
+# control over IOThreads only.
44
+#
45
+# Since: 2.12
46
+#
47
+# Example:
48
+#
49
+# 1. Move a node into an IOThread
50
+# -> { "execute": "x-blockdev-set-iothread",
51
+# "arguments": { "node-name": "disk1",
52
+# "iothread": "iothread0" } }
53
+# <- { "return": {} }
54
+#
55
+# 2. Move a node into the main loop
56
+# -> { "execute": "x-blockdev-set-iothread",
57
+# "arguments": { "node-name": "disk1",
58
+# "iothread": null } }
59
+# <- { "return": {} }
60
+#
61
+##
62
+{ 'command': 'x-blockdev-set-iothread',
63
+ 'data' : { 'node-name': 'str',
64
+ 'iothread': 'StrOrNull' } }
65
diff --git a/blockdev.c b/blockdev.c
66
index XXXXXXX..XXXXXXX 100644
67
--- a/blockdev.c
68
+++ b/blockdev.c
69
@@ -XXX,XX +XXX,XX @@
70
#include "qapi/qmp/qerror.h"
71
#include "qapi/qobject-output-visitor.h"
72
#include "sysemu/sysemu.h"
73
+#include "sysemu/iothread.h"
74
#include "block/block_int.h"
75
#include "qmp-commands.h"
76
#include "block/trace.h"
77
@@ -XXX,XX +XXX,XX @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
78
return head;
79
}
80
81
+void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
82
+ Error **errp)
83
+{
84
+ AioContext *old_context;
85
+ AioContext *new_context;
86
+ BlockDriverState *bs;
87
+
88
+ bs = bdrv_find_node(node_name);
89
+ if (!bs) {
90
+ error_setg(errp, "Cannot find node %s", node_name);
91
+ return;
92
+ }
93
+
94
+ /* If we want to allow more extreme test scenarios this guard could be
95
+ * removed. For now it protects against accidents. */
96
+ if (bdrv_has_blk(bs)) {
97
+ error_setg(errp, "Node %s is in use", node_name);
98
+ return;
99
+ }
100
+
101
+ if (iothread->type == QTYPE_QSTRING) {
102
+ IOThread *obj = iothread_by_id(iothread->u.s);
103
+ if (!obj) {
104
+ error_setg(errp, "Cannot find iothread %s", iothread->u.s);
105
+ return;
106
+ }
107
+
108
+ new_context = iothread_get_aio_context(obj);
109
+ } else {
110
+ new_context = qemu_get_aio_context();
111
+ }
112
+
113
+ old_context = bdrv_get_aio_context(bs);
114
+ aio_context_acquire(old_context);
115
+
116
+ bdrv_set_aio_context(bs, new_context);
117
+
118
+ aio_context_release(old_context);
119
+}
120
+
121
QemuOptsList qemu_common_drive_opts = {
122
.name = "drive",
123
.head = QTAILQ_HEAD_INITIALIZER(qemu_common_drive_opts.head),
124
--
125
2.14.3
126
127
diff view generated by jsdifflib
Deleted patch
1
QMP 'transaction' blockdev-snapshot-sync with multiple disks in an
2
IOThread is an untested code path. Several bugs have been found in
3
connection with this command. This patch adds a test case to prevent
4
future regressions.
5
1
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
8
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Message-id: 20171206144550.22295-10-stefanha@redhat.com
10
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
11
---
12
tests/qemu-iotests/202 | 95 ++++++++++++++++++++++++++++++++++++++++++++++
13
tests/qemu-iotests/202.out | 11 ++++++
14
tests/qemu-iotests/group | 1 +
15
3 files changed, 107 insertions(+)
16
create mode 100755 tests/qemu-iotests/202
17
create mode 100644 tests/qemu-iotests/202.out
18
19
diff --git a/tests/qemu-iotests/202 b/tests/qemu-iotests/202
20
new file mode 100755
21
index XXXXXXX..XXXXXXX
22
--- /dev/null
23
+++ b/tests/qemu-iotests/202
24
@@ -XXX,XX +XXX,XX @@
25
+#!/usr/bin/env python
26
+#
27
+# Copyright (C) 2017 Red Hat, Inc.
28
+#
29
+# This program is free software; you can redistribute it and/or modify
30
+# it under the terms of the GNU General Public License as published by
31
+# the Free Software Foundation; either version 2 of the License, or
32
+# (at your option) any later version.
33
+#
34
+# This program is distributed in the hope that it will be useful,
35
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
+# GNU General Public License for more details.
38
+#
39
+# You should have received a copy of the GNU General Public License
40
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
41
+#
42
+# Creator/Owner: Stefan Hajnoczi <stefanha@redhat.com>
43
+#
44
+# Check that QMP 'transaction' blockdev-snapshot-sync with multiple drives on a
45
+# single IOThread completes successfully. This particular command triggered a
46
+# hang due to recursive AioContext locking and BDRV_POLL_WHILE(). Protect
47
+# against regressions.
48
+
49
+import iotests
50
+
51
+iotests.verify_image_format(supported_fmts=['qcow2'])
52
+iotests.verify_platform(['linux'])
53
+
54
+with iotests.FilePath('disk0.img') as disk0_img_path, \
55
+ iotests.FilePath('disk1.img') as disk1_img_path, \
56
+ iotests.FilePath('disk0-snap.img') as disk0_snap_img_path, \
57
+ iotests.FilePath('disk1-snap.img') as disk1_snap_img_path, \
58
+ iotests.VM() as vm:
59
+
60
+ img_size = '10M'
61
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk0_img_path, img_size)
62
+ iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, disk1_img_path, img_size)
63
+
64
+ iotests.log('Launching VM...')
65
+ vm.launch()
66
+
67
+ iotests.log('Adding IOThread...')
68
+ iotests.log(vm.qmp('object-add',
69
+ qom_type='iothread',
70
+ id='iothread0'))
71
+
72
+ iotests.log('Adding blockdevs...')
73
+ iotests.log(vm.qmp('blockdev-add',
74
+ driver=iotests.imgfmt,
75
+ node_name='disk0',
76
+ file={
77
+ 'driver': 'file',
78
+ 'filename': disk0_img_path,
79
+ }))
80
+ iotests.log(vm.qmp('blockdev-add',
81
+ driver=iotests.imgfmt,
82
+ node_name='disk1',
83
+ file={
84
+ 'driver': 'file',
85
+ 'filename': disk1_img_path,
86
+ }))
87
+
88
+ iotests.log('Setting iothread...')
89
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
90
+ node_name='disk0',
91
+ iothread='iothread0'))
92
+ iotests.log(vm.qmp('x-blockdev-set-iothread',
93
+ node_name='disk1',
94
+ iothread='iothread0'))
95
+
96
+ iotests.log('Creating external snapshots...')
97
+ iotests.log(vm.qmp(
98
+ 'transaction',
99
+ actions=[
100
+ {
101
+ 'data': {
102
+ 'node-name': 'disk0',
103
+ 'snapshot-file': disk0_snap_img_path,
104
+ 'snapshot-node-name': 'disk0-snap',
105
+ 'mode': 'absolute-paths',
106
+ 'format': iotests.imgfmt,
107
+ },
108
+ 'type': 'blockdev-snapshot-sync'
109
+ }, {
110
+ 'data': {
111
+ 'node-name': 'disk1',
112
+ 'snapshot-file': disk1_snap_img_path,
113
+ 'snapshot-node-name': 'disk1-snap',
114
+ 'mode': 'absolute-paths',
115
+ 'format': iotests.imgfmt
116
+ },
117
+ 'type': 'blockdev-snapshot-sync'
118
+ }
119
+ ]))
120
diff --git a/tests/qemu-iotests/202.out b/tests/qemu-iotests/202.out
121
new file mode 100644
122
index XXXXXXX..XXXXXXX
123
--- /dev/null
124
+++ b/tests/qemu-iotests/202.out
125
@@ -XXX,XX +XXX,XX @@
126
+Launching VM...
127
+Adding IOThread...
128
+{u'return': {}}
129
+Adding blockdevs...
130
+{u'return': {}}
131
+{u'return': {}}
132
+Setting iothread...
133
+{u'return': {}}
134
+{u'return': {}}
135
+Creating external snapshots...
136
+{u'return': {}}
137
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
138
index XXXXXXX..XXXXXXX 100644
139
--- a/tests/qemu-iotests/group
140
+++ b/tests/qemu-iotests/group
141
@@ -XXX,XX +XXX,XX @@
142
197 rw auto quick
143
198 rw auto
144
200 rw auto
145
+202 rw auto quick
146
--
147
2.14.3
148
149
diff view generated by jsdifflib
Deleted patch
1
From: Mark Kanda <mark.kanda@oracle.com>
2
1
3
Depending on the configuration, it can be beneficial to adjust the virtio-blk
4
queue size to something other than the current default of 128. Add a new
5
property to make the queue size configurable.
6
7
Signed-off-by: Mark Kanda <mark.kanda@oracle.com>
8
Reviewed-by: Karl Heubaum <karl.heubaum@oracle.com>
9
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
10
Reviewed-by: Ameya More <ameya.more@oracle.com>
11
Message-id: 52e6d742811f10dbd16e996e86cf375b9577c187.1513005190.git.mark.kanda@oracle.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
include/hw/virtio/virtio-blk.h | 1 +
15
hw/block/virtio-blk.c | 10 +++++++++-
16
2 files changed, 10 insertions(+), 1 deletion(-)
17
18
diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h
19
index XXXXXXX..XXXXXXX 100644
20
--- a/include/hw/virtio/virtio-blk.h
21
+++ b/include/hw/virtio/virtio-blk.h
22
@@ -XXX,XX +XXX,XX @@ struct VirtIOBlkConf
23
uint32_t config_wce;
24
uint32_t request_merging;
25
uint16_t num_queues;
26
+ uint16_t queue_size;
27
};
28
29
struct VirtIOBlockDataPlane;
30
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
31
index XXXXXXX..XXXXXXX 100644
32
--- a/hw/block/virtio-blk.c
33
+++ b/hw/block/virtio-blk.c
34
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
35
error_setg(errp, "num-queues property must be larger than 0");
36
return;
37
}
38
+ if (!is_power_of_2(conf->queue_size) ||
39
+ conf->queue_size > VIRTQUEUE_MAX_SIZE) {
40
+ error_setg(errp, "invalid queue-size property (%" PRIu16 "), "
41
+ "must be a power of 2 (max %d)",
42
+ conf->queue_size, VIRTQUEUE_MAX_SIZE);
43
+ return;
44
+ }
45
46
blkconf_serial(&conf->conf, &conf->serial);
47
if (!blkconf_apply_backend_options(&conf->conf,
48
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
49
s->sector_mask = (s->conf.conf.logical_block_size / BDRV_SECTOR_SIZE) - 1;
50
51
for (i = 0; i < conf->num_queues; i++) {
52
- virtio_add_queue(vdev, 128, virtio_blk_handle_output);
53
+ virtio_add_queue(vdev, conf->queue_size, virtio_blk_handle_output);
54
}
55
if (!virtio_blk_data_plane_create(vdev, conf, &s->dataplane, errp)) {
56
virtio_cleanup(vdev);
57
@@ -XXX,XX +XXX,XX @@ static Property virtio_blk_properties[] = {
58
DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0,
59
true),
60
DEFINE_PROP_UINT16("num-queues", VirtIOBlock, conf.num_queues, 1),
61
+ DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128),
62
DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD,
63
IOThread *),
64
DEFINE_PROP_END_OF_LIST(),
65
--
66
2.14.3
67
68
diff view generated by jsdifflib
Deleted patch
1
From: Mark Kanda <mark.kanda@oracle.com>
2
1
3
virtio-blk logical block size should never be larger than physical block
4
size because it doesn't make sense to have such configurations. QEMU doesn't
5
have a way to effectively express this condition; the best it can do is
6
report the physical block exponent as 0 - indicating the logical block size
7
equals the physical block size.
8
9
This is identical to commit 3da023b5827543ee4c022986ea2ad9d1274410b2
10
but applied to virtio-blk (instead of virtio-scsi).
11
12
Signed-off-by: Mark Kanda <mark.kanda@oracle.com>
13
Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
14
Reviewed-by: Ameya More <ameya.more@oracle.com>
15
Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com>
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Message-id: 773169891f9f2deb4cb7c4ef2655580dbe24c1d1.1513005190.git.mark.kanda@oracle.com
18
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
19
---
20
hw/block/virtio-blk.c | 7 +++++++
21
1 file changed, 7 insertions(+)
22
23
diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c
24
index XXXXXXX..XXXXXXX 100644
25
--- a/hw/block/virtio-blk.c
26
+++ b/hw/block/virtio-blk.c
27
@@ -XXX,XX +XXX,XX @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp)
28
29
blkconf_blocksizes(&conf->conf);
30
31
+ if (conf->conf.logical_block_size >
32
+ conf->conf.physical_block_size) {
33
+ error_setg(errp,
34
+ "logical_block_size > physical_block_size not supported");
35
+ return;
36
+ }
37
+
38
virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK,
39
sizeof(struct virtio_blk_config));
40
41
--
42
2.14.3
43
44
diff view generated by jsdifflib
Deleted patch
1
See the patch for why nested AioContext locking is no longer allowed.
2
1
3
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
4
Reviewed-by: Eric Blake <eblake@redhat.com>
5
Message-id: 20171207201320.19284-3-stefanha@redhat.com
6
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
7
---
8
docs/devel/multiple-iothreads.txt | 7 ++++---
9
1 file changed, 4 insertions(+), 3 deletions(-)
10
11
diff --git a/docs/devel/multiple-iothreads.txt b/docs/devel/multiple-iothreads.txt
12
index XXXXXXX..XXXXXXX 100644
13
--- a/docs/devel/multiple-iothreads.txt
14
+++ b/docs/devel/multiple-iothreads.txt
15
@@ -XXX,XX +XXX,XX @@
16
-Copyright (c) 2014 Red Hat Inc.
17
+Copyright (c) 2014-2017 Red Hat Inc.
18
19
This work is licensed under the terms of the GNU GPL, version 2 or later. See
20
the COPYING file in the top-level directory.
21
@@ -XXX,XX +XXX,XX @@ aio_context_acquire()/aio_context_release() for mutual exclusion. Once the
22
context is acquired no other thread can access it or run event loop iterations
23
in this AioContext.
24
25
-aio_context_acquire()/aio_context_release() calls may be nested. This
26
-means you can call them if you're not sure whether #2 applies.
27
+Legacy code sometimes nests aio_context_acquire()/aio_context_release() calls.
28
+Do not use nesting anymore, it is incompatible with the BDRV_POLL_WHILE() macro
29
+used in the block layer and can lead to hangs.
30
31
There is currently no lock ordering rule if a thread needs to acquire multiple
32
AioContexts simultaneously. Therefore, it is only safe for code holding the
33
--
34
2.14.3
35
36
diff view generated by jsdifflib
Deleted patch
1
When a node is already associated with a BlockBackend the
2
x-blockdev-set-iothread command refuses to set the IOThread. This is to
3
prevent accidentally changing the IOThread when the nodes are in use.
4
1
5
When the nodes are created with -drive they automatically get a
6
BlockBackend. In that case we know nothing is using them yet and it's
7
safe to set the IOThread. Add a force boolean to override the check.
8
9
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
11
Message-id: 20171207201320.19284-4-stefanha@redhat.com
12
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
13
---
14
qapi/block-core.json | 6 +++++-
15
blockdev.c | 11 ++++++-----
16
2 files changed, 11 insertions(+), 6 deletions(-)
17
18
diff --git a/qapi/block-core.json b/qapi/block-core.json
19
index XXXXXXX..XXXXXXX 100644
20
--- a/qapi/block-core.json
21
+++ b/qapi/block-core.json
22
@@ -XXX,XX +XXX,XX @@
23
#
24
# @iothread: the name of the IOThread object or null for the main loop
25
#
26
+# @force: true if the node and its children should be moved when a BlockBackend
27
+# is already attached
28
+#
29
# Note: this command is experimental and intended for test cases that need
30
# control over IOThreads only.
31
#
32
@@ -XXX,XX +XXX,XX @@
33
##
34
{ 'command': 'x-blockdev-set-iothread',
35
'data' : { 'node-name': 'str',
36
- 'iothread': 'StrOrNull' } }
37
+ 'iothread': 'StrOrNull',
38
+ '*force': 'bool' } }
39
diff --git a/blockdev.c b/blockdev.c
40
index XXXXXXX..XXXXXXX 100644
41
--- a/blockdev.c
42
+++ b/blockdev.c
43
@@ -XXX,XX +XXX,XX @@ BlockJobInfoList *qmp_query_block_jobs(Error **errp)
44
}
45
46
void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
47
- Error **errp)
48
+ bool has_force, bool force, Error **errp)
49
{
50
AioContext *old_context;
51
AioContext *new_context;
52
@@ -XXX,XX +XXX,XX @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread,
53
return;
54
}
55
56
- /* If we want to allow more extreme test scenarios this guard could be
57
- * removed. For now it protects against accidents. */
58
- if (bdrv_has_blk(bs)) {
59
- error_setg(errp, "Node %s is in use", node_name);
60
+ /* Protects against accidents. */
61
+ if (!(has_force && force) && bdrv_has_blk(bs)) {
62
+ error_setg(errp, "Node %s is associated with a BlockBackend and could "
63
+ "be in use (use force=true to override this check)",
64
+ node_name);
65
return;
66
}
67
68
--
69
2.14.3
70
71
diff view generated by jsdifflib
Deleted patch
1
The VM.add_object() method can be used to add IOThreads or memory
2
backend objects.
3
1
4
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
5
Reviewed-by: Eric Blake <eblake@redhat.com>
6
Message-id: 20171207201320.19284-5-stefanha@redhat.com
7
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
8
---
9
tests/qemu-iotests/iotests.py | 5 +++++
10
1 file changed, 5 insertions(+)
11
12
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
13
index XXXXXXX..XXXXXXX 100644
14
--- a/tests/qemu-iotests/iotests.py
15
+++ b/tests/qemu-iotests/iotests.py
16
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
17
socket_scm_helper=socket_scm_helper)
18
self._num_drives = 0
19
20
+ def add_object(self, opts):
21
+ self._args.append('-object')
22
+ self._args.append(opts)
23
+ return self
24
+
25
def add_device(self, opts):
26
self._args.append('-device')
27
self._args.append(opts)
28
--
29
2.14.3
30
31
diff view generated by jsdifflib
Deleted patch
1
There is a small chance that iothread_stop() hangs as follows:
2
1
3
Thread 3 (Thread 0x7f63eba5f700 (LWP 16105)):
4
#0 0x00007f64012c09b6 in ppoll () at /lib64/libc.so.6
5
#1 0x000055959992eac9 in ppoll (__ss=0x0, __timeout=0x0, __nfds=<optimized out>, __fds=<optimized out>) at /usr/include/bits/poll2.h:77
6
#2 0x000055959992eac9 in qemu_poll_ns (fds=<optimized out>, nfds=<optimized out>, timeout=<optimized out>) at util/qemu-timer.c:322
7
#3 0x0000559599930711 in aio_poll (ctx=0x55959bdb83c0, blocking=blocking@entry=true) at util/aio-posix.c:629
8
#4 0x00005595996806fe in iothread_run (opaque=0x55959bd78400) at iothread.c:59
9
#5 0x00007f640159f609 in start_thread () at /lib64/libpthread.so.0
10
#6 0x00007f64012cce6f in clone () at /lib64/libc.so.6
11
12
Thread 1 (Thread 0x7f640b45b280 (LWP 16103)):
13
#0 0x00007f64015a0b6d in pthread_join () at /lib64/libpthread.so.0
14
#1 0x00005595999332ef in qemu_thread_join (thread=<optimized out>) at util/qemu-thread-posix.c:547
15
#2 0x00005595996808ae in iothread_stop (iothread=<optimized out>) at iothread.c:91
16
#3 0x000055959968094d in iothread_stop_iter (object=<optimized out>, opaque=<optimized out>) at iothread.c:102
17
#4 0x0000559599857d97 in do_object_child_foreach (obj=obj@entry=0x55959bdb8100, fn=fn@entry=0x559599680930 <iothread_stop_iter>, opaque=opaque@entry=0x0, recurse=recurse@entry=false) at qom/object.c:852
18
#5 0x0000559599859477 in object_child_foreach (obj=obj@entry=0x55959bdb8100, fn=fn@entry=0x559599680930 <iothread_stop_iter>, opaque=opaque@entry=0x0) at qom/object.c:867
19
#6 0x0000559599680a6e in iothread_stop_all () at iothread.c:341
20
#7 0x000055959955b1d5 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at vl.c:4913
21
22
The relevant code from iothread_run() is:
23
24
while (!atomic_read(&iothread->stopping)) {
25
aio_poll(iothread->ctx, true);
26
27
and iothread_stop():
28
29
iothread->stopping = true;
30
aio_notify(iothread->ctx);
31
...
32
qemu_thread_join(&iothread->thread);
33
34
The following scenario can occur:
35
36
1. IOThread:
37
while (!atomic_read(&iothread->stopping)) -> stopping=false
38
39
2. Main loop:
40
iothread->stopping = true;
41
aio_notify(iothread->ctx);
42
43
3. IOThread:
44
aio_poll(iothread->ctx, true); -> hang
45
46
The bug is explained by the AioContext->notify_me doc comments:
47
48
"If this field is 0, everything (file descriptors, bottom halves,
49
timers) will be re-evaluated before the next blocking poll(), thus the
50
event_notifier_set call can be skipped."
51
52
The problem is that "everything" does not include checking
53
iothread->stopping. This means iothread_run() will block in aio_poll()
54
if aio_notify() was called just before aio_poll().
55
56
This patch fixes the hang by replacing aio_notify() with
57
aio_bh_schedule_oneshot(). This makes aio_poll() or g_main_loop_run()
58
to return.
59
60
Implementing this properly required a new bool running flag. The new
61
flag prevents races that are tricky if we try to use iothread->stopping.
62
Now iothread->stopping is purely for iothread_stop() and
63
iothread->running is purely for the iothread_run() thread.
64
65
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
66
Reviewed-by: Eric Blake <eblake@redhat.com>
67
Message-id: 20171207201320.19284-6-stefanha@redhat.com
68
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
69
---
70
include/sysemu/iothread.h | 3 ++-
71
iothread.c | 20 +++++++++++++++-----
72
2 files changed, 17 insertions(+), 6 deletions(-)
73
74
diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h
75
index XXXXXXX..XXXXXXX 100644
76
--- a/include/sysemu/iothread.h
77
+++ b/include/sysemu/iothread.h
78
@@ -XXX,XX +XXX,XX @@ typedef struct {
79
GOnce once;
80
QemuMutex init_done_lock;
81
QemuCond init_done_cond; /* is thread initialization done? */
82
- bool stopping;
83
+ bool stopping; /* has iothread_stop() been called? */
84
+ bool running; /* should iothread_run() continue? */
85
int thread_id;
86
87
/* AioContext poll parameters */
88
diff --git a/iothread.c b/iothread.c
89
index XXXXXXX..XXXXXXX 100644
90
--- a/iothread.c
91
+++ b/iothread.c
92
@@ -XXX,XX +XXX,XX @@ static void *iothread_run(void *opaque)
93
qemu_cond_signal(&iothread->init_done_cond);
94
qemu_mutex_unlock(&iothread->init_done_lock);
95
96
- while (!atomic_read(&iothread->stopping)) {
97
+ while (iothread->running) {
98
aio_poll(iothread->ctx, true);
99
100
if (atomic_read(&iothread->worker_context)) {
101
@@ -XXX,XX +XXX,XX @@ static void *iothread_run(void *opaque)
102
return NULL;
103
}
104
105
+/* Runs in iothread_run() thread */
106
+static void iothread_stop_bh(void *opaque)
107
+{
108
+ IOThread *iothread = opaque;
109
+
110
+ iothread->running = false; /* stop iothread_run() */
111
+
112
+ if (iothread->main_loop) {
113
+ g_main_loop_quit(iothread->main_loop);
114
+ }
115
+}
116
+
117
void iothread_stop(IOThread *iothread)
118
{
119
if (!iothread->ctx || iothread->stopping) {
120
return;
121
}
122
iothread->stopping = true;
123
- aio_notify(iothread->ctx);
124
- if (atomic_read(&iothread->main_loop)) {
125
- g_main_loop_quit(iothread->main_loop);
126
- }
127
+ aio_bh_schedule_oneshot(iothread->ctx, iothread_stop_bh, iothread);
128
qemu_thread_join(&iothread->thread);
129
}
130
131
@@ -XXX,XX +XXX,XX @@ static void iothread_complete(UserCreatable *obj, Error **errp)
132
char *name, *thread_name;
133
134
iothread->stopping = false;
135
+ iothread->running = true;
136
iothread->thread_id = -1;
137
iothread->ctx = aio_context_new(&local_error);
138
if (!iothread->ctx) {
139
--
140
2.14.3
141
142
diff view generated by jsdifflib