1
The following changes since commit 9964e96dc9999cf7f7c936ee854a795415d19b60:
1
The following changes since commit 5ec2eca83dc478ddf24077e02a8b34dd26cd3ff9:
2
2
3
Merge remote-tracking branch 'jasowang/tags/net-pull-request' into staging (2017-05-23 15:01:31 +0100)
3
Merge remote-tracking branch 'remotes/awilliam/tags/vfio-updates-20190613.0' into staging (2019-06-14 09:33:55 +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/codyprime/qemu-kvm-jtc.git tags/block-pull-request
7
https://github.com/XanClic/qemu.git tags/pull-block-2019-06-14
8
8
9
for you to fetch changes up to 223a23c198787328ae75bc65d84edf5fde33c0b6:
9
for you to fetch changes up to 21c1ce592a144188dfe59b9e156a97da412a59a2:
10
10
11
block/gluster: glfs_lseek() workaround (2017-05-24 16:44:46 -0400)
11
iotests: Test qemu-img convert -C --salvage (2019-06-14 15:09:42 +0200)
12
12
13
----------------------------------------------------------------
13
----------------------------------------------------------------
14
Block patches
14
Block patches:
15
- Allow blockdev-backup from nodes that are not in qemu's main AIO
16
context to newly added nodes
17
- Add salvaging mode to qemu-img convert
18
- Minor fixes to tests, documentation, and for less Valgrind annoyance
19
15
----------------------------------------------------------------
20
----------------------------------------------------------------
21
Andrey Shinkevich (1):
22
hw/block/fdc: floppy command FIFO memory initialization
16
23
17
Jeff Cody (1):
24
John Snow (6):
18
block/gluster: glfs_lseek() workaround
25
blockdev-backup: don't check aio_context too early
26
iotests.py: do not use infinite waits
27
QEMUMachine: add events_wait method
28
iotests.py: rewrite run_job to be pickier
29
iotests: add iotest 256 for testing blockdev-backup across iothread
30
contexts
31
event_match: always match on None value
19
32
20
Paolo Bonzini (11):
33
Max Reitz (12):
21
blockjob: remove unnecessary check
34
iotests: Filter 175's allocation information
22
blockjob: remove iostatus_reset callback
35
iotests: Fix intermittent failure in 219
23
blockjob: introduce block_job_early_fail
36
qemu-img: Fix options leakage in img_rebase()
24
blockjob: introduce block_job_pause/resume_all
37
qapi/block-core: Overlays are not snapshots
25
blockjob: separate monitor and blockjob APIs
38
blockdev: Overlays are not snapshots
26
blockjob: move iostatus reset inside block_job_user_resume
39
qemu-img: Move quiet into ImgConvertState
27
blockjob: introduce block_job_cancel_async, check iostatus invariants
40
qemu-img: Add salvaging mode to convert
28
blockjob: group BlockJob transaction functions together
41
blkdebug: Add @iotype error option
29
blockjob: strengthen a bit test-blockjob-txn
42
blkdebug: Add "none" event
30
blockjob: reorganize block_job_completed_txn_abort
43
blkdebug: Inject errors on .bdrv_co_block_status()
31
blockjob: use deferred_to_main_loop to indicate the coroutine has
44
iotests: Test qemu-img convert --salvage
32
ended
45
iotests: Test qemu-img convert -C --salvage
33
46
34
block/backup.c | 2 +-
47
Vladimir Sementsov-Ogievskiy (1):
35
block/commit.c | 2 +-
48
iotests: restrict 254 to support only qcow2
36
block/gluster.c | 18 +-
49
37
block/io.c | 19 +-
50
qapi/block-core.json | 53 ++++++++---
38
block/mirror.c | 2 +-
51
block/blkdebug.c | 60 ++++++++++--
39
blockdev.c | 1 -
52
blockdev.c | 14 +--
40
blockjob.c | 750 ++++++++++++++++++++++++-------------------
53
hw/block/fdc.c | 1 +
41
include/block/blockjob.h | 16 -
54
qemu-img.c | 106 +++++++++++++++------
42
include/block/blockjob_int.h | 27 +-
55
python/qemu/__init__.py | 67 ++++++++++----
43
tests/test-blockjob-txn.c | 7 +-
56
qemu-img-cmds.hx | 4 +-
44
tests/test-blockjob.c | 10 +-
57
qemu-img.texi | 4 +
45
11 files changed, 463 insertions(+), 391 deletions(-)
58
tests/qemu-iotests/082 | 1 +
59
tests/qemu-iotests/082.out | 3 +
60
tests/qemu-iotests/085.out | 10 +-
61
tests/qemu-iotests/175 | 26 +++++-
62
tests/qemu-iotests/175.out | 8 +-
63
tests/qemu-iotests/219 | 13 ++-
64
tests/qemu-iotests/251 | 170 ++++++++++++++++++++++++++++++++++
65
tests/qemu-iotests/251.out | 43 +++++++++
66
tests/qemu-iotests/254 | 2 +
67
tests/qemu-iotests/256 | 122 ++++++++++++++++++++++++
68
tests/qemu-iotests/256.out | 119 ++++++++++++++++++++++++
69
tests/qemu-iotests/group | 2 +
70
tests/qemu-iotests/iotests.py | 60 +++++++-----
71
21 files changed, 772 insertions(+), 116 deletions(-)
72
create mode 100755 tests/qemu-iotests/251
73
create mode 100644 tests/qemu-iotests/251.out
74
create mode 100755 tests/qemu-iotests/256
75
create mode 100644 tests/qemu-iotests/256.out
46
76
47
--
77
--
48
2.9.3
78
2.21.0
49
79
50
80
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
Yet another pure code movement patch, preparing for the next change.
3
in blockdev_backup_prepare, we check to make sure that the target is
4
associated with a compatible aio context. However, do_blockdev_backup is
5
called later and has some logic to move the target to a compatible
6
aio_context. The transaction version will fail certain commands
7
needlessly early as a result.
4
8
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Allow blockdev_backup_prepare to simply call do_blockdev_backup, which
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
will ultimately decide if the contexts are compatible or not.
7
Message-id: 20170508141310.8674-9-pbonzini@redhat.com
11
8
Signed-off-by: Jeff Cody <jcody@redhat.com>
12
Note: the transaction version has always disallowed this operation since
13
its initial commit bd8baecd (2014), whereas the version of
14
qmp_blockdev_backup at the time, from commit c29c1dd312f, tried to
15
enforce the aio_context switch instead. It's not clear, and I can't see
16
from the mailing list archives at the time, why the two functions take a
17
different approach. It wasn't until later in efd7556708b (2016) that the
18
standalone version tried to determine if it could set the context or
19
not.
20
21
Reported-by: aihua liang <aliang@redhat.com>
22
Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=1683498
23
Signed-off-by: John Snow <jsnow@redhat.com>
24
Message-id: 20190523170643.20794-2-jsnow@redhat.com
25
Reviewed-by: Max Reitz <mreitz@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
27
---
10
blockjob.c | 128 ++++++++++++++++++++++++++++++-------------------------------
28
blockdev.c | 4 ----
11
1 file changed, 64 insertions(+), 64 deletions(-)
29
1 file changed, 4 deletions(-)
12
30
13
diff --git a/blockjob.c b/blockjob.c
31
diff --git a/blockdev.c b/blockdev.c
14
index XXXXXXX..XXXXXXX 100644
32
index XXXXXXX..XXXXXXX 100644
15
--- a/blockjob.c
33
--- a/blockdev.c
16
+++ b/blockjob.c
34
+++ b/blockdev.c
17
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
35
@@ -XXX,XX +XXX,XX @@ static void blockdev_backup_prepare(BlkActionState *common, Error **errp)
18
return NULL;
19
}
20
21
+BlockJobTxn *block_job_txn_new(void)
22
+{
23
+ BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
24
+ QLIST_INIT(&txn->jobs);
25
+ txn->refcnt = 1;
26
+ return txn;
27
+}
28
+
29
+static void block_job_txn_ref(BlockJobTxn *txn)
30
+{
31
+ txn->refcnt++;
32
+}
33
+
34
+void block_job_txn_unref(BlockJobTxn *txn)
35
+{
36
+ if (txn && --txn->refcnt == 0) {
37
+ g_free(txn);
38
+ }
39
+}
40
+
41
+void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
42
+{
43
+ if (!txn) {
44
+ return;
45
+ }
46
+
47
+ assert(!job->txn);
48
+ job->txn = txn;
49
+
50
+ QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
51
+ block_job_txn_ref(txn);
52
+}
53
+
54
static void block_job_pause(BlockJob *job)
55
{
56
job->pause_count++;
57
@@ -XXX,XX +XXX,XX @@ static void block_job_cancel_async(BlockJob *job)
58
job->cancelled = true;
59
}
60
61
+static int block_job_finish_sync(BlockJob *job,
62
+ void (*finish)(BlockJob *, Error **errp),
63
+ Error **errp)
64
+{
65
+ Error *local_err = NULL;
66
+ int ret;
67
+
68
+ assert(blk_bs(job->blk)->job == job);
69
+
70
+ block_job_ref(job);
71
+
72
+ finish(job, &local_err);
73
+ if (local_err) {
74
+ error_propagate(errp, local_err);
75
+ block_job_unref(job);
76
+ return -EBUSY;
77
+ }
78
+ /* block_job_drain calls block_job_enter, and it should be enough to
79
+ * induce progress until the job completes or moves to the main thread.
80
+ */
81
+ while (!job->deferred_to_main_loop && !job->completed) {
82
+ block_job_drain(job);
83
+ }
84
+ while (!job->completed) {
85
+ aio_poll(qemu_get_aio_context(), true);
86
+ }
87
+ ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
88
+ block_job_unref(job);
89
+ return ret;
90
+}
91
+
92
static void block_job_completed_txn_abort(BlockJob *job)
93
{
94
AioContext *ctx;
95
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
96
}
36
}
97
}
37
98
38
aio_context = bdrv_get_aio_context(bs);
99
-static int block_job_finish_sync(BlockJob *job,
39
- if (aio_context != bdrv_get_aio_context(target)) {
100
- void (*finish)(BlockJob *, Error **errp),
40
- error_setg(errp, "Backup between two IO threads is not implemented");
101
- Error **errp)
102
-{
103
- Error *local_err = NULL;
104
- int ret;
105
-
106
- assert(blk_bs(job->blk)->job == job);
107
-
108
- block_job_ref(job);
109
-
110
- finish(job, &local_err);
111
- if (local_err) {
112
- error_propagate(errp, local_err);
113
- block_job_unref(job);
114
- return -EBUSY;
115
- }
116
- /* block_job_drain calls block_job_enter, and it should be enough to
117
- * induce progress until the job completes or moves to the main thread.
118
- */
119
- while (!job->deferred_to_main_loop && !job->completed) {
120
- block_job_drain(job);
121
- }
122
- while (!job->completed) {
123
- aio_poll(qemu_get_aio_context(), true);
124
- }
125
- ret = (job->cancelled && job->ret == 0) ? -ECANCELED : job->ret;
126
- block_job_unref(job);
127
- return ret;
128
-}
129
-
130
/* A wrapper around block_job_cancel() taking an Error ** parameter so it may be
131
* used with block_job_finish_sync() without the need for (rather nasty)
132
* function pointer casts there. */
133
@@ -XXX,XX +XXX,XX @@ void block_job_defer_to_main_loop(BlockJob *job,
134
aio_bh_schedule_oneshot(qemu_get_aio_context(),
135
block_job_defer_to_main_loop_bh, data);
136
}
137
-
138
-BlockJobTxn *block_job_txn_new(void)
139
-{
140
- BlockJobTxn *txn = g_new0(BlockJobTxn, 1);
141
- QLIST_INIT(&txn->jobs);
142
- txn->refcnt = 1;
143
- return txn;
144
-}
145
-
146
-static void block_job_txn_ref(BlockJobTxn *txn)
147
-{
148
- txn->refcnt++;
149
-}
150
-
151
-void block_job_txn_unref(BlockJobTxn *txn)
152
-{
153
- if (txn && --txn->refcnt == 0) {
154
- g_free(txn);
155
- }
156
-}
157
-
158
-void block_job_txn_add_job(BlockJobTxn *txn, BlockJob *job)
159
-{
160
- if (!txn) {
161
- return;
41
- return;
162
- }
42
- }
163
-
43
aio_context_acquire(aio_context);
164
- assert(!job->txn);
44
state->bs = bs;
165
- job->txn = txn;
45
166
-
167
- QLIST_INSERT_HEAD(&txn->jobs, job, txn_list);
168
- block_job_txn_ref(txn);
169
-}
170
--
46
--
171
2.9.3
47
2.21.0
172
48
173
49
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
Cap waits to 60 seconds so that iotests can fail gracefully if something
4
goes wrong.
5
6
Signed-off-by: John Snow <jsnow@redhat.com>
7
Message-id: 20190523170643.20794-3-jsnow@redhat.com
8
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
tests/qemu-iotests/iotests.py | 14 +++++++-------
12
1 file changed, 7 insertions(+), 7 deletions(-)
13
14
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
15
index XXXXXXX..XXXXXXX 100644
16
--- a/tests/qemu-iotests/iotests.py
17
+++ b/tests/qemu-iotests/iotests.py
18
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
19
output_list += [key + '=' + obj[key]]
20
return ','.join(output_list)
21
22
- def get_qmp_events_filtered(self, wait=True):
23
+ def get_qmp_events_filtered(self, wait=60.0):
24
result = []
25
for ev in self.get_qmp_events(wait=wait):
26
result.append(filter_qmp_event(ev))
27
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
28
29
# Returns None on success, and an error string on failure
30
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
31
- pre_finalize=None):
32
+ pre_finalize=None, wait=60.0):
33
error = None
34
while True:
35
- for ev in self.get_qmp_events_filtered(wait=True):
36
+ for ev in self.get_qmp_events_filtered(wait=wait):
37
if ev['event'] == 'JOB_STATUS_CHANGE':
38
status = ev['data']['status']
39
if status == 'aborting':
40
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
41
self.assertEqual(self.vm.flatten_qmp_object(json.loads(json_filename[5:])),
42
self.vm.flatten_qmp_object(reference))
43
44
- def cancel_and_wait(self, drive='drive0', force=False, resume=False):
45
+ def cancel_and_wait(self, drive='drive0', force=False, resume=False, wait=60.0):
46
'''Cancel a block job and wait for it to finish, returning the event'''
47
result = self.vm.qmp('block-job-cancel', device=drive, force=force)
48
self.assert_qmp(result, 'return', {})
49
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
50
cancelled = False
51
result = None
52
while not cancelled:
53
- for event in self.vm.get_qmp_events(wait=True):
54
+ for event in self.vm.get_qmp_events(wait=wait):
55
if event['event'] == 'BLOCK_JOB_COMPLETED' or \
56
event['event'] == 'BLOCK_JOB_CANCELLED':
57
self.assert_qmp(event, 'data/device', drive)
58
@@ -XXX,XX +XXX,XX @@ class QMPTestCase(unittest.TestCase):
59
self.assert_no_active_block_jobs()
60
return result
61
62
- def wait_until_completed(self, drive='drive0', check_offset=True):
63
+ def wait_until_completed(self, drive='drive0', check_offset=True, wait=60.0):
64
'''Wait for a block job to finish, returning the event'''
65
while True:
66
- for event in self.vm.get_qmp_events(wait=True):
67
+ for event in self.vm.get_qmp_events(wait=wait):
68
if event['event'] == 'BLOCK_JOB_COMPLETED':
69
self.assert_qmp(event, 'data/device', drive)
70
self.assert_qmp_absent(event, 'data/error')
71
--
72
2.21.0
73
74
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: John Snow <jsnow@redhat.com>
2
2
3
This is unused since commit 66a0fae ("blockjob: Don't touch BDS iostatus",
3
Instead of event_wait which looks for a single event, add an events_wait
4
2016-05-19).
4
which can look for any number of events simultaneously. However, it
5
will still only return one at a time, whichever happens first.
5
6
6
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
7
Signed-off-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
8
Message-id: 20190523170643.20794-4-jsnow@redhat.com
8
Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org>
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
Reviewed-by: Jeff Cody <jcody@redhat.com>
11
Message-id: 20170508141310.8674-3-pbonzini@redhat.com
12
Signed-off-by: Jeff Cody <jcody@redhat.com>
13
---
11
---
14
blockjob.c | 3 ---
12
python/qemu/__init__.py | 69 +++++++++++++++++++++++++++++------------
15
include/block/blockjob_int.h | 3 ---
13
1 file changed, 49 insertions(+), 20 deletions(-)
16
2 files changed, 6 deletions(-)
17
14
18
diff --git a/blockjob.c b/blockjob.c
15
diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py
19
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
20
--- a/blockjob.c
17
--- a/python/qemu/__init__.py
21
+++ b/blockjob.c
18
+++ b/python/qemu/__init__.py
22
@@ -XXX,XX +XXX,XX @@ bool block_job_is_cancelled(BlockJob *job)
19
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
23
void block_job_iostatus_reset(BlockJob *job)
20
self._qmp.clear_events()
24
{
21
return events
25
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
22
26
- if (job->driver->iostatus_reset) {
23
- def event_wait(self, name, timeout=60.0, match=None):
27
- job->driver->iostatus_reset(job);
24
+ @staticmethod
28
- }
25
+ def event_match(event, match=None):
29
}
26
"""
30
27
- Wait for specified timeout on named event in QMP; optionally filter
31
static int block_job_finish_sync(BlockJob *job,
28
- results by match.
32
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
29
+ Check if an event matches optional match criteria.
33
index XXXXXXX..XXXXXXX 100644
30
34
--- a/include/block/blockjob_int.h
31
- The 'match' is checked to be a recursive subset of the 'event'; skips
35
+++ b/include/block/blockjob_int.h
32
- branch processing on match's value None
36
@@ -XXX,XX +XXX,XX @@ struct BlockJobDriver {
33
- {"foo": {"bar": 1}} matches {"foo": None}
37
/** Optional callback for job types that support setting a speed limit */
34
- {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}}
38
void (*set_speed)(BlockJob *job, int64_t speed, Error **errp);
35
+ The match criteria takes the form of a matching subdict. The event is
39
36
+ checked to be a superset of the subdict, recursively, with matching
40
- /** Optional callback for job types that need to forward I/O status reset */
37
+ values whenever those values are not None.
41
- void (*iostatus_reset)(BlockJob *job);
38
+
42
-
39
+ Examples, with the subdict queries on the left:
43
/** Mandatory: Entrypoint for the Coroutine. */
40
+ - None matches any object.
44
CoroutineEntry *start;
41
+ - {"foo": None} matches {"foo": {"bar": 1}}
42
+ - {"foo": {"baz": None}} does not match {"foo": {"bar": 1}}
43
+ - {"foo": {"baz": 2}} matches {"foo": {"bar": 1, "baz": 2}}
44
"""
45
- def event_match(event, match=None):
46
- if match is None:
47
- return True
48
+ if match is None:
49
+ return True
50
51
- for key in match:
52
- if key in event:
53
- if isinstance(event[key], dict):
54
- if not event_match(event[key], match[key]):
55
- return False
56
- elif event[key] != match[key]:
57
+ for key in match:
58
+ if key in event:
59
+ if isinstance(event[key], dict):
60
+ if not QEMUMachine.event_match(event[key], match[key]):
61
return False
62
- else:
63
+ elif event[key] != match[key]:
64
return False
65
+ else:
66
+ return False
67
+ return True
68
69
- return True
70
+ def event_wait(self, name, timeout=60.0, match=None):
71
+ """
72
+ event_wait waits for and returns a named event from QMP with a timeout.
73
+
74
+ name: The event to wait for.
75
+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
76
+ match: Optional match criteria. See event_match for details.
77
+ """
78
+ return self.events_wait([(name, match)], timeout)
79
+
80
+ def events_wait(self, events, timeout=60.0):
81
+ """
82
+ events_wait waits for and returns a named event from QMP with a timeout.
83
+
84
+ events: a sequence of (name, match_criteria) tuples.
85
+ The match criteria are optional and may be None.
86
+ See event_match for details.
87
+ timeout: QEMUMonitorProtocol.pull_event timeout parameter.
88
+ """
89
+ def _match(event):
90
+ for name, match in events:
91
+ if (event['event'] == name and
92
+ self.event_match(event, match)):
93
+ return True
94
+ return False
95
96
# Search cached events
97
for event in self._events:
98
- if (event['event'] == name) and event_match(event, match):
99
+ if _match(event):
100
self._events.remove(event)
101
return event
102
103
# Poll for new events
104
while True:
105
event = self._qmp.pull_event(wait=timeout)
106
- if (event['event'] == name) and event_match(event, match):
107
+ if _match(event):
108
return event
109
self._events.append(event)
45
110
46
--
111
--
47
2.9.3
112
2.21.0
48
113
49
114
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
Don't pull events out of the queue that don't belong to us;
4
be choosier so that we can use this method to drive jobs that
5
were launched by transactions that may have more jobs.
6
7
Signed-off-by: John Snow <jsnow@redhat.com>
8
Message-id: 20190523170643.20794-5-jsnow@redhat.com
9
Reviewed-by: Max Reitz <mreitz@redhat.com>
10
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
12
tests/qemu-iotests/iotests.py | 48 +++++++++++++++++++++--------------
13
1 file changed, 29 insertions(+), 19 deletions(-)
14
15
diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py
16
index XXXXXXX..XXXXXXX 100644
17
--- a/tests/qemu-iotests/iotests.py
18
+++ b/tests/qemu-iotests/iotests.py
19
@@ -XXX,XX +XXX,XX @@ class VM(qtest.QEMUQtestMachine):
20
# Returns None on success, and an error string on failure
21
def run_job(self, job, auto_finalize=True, auto_dismiss=False,
22
pre_finalize=None, wait=60.0):
23
+ match_device = {'data': {'device': job}}
24
+ match_id = {'data': {'id': job}}
25
+ events = [
26
+ ('BLOCK_JOB_COMPLETED', match_device),
27
+ ('BLOCK_JOB_CANCELLED', match_device),
28
+ ('BLOCK_JOB_ERROR', match_device),
29
+ ('BLOCK_JOB_READY', match_device),
30
+ ('BLOCK_JOB_PENDING', match_id),
31
+ ('JOB_STATUS_CHANGE', match_id)
32
+ ]
33
error = None
34
while True:
35
- for ev in self.get_qmp_events_filtered(wait=wait):
36
- if ev['event'] == 'JOB_STATUS_CHANGE':
37
- status = ev['data']['status']
38
- if status == 'aborting':
39
- result = self.qmp('query-jobs')
40
- for j in result['return']:
41
- if j['id'] == job:
42
- error = j['error']
43
- log('Job failed: %s' % (j['error']))
44
- elif status == 'pending' and not auto_finalize:
45
- if pre_finalize:
46
- pre_finalize()
47
- self.qmp_log('job-finalize', id=job)
48
- elif status == 'concluded' and not auto_dismiss:
49
- self.qmp_log('job-dismiss', id=job)
50
- elif status == 'null':
51
- return error
52
- else:
53
- log(ev)
54
+ ev = filter_qmp_event(self.events_wait(events))
55
+ if ev['event'] != 'JOB_STATUS_CHANGE':
56
+ log(ev)
57
+ continue
58
+ status = ev['data']['status']
59
+ if status == 'aborting':
60
+ result = self.qmp('query-jobs')
61
+ for j in result['return']:
62
+ if j['id'] == job:
63
+ error = j['error']
64
+ log('Job failed: %s' % (j['error']))
65
+ elif status == 'pending' and not auto_finalize:
66
+ if pre_finalize:
67
+ pre_finalize()
68
+ self.qmp_log('job-finalize', id=job)
69
+ elif status == 'concluded' and not auto_dismiss:
70
+ self.qmp_log('job-dismiss', id=job)
71
+ elif status == 'null':
72
+ return error
73
74
def node_info(self, node_name):
75
nodes = self.qmp('query-named-block-nodes')
76
--
77
2.21.0
78
79
diff view generated by jsdifflib
New patch
1
1
From: John Snow <jsnow@redhat.com>
2
3
Signed-off-by: John Snow <jsnow@redhat.com>
4
Message-id: 20190523170643.20794-6-jsnow@redhat.com
5
Reviewed-by: Max Reitz <mreitz@redhat.com>
6
[mreitz: Moved from 250 to 256]
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
---
9
tests/qemu-iotests/256 | 122 +++++++++++++++++++++++++++++++++++++
10
tests/qemu-iotests/256.out | 119 ++++++++++++++++++++++++++++++++++++
11
tests/qemu-iotests/group | 1 +
12
3 files changed, 242 insertions(+)
13
create mode 100755 tests/qemu-iotests/256
14
create mode 100644 tests/qemu-iotests/256.out
15
16
diff --git a/tests/qemu-iotests/256 b/tests/qemu-iotests/256
17
new file mode 100755
18
index XXXXXXX..XXXXXXX
19
--- /dev/null
20
+++ b/tests/qemu-iotests/256
21
@@ -XXX,XX +XXX,XX @@
22
+#!/usr/bin/env python
23
+#
24
+# Test incremental/backup across iothread contexts
25
+#
26
+# Copyright (c) 2019 John Snow for Red Hat, Inc.
27
+#
28
+# This program is free software; you can redistribute it and/or modify
29
+# it under the terms of the GNU General Public License as published by
30
+# the Free Software Foundation; either version 2 of the License, or
31
+# (at your option) any later version.
32
+#
33
+# This program is distributed in the hope that it will be useful,
34
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
35
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36
+# GNU General Public License for more details.
37
+#
38
+# You should have received a copy of the GNU General Public License
39
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
40
+#
41
+# owner=jsnow@redhat.com
42
+
43
+import os
44
+import iotests
45
+from iotests import log
46
+
47
+iotests.verify_image_format(supported_fmts=['qcow2'])
48
+size = 64 * 1024 * 1024
49
+
50
+with iotests.FilePath('img0') as img0_path, \
51
+ iotests.FilePath('img1') as img1_path, \
52
+ iotests.FilePath('img0-full') as img0_full_path, \
53
+ iotests.FilePath('img1-full') as img1_full_path, \
54
+ iotests.FilePath('img0-incr') as img0_incr_path, \
55
+ iotests.FilePath('img1-incr') as img1_incr_path, \
56
+ iotests.VM() as vm:
57
+
58
+ def create_target(filepath, name, size):
59
+ basename = os.path.basename(filepath)
60
+ nodename = "file_{}".format(basename)
61
+ log(vm.command('blockdev-create', job_id='job1',
62
+ options={
63
+ 'driver': 'file',
64
+ 'filename': filepath,
65
+ 'size': 0,
66
+ }))
67
+ vm.run_job('job1')
68
+ log(vm.command('blockdev-add', driver='file',
69
+ node_name=nodename, filename=filepath))
70
+ log(vm.command('blockdev-create', job_id='job2',
71
+ options={
72
+ 'driver': iotests.imgfmt,
73
+ 'file': nodename,
74
+ 'size': size,
75
+ }))
76
+ vm.run_job('job2')
77
+ log(vm.command('blockdev-add', driver=iotests.imgfmt,
78
+ node_name=name,
79
+ file=nodename))
80
+
81
+ log('--- Preparing images & VM ---\n')
82
+ vm.add_object('iothread,id=iothread0')
83
+ vm.add_object('iothread,id=iothread1')
84
+ vm.add_device('virtio-scsi-pci,id=scsi0,iothread=iothread0')
85
+ vm.add_device('virtio-scsi-pci,id=scsi1,iothread=iothread1')
86
+ iotests.qemu_img_create('-f', iotests.imgfmt, img0_path, str(size))
87
+ iotests.qemu_img_create('-f', iotests.imgfmt, img1_path, str(size))
88
+ vm.add_drive(img0_path, interface='none')
89
+ vm.add_device('scsi-hd,id=device0,drive=drive0,bus=scsi0.0')
90
+ vm.add_drive(img1_path, interface='none')
91
+ vm.add_device('scsi-hd,id=device1,drive=drive1,bus=scsi1.0')
92
+
93
+ log('--- Starting VM ---\n')
94
+ vm.launch()
95
+
96
+ log('--- Create Targets & Full Backups ---\n')
97
+ create_target(img0_full_path, 'img0-full', size)
98
+ create_target(img1_full_path, 'img1-full', size)
99
+ ret = vm.qmp_log('transaction', indent=2, actions=[
100
+ { 'type': 'block-dirty-bitmap-add',
101
+ 'data': { 'node': 'drive0', 'name': 'bitmap0' }},
102
+ { 'type': 'block-dirty-bitmap-add',
103
+ 'data': { 'node': 'drive1', 'name': 'bitmap1' }},
104
+ { 'type': 'blockdev-backup',
105
+ 'data': { 'device': 'drive0',
106
+ 'target': 'img0-full',
107
+ 'sync': 'full',
108
+ 'job-id': 'j0' }},
109
+ { 'type': 'blockdev-backup',
110
+ 'data': { 'device': 'drive1',
111
+ 'target': 'img1-full',
112
+ 'sync': 'full',
113
+ 'job-id': 'j1' }}
114
+ ])
115
+ if "error" in ret:
116
+ raise Exception(ret['error']['desc'])
117
+ vm.run_job('j0', auto_dismiss=True)
118
+ vm.run_job('j1', auto_dismiss=True)
119
+
120
+ log('\n--- Create Targets & Incremental Backups ---\n')
121
+ create_target(img0_incr_path, 'img0-incr', size)
122
+ create_target(img1_incr_path, 'img1-incr', size)
123
+ ret = vm.qmp_log('transaction', indent=2, actions=[
124
+ { 'type': 'blockdev-backup',
125
+ 'data': { 'device': 'drive0',
126
+ 'target': 'img0-incr',
127
+ 'sync': 'incremental',
128
+ 'bitmap': 'bitmap0',
129
+ 'job-id': 'j2' }},
130
+ { 'type': 'blockdev-backup',
131
+ 'data': { 'device': 'drive1',
132
+ 'target': 'img1-incr',
133
+ 'sync': 'incremental',
134
+ 'bitmap': 'bitmap1',
135
+ 'job-id': 'j3' }}
136
+ ])
137
+ if "error" in ret:
138
+ raise Exception(ret['error']['desc'])
139
+ vm.run_job('j2', auto_dismiss=True)
140
+ vm.run_job('j3', auto_dismiss=True)
141
+
142
+ log('\n--- Done ---')
143
+ vm.shutdown()
144
diff --git a/tests/qemu-iotests/256.out b/tests/qemu-iotests/256.out
145
new file mode 100644
146
index XXXXXXX..XXXXXXX
147
--- /dev/null
148
+++ b/tests/qemu-iotests/256.out
149
@@ -XXX,XX +XXX,XX @@
150
+--- Preparing images & VM ---
151
+
152
+--- Starting VM ---
153
+
154
+--- Create Targets & Full Backups ---
155
+
156
+{}
157
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
158
+{"return": {}}
159
+{}
160
+{}
161
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
162
+{"return": {}}
163
+{}
164
+{}
165
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
166
+{"return": {}}
167
+{}
168
+{}
169
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
170
+{"return": {}}
171
+{}
172
+{
173
+ "execute": "transaction",
174
+ "arguments": {
175
+ "actions": [
176
+ {
177
+ "data": {
178
+ "name": "bitmap0",
179
+ "node": "drive0"
180
+ },
181
+ "type": "block-dirty-bitmap-add"
182
+ },
183
+ {
184
+ "data": {
185
+ "name": "bitmap1",
186
+ "node": "drive1"
187
+ },
188
+ "type": "block-dirty-bitmap-add"
189
+ },
190
+ {
191
+ "data": {
192
+ "device": "drive0",
193
+ "job-id": "j0",
194
+ "sync": "full",
195
+ "target": "img0-full"
196
+ },
197
+ "type": "blockdev-backup"
198
+ },
199
+ {
200
+ "data": {
201
+ "device": "drive1",
202
+ "job-id": "j1",
203
+ "sync": "full",
204
+ "target": "img1-full"
205
+ },
206
+ "type": "blockdev-backup"
207
+ }
208
+ ]
209
+ }
210
+}
211
+{
212
+ "return": {}
213
+}
214
+{"data": {"device": "j0", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
215
+{"data": {"device": "j1", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
216
+
217
+--- Create Targets & Incremental Backups ---
218
+
219
+{}
220
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
221
+{"return": {}}
222
+{}
223
+{}
224
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
225
+{"return": {}}
226
+{}
227
+{}
228
+{"execute": "job-dismiss", "arguments": {"id": "job1"}}
229
+{"return": {}}
230
+{}
231
+{}
232
+{"execute": "job-dismiss", "arguments": {"id": "job2"}}
233
+{"return": {}}
234
+{}
235
+{
236
+ "execute": "transaction",
237
+ "arguments": {
238
+ "actions": [
239
+ {
240
+ "data": {
241
+ "bitmap": "bitmap0",
242
+ "device": "drive0",
243
+ "job-id": "j2",
244
+ "sync": "incremental",
245
+ "target": "img0-incr"
246
+ },
247
+ "type": "blockdev-backup"
248
+ },
249
+ {
250
+ "data": {
251
+ "bitmap": "bitmap1",
252
+ "device": "drive1",
253
+ "job-id": "j3",
254
+ "sync": "incremental",
255
+ "target": "img1-incr"
256
+ },
257
+ "type": "blockdev-backup"
258
+ }
259
+ ]
260
+ }
261
+}
262
+{
263
+ "return": {}
264
+}
265
+{"data": {"device": "j2", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
266
+{"data": {"device": "j3", "len": 67108864, "offset": 67108864, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}}
267
+
268
+--- Done ---
269
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
270
index XXXXXXX..XXXXXXX 100644
271
--- a/tests/qemu-iotests/group
272
+++ b/tests/qemu-iotests/group
273
@@ -XXX,XX +XXX,XX @@
274
253 rw auto quick
275
254 rw auto backing quick
276
255 rw auto quick
277
+256 rw auto quick
278
--
279
2.21.0
280
281
diff view generated by jsdifflib
New patch
1
From: John Snow <jsnow@redhat.com>
1
2
3
Before, event_match didn't always recurse if the event value was not a
4
dictionary, and would instead check for equality immediately.
5
6
By delaying equality checking to post-recursion, we can allow leaf
7
values like "5" to match "None" and take advantage of the generic
8
None-returns-True clause.
9
10
This makes the matching a little more obviously consistent at the
11
expense of being able to check for explicit None values, which is
12
probably not that important given what this function is used for.
13
14
Signed-off-by: John Snow <jsnow@redhat.com>
15
Message-id: 20190528183857.26167-1-jsnow@redhat.com
16
Signed-off-by: Max Reitz <mreitz@redhat.com>
17
---
18
python/qemu/__init__.py | 24 ++++++++++++++----------
19
1 file changed, 14 insertions(+), 10 deletions(-)
20
21
diff --git a/python/qemu/__init__.py b/python/qemu/__init__.py
22
index XXXXXXX..XXXXXXX 100644
23
--- a/python/qemu/__init__.py
24
+++ b/python/qemu/__init__.py
25
@@ -XXX,XX +XXX,XX @@ class QEMUMachine(object):
26
27
The match criteria takes the form of a matching subdict. The event is
28
checked to be a superset of the subdict, recursively, with matching
29
- values whenever those values are not None.
30
+ values whenever the subdict values are not None.
31
+
32
+ This has a limitation that you cannot explicitly check for None values.
33
34
Examples, with the subdict queries on the left:
35
- None matches any object.
36
- {"foo": None} matches {"foo": {"bar": 1}}
37
- - {"foo": {"baz": None}} does not match {"foo": {"bar": 1}}
38
- - {"foo": {"baz": 2}} matches {"foo": {"bar": 1, "baz": 2}}
39
+ - {"foo": None} matches {"foo": 5}
40
+ - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}}
41
+ - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}}
42
"""
43
if match is None:
44
return True
45
46
- for key in match:
47
- if key in event:
48
- if isinstance(event[key], dict):
49
+ try:
50
+ for key in match:
51
+ if key in event:
52
if not QEMUMachine.event_match(event[key], match[key]):
53
return False
54
- elif event[key] != match[key]:
55
+ else:
56
return False
57
- else:
58
- return False
59
- return True
60
+ return True
61
+ except TypeError:
62
+ # either match or event wasn't iterable (not a dict)
63
+ return match == event
64
65
def event_wait(self, name, timeout=60.0, match=None):
66
"""
67
--
68
2.21.0
69
70
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
It is possible for an empty file to take up blocks on a filesystem, for
2
example:
2
3
3
Outside blockjob.c, block_job_unref is only used when a block job fails
4
$ qemu-img create -f raw test.img 1G
4
to start, and block_job_ref is not used at all. The reference counting
5
Formatting 'test.img', fmt=raw size=1073741824
5
thus is pretty well hidden. Introduce a separate function to be used
6
$ mkfs.ext4 -I 128 -q test.img
6
by block jobs; because block_job_ref and block_job_unref now become
7
$ mkdir test-mount
7
static, move them earlier in blockjob.c.
8
$ sudo mount -o loop test.img test-mount
9
$ sudo touch test-mount/test-file
10
$ stat -c 'blocks=%b' test-mount/test-file
11
blocks=8
8
12
9
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
13
These extra blocks (one cluster) are apparently used for metadata,
10
Reviewed-by: John Snow <jsnow@redhat.com>
14
because they are always there, on top of blocks used for data:
11
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
15
12
Reviewed-by: Jeff Cody <jcody@redhat.com>
16
$ sudo dd if=/dev/zero of=test-mount/test-file bs=1M count=1
13
Message-id: 20170508141310.8674-4-pbonzini@redhat.com
17
1+0 records in
14
Signed-off-by: Jeff Cody <jcody@redhat.com>
18
1+0 records out
19
1048576 bytes (1.0 MB, 1.0 MiB) copied, 0.00135339 s, 775 MB/s
20
$ stat -c 'blocks=%b' test-mount/test-file
21
blocks=2056
22
23
Make iotest 175 take this into account.
24
25
Reported-by: Thomas Huth <thuth@redhat.com>
26
Signed-off-by: Max Reitz <mreitz@redhat.com>
27
Reviewed-by: Eric Blake <eblake@redhat.com>
28
Reviewed-by: Nir Soffer <nsoffer@redhat.com>
29
Message-id: 20190516144319.12570-1-mreitz@redhat.com
30
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
31
---
16
block/backup.c | 2 +-
32
tests/qemu-iotests/175 | 26 ++++++++++++++++++++++----
17
block/commit.c | 2 +-
33
tests/qemu-iotests/175.out | 8 ++++----
18
block/mirror.c | 2 +-
34
2 files changed, 26 insertions(+), 8 deletions(-)
19
blockjob.c | 47 ++++++++++++++++++++++++++------------------
20
include/block/blockjob_int.h | 15 +++-----------
21
tests/test-blockjob.c | 10 +++++-----
22
6 files changed, 39 insertions(+), 39 deletions(-)
23
35
24
diff --git a/block/backup.c b/block/backup.c
36
diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175
25
index XXXXXXX..XXXXXXX 100644
37
index XXXXXXX..XXXXXXX 100755
26
--- a/block/backup.c
38
--- a/tests/qemu-iotests/175
27
+++ b/block/backup.c
39
+++ b/tests/qemu-iotests/175
28
@@ -XXX,XX +XXX,XX @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs,
40
@@ -XXX,XX +XXX,XX @@ status=1    # failure is the default!
29
}
41
30
if (job) {
42
_cleanup()
31
backup_clean(&job->common);
43
{
32
- block_job_unref(&job->common);
44
-    _cleanup_test_img
33
+ block_job_early_fail(&job->common);
45
+ _cleanup_test_img
34
}
46
+ rm -f "$TEST_DIR/empty"
35
36
return NULL;
37
diff --git a/block/commit.c b/block/commit.c
38
index XXXXXXX..XXXXXXX 100644
39
--- a/block/commit.c
40
+++ b/block/commit.c
41
@@ -XXX,XX +XXX,XX @@ fail:
42
if (commit_top_bs) {
43
bdrv_set_backing_hd(overlay_bs, top, &error_abort);
44
}
45
- block_job_unref(&s->common);
46
+ block_job_early_fail(&s->common);
47
}
47
}
48
48
trap "_cleanup; exit \$status" 0 1 2 3 15
49
49
50
diff --git a/block/mirror.c b/block/mirror.c
50
+# Some file systems sometimes allocate extra blocks independently of
51
index XXXXXXX..XXXXXXX 100644
51
+# the file size. This function hides the resulting difference in the
52
--- a/block/mirror.c
52
+# stat -c '%b' output.
53
+++ b/block/mirror.c
53
+# Parameter 1: Number of blocks an empty file occupies
54
@@ -XXX,XX +XXX,XX @@ fail:
54
+# Parameter 2: Image size in bytes
55
55
+_filter_blocks()
56
g_free(s->replaces);
57
blk_unref(s->target);
58
- block_job_unref(&s->common);
59
+ block_job_early_fail(&s->common);
60
}
61
62
bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL,
63
diff --git a/blockjob.c b/blockjob.c
64
index XXXXXXX..XXXXXXX 100644
65
--- a/blockjob.c
66
+++ b/blockjob.c
67
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
68
return NULL;
69
}
70
71
+static void block_job_ref(BlockJob *job)
72
+{
56
+{
73
+ ++job->refcnt;
57
+ extra_blocks=$1
58
+ img_size=$2
59
+
60
+ sed -e "s/blocks=$extra_blocks\\(\$\\|[^0-9]\\)/nothing allocated/" \
61
+ -e "s/blocks=$((extra_blocks + img_size / 512))\\(\$\\|[^0-9]\\)/everything allocated/"
74
+}
62
+}
75
+
63
+
76
+static void block_job_attached_aio_context(AioContext *new_context,
64
# get standard environment, filters and checks
77
+ void *opaque);
65
. ./common.rc
78
+static void block_job_detach_aio_context(void *opaque);
66
. ./common.filter
67
@@ -XXX,XX +XXX,XX @@ _supported_fmt raw
68
_supported_proto file
69
_supported_os Linux
70
71
-size=1m
72
+size=$((1 * 1024 * 1024))
79
+
73
+
80
+static void block_job_unref(BlockJob *job)
74
+touch "$TEST_DIR/empty"
81
+{
75
+extra_blocks=$(stat -c '%b' "$TEST_DIR/empty")
82
+ if (--job->refcnt == 0) {
76
83
+ BlockDriverState *bs = blk_bs(job->blk);
77
echo
84
+ bs->job = NULL;
78
echo "== creating image with default preallocation =="
85
+ block_job_remove_all_bdrv(job);
79
_make_test_img $size | _filter_imgfmt
86
+ blk_remove_aio_context_notifier(job->blk,
80
-stat -c "size=%s, blocks=%b" $TEST_IMG
87
+ block_job_attached_aio_context,
81
+stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
88
+ block_job_detach_aio_context, job);
82
89
+ blk_unref(job->blk);
83
for mode in off full falloc; do
90
+ error_free(job->blocker);
84
echo
91
+ g_free(job->id);
85
echo "== creating image with preallocation $mode =="
92
+ QLIST_REMOVE(job, job_list);
86
IMGOPTS=preallocation=$mode _make_test_img $size | _filter_imgfmt
93
+ g_free(job);
87
- stat -c "size=%s, blocks=%b" $TEST_IMG
94
+ }
88
+ stat -c "size=%s, blocks=%b" $TEST_IMG | _filter_blocks $extra_blocks $size
95
+}
89
done
96
+
90
97
static void block_job_attached_aio_context(AioContext *new_context,
91
# success, all done
98
void *opaque)
92
diff --git a/tests/qemu-iotests/175.out b/tests/qemu-iotests/175.out
99
{
100
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
101
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
102
}
103
104
-void block_job_ref(BlockJob *job)
105
+void block_job_early_fail(BlockJob *job)
106
{
107
- ++job->refcnt;
108
-}
109
-
110
-void block_job_unref(BlockJob *job)
111
-{
112
- if (--job->refcnt == 0) {
113
- BlockDriverState *bs = blk_bs(job->blk);
114
- bs->job = NULL;
115
- block_job_remove_all_bdrv(job);
116
- blk_remove_aio_context_notifier(job->blk,
117
- block_job_attached_aio_context,
118
- block_job_detach_aio_context, job);
119
- blk_unref(job->blk);
120
- error_free(job->blocker);
121
- g_free(job->id);
122
- QLIST_REMOVE(job, job_list);
123
- g_free(job);
124
- }
125
+ block_job_unref(job);
126
}
127
128
static void block_job_completed_single(BlockJob *job)
129
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
130
index XXXXXXX..XXXXXXX 100644
93
index XXXXXXX..XXXXXXX 100644
131
--- a/include/block/blockjob_int.h
94
--- a/tests/qemu-iotests/175.out
132
+++ b/include/block/blockjob_int.h
95
+++ b/tests/qemu-iotests/175.out
133
@@ -XXX,XX +XXX,XX @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns);
96
@@ -XXX,XX +XXX,XX @@ QA output created by 175
134
void block_job_yield(BlockJob *job);
97
135
98
== creating image with default preallocation ==
136
/**
99
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576
137
- * block_job_ref:
100
-size=1048576, blocks=0
138
+ * block_job_early_fail:
101
+size=1048576, nothing allocated
139
* @bs: The block device.
102
140
*
103
== creating image with preallocation off ==
141
- * Grab a reference to the block job. Should be paired with block_job_unref.
104
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=off
142
+ * The block job could not be started, free it.
105
-size=1048576, blocks=0
143
*/
106
+size=1048576, nothing allocated
144
-void block_job_ref(BlockJob *job);
107
145
-
108
== creating image with preallocation full ==
146
-/**
109
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=full
147
- * block_job_unref:
110
-size=1048576, blocks=2048
148
- * @bs: The block device.
111
+size=1048576, everything allocated
149
- *
112
150
- * Release reference to the block job and release resources if it is the last
113
== creating image with preallocation falloc ==
151
- * reference.
114
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 preallocation=falloc
152
- */
115
-size=1048576, blocks=2048
153
-void block_job_unref(BlockJob *job);
116
+size=1048576, everything allocated
154
+void block_job_early_fail(BlockJob *job);
117
*** done
155
156
/**
157
* block_job_completed:
158
diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c
159
index XXXXXXX..XXXXXXX 100644
160
--- a/tests/test-blockjob.c
161
+++ b/tests/test-blockjob.c
162
@@ -XXX,XX +XXX,XX @@ static void test_job_ids(void)
163
job[1] = do_test_id(blk[1], "id0", false);
164
165
/* But once job[0] finishes we can reuse its ID */
166
- block_job_unref(job[0]);
167
+ block_job_early_fail(job[0]);
168
job[1] = do_test_id(blk[1], "id0", true);
169
170
/* No job ID specified, defaults to the backend name ('drive1') */
171
- block_job_unref(job[1]);
172
+ block_job_early_fail(job[1]);
173
job[1] = do_test_id(blk[1], NULL, true);
174
175
/* Duplicate job ID */
176
@@ -XXX,XX +XXX,XX @@ static void test_job_ids(void)
177
/* This one is valid */
178
job[2] = do_test_id(blk[2], "id_2", true);
179
180
- block_job_unref(job[0]);
181
- block_job_unref(job[1]);
182
- block_job_unref(job[2]);
183
+ block_job_early_fail(job[0]);
184
+ block_job_early_fail(job[1]);
185
+ block_job_early_fail(job[2]);
186
187
destroy_blk(blk[0]);
188
destroy_blk(blk[1]);
189
--
118
--
190
2.9.3
119
2.21.0
191
120
192
121
diff view generated by jsdifflib
New patch
1
In 219, we wait for the job to make progress before we emit its status.
2
This makes the output reliable. We do not wait for any more progress if
3
the job's current-progress already matches its total-progress.
1
4
5
Unfortunately, there is a bug: Right after the job has been started,
6
it's possible that total-progress is still 0. In that case, we may skip
7
the first progress-making step and keep ending up 64 kB short.
8
9
To fix that bug, we can simply wait for total-progress to reach 4 MB
10
(the image size) after starting the job.
11
12
Reported-by: Karen Mezick <kmezick@redhat.com>
13
Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=1686651
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
Message-id: 20190516161114.27596-1-mreitz@redhat.com
16
Reviewed-by: John Snow <jsnow@redhat.com>
17
[mreitz: Adjusted commit message as per John's proposal]
18
Signed-off-by: Max Reitz <mreitz@redhat.com>
19
---
20
tests/qemu-iotests/219 | 13 ++++++++++---
21
1 file changed, 10 insertions(+), 3 deletions(-)
22
23
diff --git a/tests/qemu-iotests/219 b/tests/qemu-iotests/219
24
index XXXXXXX..XXXXXXX 100755
25
--- a/tests/qemu-iotests/219
26
+++ b/tests/qemu-iotests/219
27
@@ -XXX,XX +XXX,XX @@ import iotests
28
29
iotests.verify_image_format(supported_fmts=['qcow2'])
30
31
+img_size = 4 * 1024 * 1024
32
+
33
def pause_wait(vm, job_id):
34
with iotests.Timeout(3, "Timeout waiting for job to pause"):
35
while True:
36
@@ -XXX,XX +XXX,XX @@ def test_pause_resume(vm):
37
iotests.log(vm.qmp('query-jobs'))
38
39
def test_job_lifecycle(vm, job, job_args, has_ready=False):
40
+ global img_size
41
+
42
iotests.log('')
43
iotests.log('')
44
iotests.log('Starting block job: %s (auto-finalize: %s; auto-dismiss: %s)' %
45
@@ -XXX,XX +XXX,XX @@ def test_job_lifecycle(vm, job, job_args, has_ready=False):
46
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
47
iotests.log(iotests.filter_qmp_event(vm.event_wait('JOB_STATUS_CHANGE')))
48
49
+ # Wait for total-progress to stabilize
50
+ while vm.qmp('query-jobs')['return'][0]['total-progress'] < img_size:
51
+ pass
52
+
53
# RUNNING state:
54
# pause/resume should work, complete/finalize/dismiss should error out
55
iotests.log('')
56
@@ -XXX,XX +XXX,XX @@ with iotests.FilePath('disk.img') as disk_path, \
57
iotests.FilePath('copy.img') as copy_path, \
58
iotests.VM() as vm:
59
60
- img_size = '4M'
61
- iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, img_size)
62
- iotests.qemu_io('-c', 'write 0 %s' % (img_size),
63
+ iotests.qemu_img_create('-f', iotests.imgfmt, disk_path, str(img_size))
64
+ iotests.qemu_io('-c', 'write 0 %i' % (img_size),
65
'-f', iotests.imgfmt, disk_path)
66
67
iotests.log('Launching VM...')
68
--
69
2.21.0
70
71
diff view generated by jsdifflib
1
On current released versions of glusterfs, glfs_lseek() will sometimes
1
From: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
2
return invalid values for SEEK_DATA or SEEK_HOLE. For SEEK_DATA and
3
SEEK_HOLE, the returned value should be >= the passed offset, or < 0 in
4
the case of error:
5
2
6
LSEEK(2):
3
The uninitialized memory allocated for the command FIFO of the
4
floppy controller during the VM hardware initialization incurs
5
many unwanted reports by Valgrind when VM state is being saved.
6
That verbosity hardens a search for the real memory issues when
7
the iotests run. Particularly, the patch eliminates 20 unnecessary
8
reports of the Valgrind tool in the iotest #169.
7
9
8
off_t lseek(int fd, off_t offset, int whence);
10
Signed-off-by: Andrey Shinkevich <andrey.shinkevich@virtuozzo.com>
11
Message-id: 1559154027-282547-1-git-send-email-andrey.shinkevich@virtuozzo.com
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Signed-off-by: Max Reitz <mreitz@redhat.com>
14
---
15
hw/block/fdc.c | 1 +
16
1 file changed, 1 insertion(+)
9
17
10
[...]
18
diff --git a/hw/block/fdc.c b/hw/block/fdc.c
11
12
SEEK_HOLE
13
Adjust the file offset to the next hole in the file greater
14
than or equal to offset. If offset points into the middle of
15
a hole, then the file offset is set to offset. If there is no
16
hole past offset, then the file offset is adjusted to the end
17
of the file (i.e., there is an implicit hole at the end of
18
any file).
19
20
[...]
21
22
RETURN VALUE
23
Upon successful completion, lseek() returns the resulting
24
offset location as measured in bytes from the beginning of the
25
file. On error, the value (off_t) -1 is returned and errno is
26
set to indicate the error
27
28
However, occasionally glfs_lseek() for SEEK_HOLE/DATA will return a
29
value less than the passed offset, yet greater than zero.
30
31
For instance, here are example values observed from this call:
32
33
offs = glfs_lseek(s->fd, start, SEEK_HOLE);
34
if (offs < 0) {
35
return -errno; /* D1 and (H3 or H4) */
36
}
37
38
start == 7608336384
39
offs == 7607877632
40
41
This causes QEMU to abort on the assert test. When this value is
42
returned, errno is also 0.
43
44
This is a reported and known bug to glusterfs:
45
https://bugzilla.redhat.com/show_bug.cgi?id=1425293
46
47
Although this is being fixed in gluster, we still should work around it
48
in QEMU, given that multiple released versions of gluster behave this
49
way.
50
51
This patch treats the return case of (offs < start) the same as if an
52
error value other than ENXIO is returned; we will assume we learned
53
nothing, and there are no holes in the file.
54
55
Signed-off-by: Jeff Cody <jcody@redhat.com>
56
Reviewed-by: Eric Blake <eblake@redhat.com>
57
Reviewed-by: Niels de Vos <ndevos@redhat.com>
58
Message-id: 87c0140e9407c08f6e74b04131b610f2e27c014c.1495560397.git.jcody@redhat.com
59
Signed-off-by: Jeff Cody <jcody@redhat.com>
60
---
61
block/gluster.c | 18 ++++++++++++++++--
62
1 file changed, 16 insertions(+), 2 deletions(-)
63
64
diff --git a/block/gluster.c b/block/gluster.c
65
index XXXXXXX..XXXXXXX 100644
19
index XXXXXXX..XXXXXXX 100644
66
--- a/block/gluster.c
20
--- a/hw/block/fdc.c
67
+++ b/block/gluster.c
21
+++ b/hw/block/fdc.c
68
@@ -XXX,XX +XXX,XX @@ static int find_allocation(BlockDriverState *bs, off_t start,
22
@@ -XXX,XX +XXX,XX @@ static void fdctrl_realize_common(DeviceState *dev, FDCtrl *fdctrl,
69
if (offs < 0) {
23
70
return -errno; /* D3 or D4 */
24
FLOPPY_DPRINTF("init controller\n");
71
}
25
fdctrl->fifo = qemu_memalign(512, FD_SECTOR_LEN);
72
- assert(offs >= start);
26
+ memset(fdctrl->fifo, 0, FD_SECTOR_LEN);
73
+
27
fdctrl->fifo_size = 512;
74
+ if (offs < start) {
28
fdctrl->result_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
75
+ /* This is not a valid return by lseek(). We are safe to just return
29
fdctrl_result_timer, fdctrl);
76
+ * -EIO in this case, and we'll treat it like D4. Unfortunately some
77
+ * versions of gluster server will return offs < start, so an assert
78
+ * here will unnecessarily abort QEMU. */
79
+ return -EIO;
80
+ }
81
82
if (offs > start) {
83
/* D2: in hole, next data at offs */
84
@@ -XXX,XX +XXX,XX @@ static int find_allocation(BlockDriverState *bs, off_t start,
85
if (offs < 0) {
86
return -errno; /* D1 and (H3 or H4) */
87
}
88
- assert(offs >= start);
89
+
90
+ if (offs < start) {
91
+ /* This is not a valid return by lseek(). We are safe to just return
92
+ * -EIO in this case, and we'll treat it like H4. Unfortunately some
93
+ * versions of gluster server will return offs < start, so an assert
94
+ * here will unnecessarily abort QEMU. */
95
+ return -EIO;
96
+ }
97
98
if (offs > start) {
99
/*
100
--
30
--
101
2.9.3
31
2.21.0
102
32
103
33
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
From: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
2
2
3
The new functions helps respecting the invariant that the coroutine
3
Test fails at least for qcow, because of different cluster sizes in
4
is entered with false user_resume, zero pause count and no error
4
base and top (and therefore different granularities of bitmaps we are
5
recorded in the iostatus.
5
trying to merge).
6
6
7
Resetting the iostatus is now common to all of block_job_cancel_async,
7
The test aim is to check block-dirty-bitmap-merge between different
8
block_job_user_resume and block_job_iostatus_reset, albeit with slight
8
nodes functionality, no needs to check all formats. So, let's just drop
9
differences:
9
support for anything except qcow2.
10
10
11
- block_job_cancel_async resets the iostatus, and resumes the job if
11
Reported-by: Max Reitz <mreitz@redhat.com>
12
there was an error, but the coroutine is not restarted immediately.
12
Signed-off-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
13
For example the caller may continue with a call to block_job_finish_sync.
13
Message-id: 20190605155405.104384-1-vsementsov@virtuozzo.com
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
15
---
16
tests/qemu-iotests/254 | 2 ++
17
1 file changed, 2 insertions(+)
14
18
15
- block_job_user_resume resets the iostatus. It wants to resume the job
19
diff --git a/tests/qemu-iotests/254 b/tests/qemu-iotests/254
16
unconditionally, even if there was no error.
20
index XXXXXXX..XXXXXXX 100755
17
21
--- a/tests/qemu-iotests/254
18
- block_job_iostatus_reset doesn't resume the job at all. Maybe that's
22
+++ b/tests/qemu-iotests/254
19
a bug but it should be fixed separately.
23
@@ -XXX,XX +XXX,XX @@
20
24
import iotests
21
block_job_iostatus_reset does the least common denominator, so add some
25
from iotests import qemu_img_create, file_path, log
22
checking but otherwise leave it as the entry point for resetting the
26
23
iostatus.
27
+iotests.verify_image_format(supported_fmts=['qcow2'])
24
25
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
26
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
27
Message-id: 20170508141310.8674-8-pbonzini@redhat.com
28
Signed-off-by: Jeff Cody <jcody@redhat.com>
29
---
30
blockjob.c | 24 ++++++++++++++++++++----
31
1 file changed, 20 insertions(+), 4 deletions(-)
32
33
diff --git a/blockjob.c b/blockjob.c
34
index XXXXXXX..XXXXXXX 100644
35
--- a/blockjob.c
36
+++ b/blockjob.c
37
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_single(BlockJob *job)
38
block_job_unref(job);
39
}
40
41
+static void block_job_cancel_async(BlockJob *job)
42
+{
43
+ if (job->iostatus != BLOCK_DEVICE_IO_STATUS_OK) {
44
+ block_job_iostatus_reset(job);
45
+ }
46
+ if (job->user_paused) {
47
+ /* Do not call block_job_enter here, the caller will handle it. */
48
+ job->user_paused = false;
49
+ job->pause_count--;
50
+ }
51
+ job->cancelled = true;
52
+}
53
+
28
+
54
static void block_job_completed_txn_abort(BlockJob *job)
29
disk, top = file_path('disk', 'top')
55
{
30
size = 1024 * 1024
56
AioContext *ctx;
57
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
58
* them; this job, however, may or may not be cancelled, depending
59
* on the caller, so leave it. */
60
if (other_job != job) {
61
- other_job->cancelled = true;
62
+ block_job_cancel_async(other_job);
63
}
64
continue;
65
}
66
@@ -XXX,XX +XXX,XX @@ bool block_job_user_paused(BlockJob *job)
67
void block_job_user_resume(BlockJob *job)
68
{
69
if (job && job->user_paused && job->pause_count > 0) {
70
- job->user_paused = false;
71
block_job_iostatus_reset(job);
72
+ job->user_paused = false;
73
block_job_resume(job);
74
}
75
}
76
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job)
77
void block_job_cancel(BlockJob *job)
78
{
79
if (block_job_started(job)) {
80
- job->cancelled = true;
81
- block_job_iostatus_reset(job);
82
+ block_job_cancel_async(job);
83
block_job_enter(job);
84
} else {
85
block_job_completed(job, -ECANCELED);
86
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job)
87
88
void block_job_iostatus_reset(BlockJob *job)
89
{
90
+ if (job->iostatus == BLOCK_DEVICE_IO_STATUS_OK) {
91
+ return;
92
+ }
93
+ assert(job->user_paused && job->pause_count > 0);
94
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
95
}
96
31
97
--
32
--
98
2.9.3
33
2.21.0
99
34
100
35
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
img_rebase() can leak a QDict in two occasions. Fix it.
2
2
3
All block jobs are using block_job_defer_to_main_loop as the final
3
Coverity: CID 1401416
4
step just before the coroutine terminates. At this point,
4
Fixes: d16699b64671466b42079c45b89127aeea1ca565
5
block_job_enter should do nothing, but currently it restarts
5
Fixes: 330c72957196e0ae382abcaa97ebf4eb9bc8574f
6
the freed coroutine.
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-id: 20190528195338.12376-1-mreitz@redhat.com
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
qemu-img.c | 3 +++
12
1 file changed, 3 insertions(+)
7
13
8
Now, the job->co states should probably be changed to an enum
14
diff --git a/qemu-img.c b/qemu-img.c
9
(e.g. BEFORE_START, STARTED, YIELDED, COMPLETED) subsuming
10
block_job_started, job->deferred_to_main_loop and job->busy.
11
For now, this patch eliminates the problematic reenter by
12
removing the reset of job->deferred_to_main_loop (which served
13
no purpose, as far as I could see) and checking the flag in
14
block_job_enter.
15
16
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
17
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
18
Message-id: 20170508141310.8674-12-pbonzini@redhat.com
19
Signed-off-by: Jeff Cody <jcody@redhat.com>
20
---
21
blockjob.c | 10 ++++++++--
22
include/block/blockjob_int.h | 3 ++-
23
2 files changed, 10 insertions(+), 3 deletions(-)
24
25
diff --git a/blockjob.c b/blockjob.c
26
index XXXXXXX..XXXXXXX 100644
15
index XXXXXXX..XXXXXXX 100644
27
--- a/blockjob.c
16
--- a/qemu-img.c
28
+++ b/blockjob.c
17
+++ b/qemu-img.c
29
@@ -XXX,XX +XXX,XX @@ void block_job_resume_all(void)
18
@@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv)
30
19
out_baseimg,
31
void block_job_enter(BlockJob *job)
20
&local_err);
32
{
21
if (local_err) {
33
- if (job->co && !job->busy) {
22
+ qobject_unref(options);
34
+ if (!block_job_started(job)) {
23
error_reportf_err(local_err,
35
+ return;
24
"Could not resolve backing filename: ");
36
+ }
25
ret = -1;
37
+ if (job->deferred_to_main_loop) {
26
@@ -XXX,XX +XXX,XX @@ static int img_rebase(int argc, char **argv)
38
+ return;
27
*/
39
+ }
28
prefix_chain_bs = bdrv_find_backing_image(bs, out_real_path);
29
if (prefix_chain_bs) {
30
+ qobject_unref(options);
31
g_free(out_real_path);
40
+
32
+
41
+ if (!job->busy) {
33
blk_new_backing = blk_new(qemu_get_aio_context(),
42
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
34
BLK_PERM_CONSISTENT_READ,
43
}
35
BLK_PERM_ALL);
44
}
45
@@ -XXX,XX +XXX,XX @@ static void block_job_defer_to_main_loop_bh(void *opaque)
46
aio_context_acquire(aio_context);
47
}
48
49
- data->job->deferred_to_main_loop = false;
50
data->fn(data->job, data->opaque);
51
52
if (aio_context != data->aio_context) {
53
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
54
index XXXXXXX..XXXXXXX 100644
55
--- a/include/block/blockjob_int.h
56
+++ b/include/block/blockjob_int.h
57
@@ -XXX,XX +XXX,XX @@ typedef void BlockJobDeferToMainLoopFn(BlockJob *job, void *opaque);
58
* @fn: The function to run in the main loop
59
* @opaque: The opaque value that is passed to @fn
60
*
61
- * Execute a given function in the main loop with the BlockDriverState
62
+ * This function must be called by the main job coroutine just before it
63
+ * returns. @fn is executed in the main loop with the BlockDriverState
64
* AioContext acquired. Block jobs must call bdrv_unref(), bdrv_close(), and
65
* anything that uses bdrv_drain_all() in the main loop.
66
*
67
--
36
--
68
2.9.3
37
2.21.0
69
38
70
39
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
A snapshot is something that reflects the state of something at a
2
certain point in time. It does not change.
2
3
3
This splits the part that touches job states from the part that invokes
4
The file our snapshot commands create (or the node they install) is not
4
callbacks. It will make the code simpler to understand once job states will
5
a snapshot, as it does change over time. It is an overlay. We cannot
5
be protected by a different mutex than the AioContext lock.
6
do anything about the parameter names, but we can at least adjust the
7
descriptions to reflect that fact.
6
8
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
8
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Reviewed-by: Eric Blake <eblake@redhat.com>
9
Message-id: 20170508141310.8674-11-pbonzini@redhat.com
11
Message-id: 20190603202236.1342-2-mreitz@redhat.com
10
Signed-off-by: Jeff Cody <jcody@redhat.com>
12
Reviewed-by: John Snow <jsnow@redhat.com>
13
Reviewed-by: Alberto Garcia <berto@igalia.com>
14
Signed-off-by: Max Reitz <mreitz@redhat.com>
11
---
15
---
12
blockjob.c | 35 ++++++++++++++++++++++-------------
16
qapi/block-core.json | 20 ++++++++++----------
13
1 file changed, 22 insertions(+), 13 deletions(-)
17
1 file changed, 10 insertions(+), 10 deletions(-)
14
18
15
diff --git a/blockjob.c b/blockjob.c
19
diff --git a/qapi/block-core.json b/qapi/block-core.json
16
index XXXXXXX..XXXXXXX 100644
20
index XXXXXXX..XXXXXXX 100644
17
--- a/blockjob.c
21
--- a/qapi/block-core.json
18
+++ b/blockjob.c
22
+++ b/qapi/block-core.json
19
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
23
@@ -XXX,XX +XXX,XX @@
20
24
#
21
static void block_job_completed_single(BlockJob *job)
25
# Either @device or @node-name must be set but not both.
22
{
26
#
23
+ assert(job->completed);
27
-# @device: the name of the device to generate the snapshot from.
24
+
28
+# @device: the name of the device to take a snapshot of.
25
if (!job->ret) {
29
#
26
if (job->driver->commit) {
30
# @node-name: graph node name to generate the snapshot from (Since 2.0)
27
job->driver->commit(job);
31
#
28
@@ -XXX,XX +XXX,XX @@ static int block_job_finish_sync(BlockJob *job,
32
-# @snapshot-file: the target of the new image. If the file exists, or
29
33
-# if it is a device, the snapshot will be created in the existing
30
block_job_ref(job);
34
-# file/device. Otherwise, a new file will be created.
31
35
+# @snapshot-file: the target of the new overlay image. If the file
32
- finish(job, &local_err);
36
+# exists, or if it is a device, the overlay will be created in the
33
+ if (finish) {
37
+# existing file/device. Otherwise, a new file will be created.
34
+ finish(job, &local_err);
38
#
35
+ }
39
# @snapshot-node-name: the graph node name of the new image (Since 2.0)
36
if (local_err) {
40
#
37
error_propagate(errp, local_err);
41
-# @format: the format of the snapshot image, default is 'qcow2'.
38
block_job_unref(job);
42
+# @format: the format of the overlay image, default is 'qcow2'.
39
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
43
#
40
{
44
# @mode: whether and how QEMU should create a new image, default is
41
AioContext *ctx;
45
# 'absolute-paths'.
42
BlockJobTxn *txn = job->txn;
46
@@ -XXX,XX +XXX,XX @@
43
- BlockJob *other_job, *next;
47
##
44
+ BlockJob *other_job;
48
# @BlockdevSnapshot:
45
49
#
46
if (txn->aborting) {
50
-# @node: device or node name that will have a snapshot created.
47
/*
51
+# @node: device or node name that will have a snapshot taken.
48
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_abort(BlockJob *job)
52
#
49
return;
53
# @overlay: reference to the existing block device that will become
50
}
54
-# the overlay of @node, as part of creating the snapshot.
51
txn->aborting = true;
55
+# the overlay of @node, as part of taking the snapshot.
52
+ block_job_txn_ref(txn);
56
# It must not have a current backing file (this can be
53
+
57
# achieved by passing "backing": null to blockdev-add).
54
/* We are the first failed job. Cancel other jobs. */
58
#
55
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
59
@@ -XXX,XX +XXX,XX @@
56
ctx = blk_get_aio_context(other_job->blk);
60
##
57
aio_context_acquire(ctx);
61
# @blockdev-snapshot-sync:
58
}
62
#
59
+
63
-# Generates a synchronous snapshot of a block device.
60
+ /* Other jobs are effectively cancelled by us, set the status for
64
+# Takes a synchronous snapshot of a block device.
61
+ * them; this job, however, may or may not be cancelled, depending
65
#
62
+ * on the caller, so leave it. */
66
# For the arguments, see the documentation of BlockdevSnapshotSync.
63
QLIST_FOREACH(other_job, &txn->jobs, txn_list) {
67
#
64
- if (other_job == job || other_job->completed) {
68
@@ -XXX,XX +XXX,XX @@
65
- /* Other jobs are "effectively" cancelled by us, set the status for
69
##
66
- * them; this job, however, may or may not be cancelled, depending
70
# @blockdev-snapshot:
67
- * on the caller, so leave it. */
71
#
68
- if (other_job != job) {
72
-# Generates a snapshot of a block device.
69
- block_job_cancel_async(other_job);
73
+# Takes a snapshot of a block device.
70
- }
74
#
71
- continue;
75
-# Create a snapshot, by installing 'node' as the backing image of
72
+ if (other_job != job) {
76
+# Take a snapshot, by installing 'node' as the backing image of
73
+ block_job_cancel_async(other_job);
77
# 'overlay'. Additionally, if 'node' is associated with a block
74
}
78
# device, the block device changes to using 'overlay' as its new active
75
- block_job_cancel_sync(other_job);
79
# image.
76
- assert(other_job->completed);
77
}
78
- QLIST_FOREACH_SAFE(other_job, &txn->jobs, txn_list, next) {
79
+ while (!QLIST_EMPTY(&txn->jobs)) {
80
+ other_job = QLIST_FIRST(&txn->jobs);
81
ctx = blk_get_aio_context(other_job->blk);
82
+ if (!other_job->completed) {
83
+ assert(other_job->cancelled);
84
+ block_job_finish_sync(other_job, NULL, NULL);
85
+ }
86
block_job_completed_single(other_job);
87
aio_context_release(ctx);
88
}
89
+
90
+ block_job_txn_unref(txn);
91
}
92
93
static void block_job_completed_txn_success(BlockJob *job)
94
--
80
--
95
2.9.3
81
2.21.0
96
82
97
83
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
There are error messages which refer to an overlay node as the snapshot.
2
That is wrong, those are two different things.
2
3
3
Outside blockjob.c, the block_job_iostatus_reset function is used once
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
in the monitor and once in BlockBackend. When we introduce the block
5
Reviewed-by: Eric Blake <eblake@redhat.com>
5
job mutex, block_job_iostatus_reset's client is going to be the block
6
Message-id: 20190603202236.1342-3-mreitz@redhat.com
6
layer (for which blockjob.c will take the block job mutex) rather than
7
the monitor (which will take the block job mutex by itself).
8
9
The monitor's call to block_job_iostatus_reset from the monitor comes
10
just before the sole call to block_job_user_resume, so reset the
11
iostatus directly from block_job_iostatus_reset. This will avoid
12
the need to introduce separate block_job_iostatus_reset and
13
block_job_iostatus_reset_locked APIs.
14
15
After making this change, move the function together with the others
16
that were moved in the previous patch.
17
18
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
19
Reviewed-by: John Snow <jsnow@redhat.com>
7
Reviewed-by: John Snow <jsnow@redhat.com>
20
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
8
Reviewed-by: Alberto Garcia <berto@igalia.com>
21
Reviewed-by: Jeff Cody <jcody@redhat.com>
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
22
Message-id: 20170508141310.8674-7-pbonzini@redhat.com
23
Signed-off-by: Jeff Cody <jcody@redhat.com>
24
---
10
---
25
blockdev.c | 1 -
11
blockdev.c | 10 +++++-----
26
blockjob.c | 11 ++++++-----
12
tests/qemu-iotests/085.out | 10 +++++-----
27
2 files changed, 6 insertions(+), 6 deletions(-)
13
2 files changed, 10 insertions(+), 10 deletions(-)
28
14
29
diff --git a/blockdev.c b/blockdev.c
15
diff --git a/blockdev.c b/blockdev.c
30
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
31
--- a/blockdev.c
17
--- a/blockdev.c
32
+++ b/blockdev.c
18
+++ b/blockdev.c
33
@@ -XXX,XX +XXX,XX @@ void qmp_block_job_resume(const char *device, Error **errp)
19
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
20
s->has_snapshot_node_name ? s->snapshot_node_name : NULL;
21
22
if (node_name && !snapshot_node_name) {
23
- error_setg(errp, "New snapshot node name missing");
24
+ error_setg(errp, "New overlay node name missing");
25
goto out;
26
}
27
28
if (snapshot_node_name &&
29
bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) {
30
- error_setg(errp, "New snapshot node name already in use");
31
+ error_setg(errp, "New overlay node name already in use");
32
goto out;
33
}
34
35
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
34
}
36
}
35
37
36
trace_qmp_block_job_resume(job);
38
if (bdrv_has_blk(state->new_bs)) {
37
- block_job_iostatus_reset(job);
39
- error_setg(errp, "The snapshot is already in use");
38
block_job_user_resume(job);
40
+ error_setg(errp, "The overlay is already in use");
39
aio_context_release(aio_context);
41
goto out;
40
}
42
}
41
diff --git a/blockjob.c b/blockjob.c
43
44
@@ -XXX,XX +XXX,XX @@ static void external_snapshot_prepare(BlkActionState *common,
45
}
46
47
if (state->new_bs->backing != NULL) {
48
- error_setg(errp, "The snapshot already has a backing image");
49
+ error_setg(errp, "The overlay already has a backing image");
50
goto out;
51
}
52
53
if (!state->new_bs->drv->supports_backing) {
54
- error_setg(errp, "The snapshot does not support backing images");
55
+ error_setg(errp, "The overlay does not support backing images");
56
goto out;
57
}
58
59
diff --git a/tests/qemu-iotests/085.out b/tests/qemu-iotests/085.out
42
index XXXXXXX..XXXXXXX 100644
60
index XXXXXXX..XXXXXXX 100644
43
--- a/blockjob.c
61
--- a/tests/qemu-iotests/085.out
44
+++ b/blockjob.c
62
+++ b/tests/qemu-iotests/085.out
45
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job)
63
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
46
{
64
47
if (job && job->user_paused && job->pause_count > 0) {
65
=== Invalid command - cannot create a snapshot using a file BDS ===
48
job->user_paused = false;
66
49
+ block_job_iostatus_reset(job);
67
-{"error": {"class": "GenericError", "desc": "The snapshot does not support backing images"}}
50
block_job_resume(job);
68
+{"error": {"class": "GenericError", "desc": "The overlay does not support backing images"}}
51
}
69
52
}
70
=== Invalid command - snapshot node used as active layer ===
53
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
71
54
}
72
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
55
}
73
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
56
74
-{"error": {"class": "GenericError", "desc": "The snapshot is already in use"}}
57
-void block_job_iostatus_reset(BlockJob *job)
75
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
58
-{
76
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
59
- job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
77
+{"error": {"class": "GenericError", "desc": "The overlay is already in use"}}
60
-}
78
61
-
79
=== Invalid command - snapshot node used as backing hd ===
62
static int block_job_finish_sync(BlockJob *job,
80
63
void (*finish)(BlockJob *, Error **errp),
81
@@ -XXX,XX +XXX,XX @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/
64
Error **errp)
82
Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=134217728
65
@@ -XXX,XX +XXX,XX @@ void block_job_yield(BlockJob *job)
83
Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 backing_file=TEST_DIR/t.IMGFMT.base
66
block_job_pause_point(job);
84
{"return": {}}
67
}
85
-{"error": {"class": "GenericError", "desc": "The snapshot already has a backing image"}}
68
86
+{"error": {"class": "GenericError", "desc": "The overlay already has a backing image"}}
69
+void block_job_iostatus_reset(BlockJob *job)
87
70
+{
88
=== Invalid command - The node does not exist ===
71
+ job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
89
72
+}
73
+
74
void block_job_event_ready(BlockJob *job)
75
{
76
job->ready = true;
77
--
90
--
78
2.9.3
91
2.21.0
79
92
80
93
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
Move img_convert()'s quiet flag into the ImgConvertState so it is
2
accessible by nested functions. -q dictates that it suppresses anything
3
but errors, so if those functions want to emit warnings, they need to
4
query this flag first. (There currently are no such warnings, but there
5
will be as of the next patch.)
2
6
3
Unlike test-blockjob-txn, QMP releases the reference to the transaction
7
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
before the jobs finish. Thus, qemu-iotest 124 showed a failure while
8
Reviewed-by: Eric Blake <eblake@redhat.com>
5
working on the next patch that the unit tests did not have. Make
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
the test a little nastier.
10
Message-id: 20190507203508.18026-2-mreitz@redhat.com
11
Signed-off-by: Max Reitz <mreitz@redhat.com>
12
---
13
qemu-img.c | 13 +++++++------
14
1 file changed, 7 insertions(+), 6 deletions(-)
7
15
8
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
16
diff --git a/qemu-img.c b/qemu-img.c
9
Reviewed-by: John Snow <jsnow@redhat.com>
10
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
11
Message-id: 20170508141310.8674-10-pbonzini@redhat.com
12
Signed-off-by: Jeff Cody <jcody@redhat.com>
13
---
14
tests/test-blockjob-txn.c | 7 +++++--
15
1 file changed, 5 insertions(+), 2 deletions(-)
16
17
diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c
18
index XXXXXXX..XXXXXXX 100644
17
index XXXXXXX..XXXXXXX 100644
19
--- a/tests/test-blockjob-txn.c
18
--- a/qemu-img.c
20
+++ b/tests/test-blockjob-txn.c
19
+++ b/qemu-img.c
21
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
20
@@ -XXX,XX +XXX,XX @@ typedef struct ImgConvertState {
22
block_job_start(job1);
21
int64_t target_backing_sectors; /* negative if unknown */
23
block_job_start(job2);
22
bool wr_in_order;
24
23
bool copy_range;
25
+ /* Release our reference now to trigger as many nice
24
+ bool quiet;
26
+ * use-after-free bugs as possible.
25
int min_sparse;
27
+ */
26
int alignment;
28
+ block_job_txn_unref(txn);
27
size_t cluster_sectors;
29
+
28
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
30
if (expected1 == -ECANCELED) {
29
QDict *open_opts = NULL;
31
block_job_cancel(job1);
30
char *options = NULL;
31
Error *local_err = NULL;
32
- bool writethrough, src_writethrough, quiet = false, image_opts = false,
33
+ bool writethrough, src_writethrough, image_opts = false,
34
skip_create = false, progress = false, tgt_image_opts = false;
35
int64_t ret = -EINVAL;
36
bool force_share = false;
37
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
38
src_cache = optarg;
39
break;
40
case 'q':
41
- quiet = true;
42
+ s.quiet = true;
43
break;
44
case 'n':
45
skip_create = true;
46
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
32
}
47
}
33
@@ -XXX,XX +XXX,XX @@ static void test_pair_jobs(int expected1, int expected2)
48
34
49
/* Initialize before goto out */
35
g_assert_cmpint(result1, ==, expected1);
50
- if (quiet) {
36
g_assert_cmpint(result2, ==, expected2);
51
+ if (s.quiet) {
37
-
52
progress = false;
38
- block_job_txn_unref(txn);
53
}
39
}
54
qemu_progress_init(progress, 1.0);
40
55
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
41
static void test_pair_jobs_success(void)
56
57
for (bs_i = 0; bs_i < s.src_num; bs_i++) {
58
s.src[bs_i] = img_open(image_opts, argv[optind + bs_i],
59
- fmt, src_flags, src_writethrough, quiet,
60
+ fmt, src_flags, src_writethrough, s.quiet,
61
force_share);
62
if (!s.src[bs_i]) {
63
ret = -1;
64
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
65
66
if (skip_create) {
67
s.target = img_open(tgt_image_opts, out_filename, out_fmt,
68
- flags, writethrough, quiet, false);
69
+ flags, writethrough, s.quiet, false);
70
} else {
71
/* TODO ultimately we should allow --target-image-opts
72
* to be used even when -n is not given.
73
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
74
* to allow filenames in option syntax
75
*/
76
s.target = img_open_file(out_filename, open_opts, out_fmt,
77
- flags, writethrough, quiet, false);
78
+ flags, writethrough, s.quiet, false);
79
open_opts = NULL; /* blk_new_open will have freed it */
80
}
81
if (!s.target) {
42
--
82
--
43
2.9.3
83
2.21.0
44
84
45
85
diff view generated by jsdifflib
New patch
1
1
This adds a salvaging mode (--salvage) to qemu-img convert which ignores
2
read errors and treats the respective areas as containing only zeroes.
3
This can be used for instance to at least partially recover the data
4
from terminally corrupted qcow2 images.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
8
Message-id: 20190507203508.18026-3-mreitz@redhat.com
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
qemu-img.c | 90 +++++++++++++++++++++++++++++++++++++-----------
12
qemu-img-cmds.hx | 4 +--
13
qemu-img.texi | 4 +++
14
3 files changed, 75 insertions(+), 23 deletions(-)
15
16
diff --git a/qemu-img.c b/qemu-img.c
17
index XXXXXXX..XXXXXXX 100644
18
--- a/qemu-img.c
19
+++ b/qemu-img.c
20
@@ -XXX,XX +XXX,XX @@ enum {
21
OPTION_SIZE = 264,
22
OPTION_PREALLOCATION = 265,
23
OPTION_SHRINK = 266,
24
+ OPTION_SALVAGE = 267,
25
};
26
27
typedef enum OutputFormat {
28
@@ -XXX,XX +XXX,XX @@ typedef struct ImgConvertState {
29
int64_t target_backing_sectors; /* negative if unknown */
30
bool wr_in_order;
31
bool copy_range;
32
+ bool salvage;
33
bool quiet;
34
int min_sparse;
35
int alignment;
36
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
37
}
38
39
if (s->sector_next_status <= sector_num) {
40
- int64_t count = n * BDRV_SECTOR_SIZE;
41
+ uint64_t offset = (sector_num - src_cur_offset) * BDRV_SECTOR_SIZE;
42
+ int64_t count;
43
44
- if (s->target_has_backing) {
45
+ do {
46
+ count = n * BDRV_SECTOR_SIZE;
47
+
48
+ if (s->target_has_backing) {
49
+ ret = bdrv_block_status(blk_bs(s->src[src_cur]), offset,
50
+ count, &count, NULL, NULL);
51
+ } else {
52
+ ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
53
+ offset, count, &count, NULL,
54
+ NULL);
55
+ }
56
+
57
+ if (ret < 0) {
58
+ if (s->salvage) {
59
+ if (n == 1) {
60
+ if (!s->quiet) {
61
+ warn_report("error while reading block status at "
62
+ "offset %" PRIu64 ": %s", offset,
63
+ strerror(-ret));
64
+ }
65
+ /* Just try to read the data, then */
66
+ ret = BDRV_BLOCK_DATA;
67
+ count = BDRV_SECTOR_SIZE;
68
+ } else {
69
+ /* Retry on a shorter range */
70
+ n = DIV_ROUND_UP(n, 4);
71
+ }
72
+ } else {
73
+ error_report("error while reading block status at offset "
74
+ "%" PRIu64 ": %s", offset, strerror(-ret));
75
+ return ret;
76
+ }
77
+ }
78
+ } while (ret < 0);
79
80
- ret = bdrv_block_status(blk_bs(s->src[src_cur]),
81
- (sector_num - src_cur_offset) *
82
- BDRV_SECTOR_SIZE,
83
- count, &count, NULL, NULL);
84
- } else {
85
- ret = bdrv_block_status_above(blk_bs(s->src[src_cur]), NULL,
86
- (sector_num - src_cur_offset) *
87
- BDRV_SECTOR_SIZE,
88
- count, &count, NULL, NULL);
89
- }
90
- if (ret < 0) {
91
- error_report("error while reading block status of sector %" PRId64
92
- ": %s", sector_num, strerror(-ret));
93
- return ret;
94
- }
95
n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE);
96
97
if (ret & BDRV_BLOCK_ZERO) {
98
@@ -XXX,XX +XXX,XX @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num)
99
static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
100
int nb_sectors, uint8_t *buf)
101
{
102
+ uint64_t single_read_until = 0;
103
int n, ret;
104
105
assert(nb_sectors <= s->buf_sectors);
106
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
107
BlockBackend *blk;
108
int src_cur;
109
int64_t bs_sectors, src_cur_offset;
110
+ uint64_t offset;
111
112
/* In the case of compression with multiple source files, we can get a
113
* nb_sectors that spreads into the next part. So we must be able to
114
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num,
115
blk = s->src[src_cur];
116
bs_sectors = s->src_sectors[src_cur];
117
118
+ offset = (sector_num - src_cur_offset) << BDRV_SECTOR_BITS;
119
+
120
n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset));
121
+ if (single_read_until > offset) {
122
+ n = 1;
123
+ }
124
125
- ret = blk_co_pread(
126
- blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS,
127
- n << BDRV_SECTOR_BITS, buf, 0);
128
+ ret = blk_co_pread(blk, offset, n << BDRV_SECTOR_BITS, buf, 0);
129
if (ret < 0) {
130
- return ret;
131
+ if (s->salvage) {
132
+ if (n > 1) {
133
+ single_read_until = offset + (n << BDRV_SECTOR_BITS);
134
+ continue;
135
+ } else {
136
+ if (!s->quiet) {
137
+ warn_report("error while reading offset %" PRIu64
138
+ ": %s", offset, strerror(-ret));
139
+ }
140
+ memset(buf, 0, BDRV_SECTOR_SIZE);
141
+ }
142
+ } else {
143
+ return ret;
144
+ }
145
}
146
147
sector_num += n;
148
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
149
{"image-opts", no_argument, 0, OPTION_IMAGE_OPTS},
150
{"force-share", no_argument, 0, 'U'},
151
{"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS},
152
+ {"salvage", no_argument, 0, OPTION_SALVAGE},
153
{0, 0, 0, 0}
154
};
155
c = getopt_long(argc, argv, ":hf:O:B:Cco:l:S:pt:T:qnm:WU",
156
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
157
case OPTION_IMAGE_OPTS:
158
image_opts = true;
159
break;
160
+ case OPTION_SALVAGE:
161
+ s.salvage = true;
162
+ break;
163
case OPTION_TARGET_IMAGE_OPTS:
164
tgt_image_opts = true;
165
break;
166
@@ -XXX,XX +XXX,XX @@ static int img_convert(int argc, char **argv)
167
goto fail_getopt;
168
}
169
170
+ if (s.copy_range && s.salvage) {
171
+ error_report("Cannot use copy offloading in salvaging mode");
172
+ goto fail_getopt;
173
+ }
174
+
175
if (tgt_image_opts && !skip_create) {
176
error_report("--target-image-opts requires use of -n flag");
177
goto fail_getopt;
178
diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx
179
index XXXXXXX..XXXXXXX 100644
180
--- a/qemu-img-cmds.hx
181
+++ b/qemu-img-cmds.hx
182
@@ -XXX,XX +XXX,XX @@ STEXI
183
ETEXI
184
185
DEF("convert", img_convert,
186
- "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename")
187
+ "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] [--salvage] filename [filename2 [...]] output_filename")
188
STEXI
189
-@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename}
190
+@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] [--salvage] @var{filename} [@var{filename2} [...]] @var{output_filename}
191
ETEXI
192
193
DEF("create", img_create,
194
diff --git a/qemu-img.texi b/qemu-img.texi
195
index XXXXXXX..XXXXXXX 100644
196
--- a/qemu-img.texi
197
+++ b/qemu-img.texi
198
@@ -XXX,XX +XXX,XX @@ improve performance if the data is remote, such as with NFS or iSCSI backends,
199
but will not automatically sparsify zero sectors, and may result in a fully
200
allocated target image depending on the host support for getting allocation
201
information.
202
+@item --salvage
203
+Try to ignore I/O errors when reading. Unless in quiet mode (@code{-q}), errors
204
+will still be printed. Areas that cannot be read from the source will be
205
+treated as containing only zeroes.
206
@end table
207
208
Parameters to dd subcommand:
209
--
210
2.21.0
211
212
diff view generated by jsdifflib
New patch
1
1
This new error option allows users of blkdebug to inject errors only on
2
certain kinds of I/O operations. Users usually want to make a very
3
specific operation fail, not just any; but right now they simply hope
4
that the event that triggers the error injection is followed up with
5
that very operation. That may not be true, however, because the block
6
layer is changing (including blkdebug, which may increase the number of
7
types of I/O operations on which to inject errors).
8
9
The new option's default has been chosen to keep backwards
10
compatibility.
11
12
Note that similar to the internal representation, we could choose to
13
expose this option as a list of I/O types. But there is no practical
14
use for this, because as described above, users usually know exactly
15
which kind of operation they want to make fail, so there is no need to
16
specify multiple I/O types at once. In addition, exposing this option
17
as a list would require non-trivial changes to qemu_opts_absorb_qdict().
18
19
Signed-off-by: Max Reitz <mreitz@redhat.com>
20
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
21
Message-id: 20190507203508.18026-4-mreitz@redhat.com
22
Signed-off-by: Max Reitz <mreitz@redhat.com>
23
---
24
qapi/block-core.json | 26 +++++++++++++++++++++++
25
block/blkdebug.c | 50 ++++++++++++++++++++++++++++++++++++--------
26
2 files changed, 67 insertions(+), 9 deletions(-)
27
28
diff --git a/qapi/block-core.json b/qapi/block-core.json
29
index XXXXXXX..XXXXXXX 100644
30
--- a/qapi/block-core.json
31
+++ b/qapi/block-core.json
32
@@ -XXX,XX +XXX,XX @@
33
'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
34
'cor_write', 'cluster_alloc_space'] }
35
36
+##
37
+# @BlkdebugIOType:
38
+#
39
+# Kinds of I/O that blkdebug can inject errors in.
40
+#
41
+# @read: .bdrv_co_preadv()
42
+#
43
+# @write: .bdrv_co_pwritev()
44
+#
45
+# @write-zeroes: .bdrv_co_pwrite_zeroes()
46
+#
47
+# @discard: .bdrv_co_pdiscard()
48
+#
49
+# @flush: .bdrv_co_flush_to_disk()
50
+#
51
+# Since: 4.1
52
+##
53
+{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
54
+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush' ] }
55
+
56
##
57
# @BlkdebugInjectErrorOptions:
58
#
59
@@ -XXX,XX +XXX,XX @@
60
# @state: the state identifier blkdebug needs to be in to
61
# actually trigger the event; defaults to "any"
62
#
63
+# @iotype: the type of I/O operations on which this error should
64
+# be injected; defaults to "all read, write,
65
+# write-zeroes, discard, and flush operations"
66
+# (since: 4.1)
67
+#
68
# @errno: error identifier (errno) to be returned; defaults to
69
# EIO
70
#
71
@@ -XXX,XX +XXX,XX @@
72
{ 'struct': 'BlkdebugInjectErrorOptions',
73
'data': { 'event': 'BlkdebugEvent',
74
'*state': 'int',
75
+ '*iotype': 'BlkdebugIOType',
76
'*errno': 'int',
77
'*sector': 'int',
78
'*once': 'bool',
79
diff --git a/block/blkdebug.c b/block/blkdebug.c
80
index XXXXXXX..XXXXXXX 100644
81
--- a/block/blkdebug.c
82
+++ b/block/blkdebug.c
83
@@ -XXX,XX +XXX,XX @@ typedef struct BlkdebugRule {
84
int state;
85
union {
86
struct {
87
+ uint64_t iotype_mask;
88
int error;
89
int immediately;
90
int once;
91
@@ -XXX,XX +XXX,XX @@ typedef struct BlkdebugRule {
92
QSIMPLEQ_ENTRY(BlkdebugRule) active_next;
93
} BlkdebugRule;
94
95
+QEMU_BUILD_BUG_MSG(BLKDEBUG_IO_TYPE__MAX > 64,
96
+ "BlkdebugIOType mask does not fit into an uint64_t");
97
+
98
static QemuOptsList inject_error_opts = {
99
.name = "inject-error",
100
.head = QTAILQ_HEAD_INITIALIZER(inject_error_opts.head),
101
@@ -XXX,XX +XXX,XX @@ static QemuOptsList inject_error_opts = {
102
.name = "state",
103
.type = QEMU_OPT_NUMBER,
104
},
105
+ {
106
+ .name = "iotype",
107
+ .type = QEMU_OPT_STRING,
108
+ },
109
{
110
.name = "errno",
111
.type = QEMU_OPT_NUMBER,
112
@@ -XXX,XX +XXX,XX @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
113
int event;
114
struct BlkdebugRule *rule;
115
int64_t sector;
116
+ BlkdebugIOType iotype;
117
+ Error *local_error = NULL;
118
119
/* Find the right event for the rule */
120
event_name = qemu_opt_get(opts, "event");
121
@@ -XXX,XX +XXX,XX @@ static int add_rule(void *opaque, QemuOpts *opts, Error **errp)
122
sector = qemu_opt_get_number(opts, "sector", -1);
123
rule->options.inject.offset =
124
sector == -1 ? -1 : sector * BDRV_SECTOR_SIZE;
125
+
126
+ iotype = qapi_enum_parse(&BlkdebugIOType_lookup,
127
+ qemu_opt_get(opts, "iotype"),
128
+ BLKDEBUG_IO_TYPE__MAX, &local_error);
129
+ if (local_error) {
130
+ error_propagate(errp, local_error);
131
+ return -1;
132
+ }
133
+ if (iotype != BLKDEBUG_IO_TYPE__MAX) {
134
+ rule->options.inject.iotype_mask = (1ull << iotype);
135
+ } else {
136
+ /* Apply the default */
137
+ rule->options.inject.iotype_mask =
138
+ (1ull << BLKDEBUG_IO_TYPE_READ)
139
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE)
140
+ | (1ull << BLKDEBUG_IO_TYPE_WRITE_ZEROES)
141
+ | (1ull << BLKDEBUG_IO_TYPE_DISCARD)
142
+ | (1ull << BLKDEBUG_IO_TYPE_FLUSH);
143
+ }
144
+
145
break;
146
147
case ACTION_SET_STATE:
148
@@ -XXX,XX +XXX,XX @@ out:
149
return ret;
150
}
151
152
-static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
153
+static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
154
+ BlkdebugIOType iotype)
155
{
156
BDRVBlkdebugState *s = bs->opaque;
157
BlkdebugRule *rule = NULL;
158
@@ -XXX,XX +XXX,XX @@ static int rule_check(BlockDriverState *bs, uint64_t offset, uint64_t bytes)
159
QSIMPLEQ_FOREACH(rule, &s->active_rules, active_next) {
160
uint64_t inject_offset = rule->options.inject.offset;
161
162
- if (inject_offset == -1 ||
163
- (bytes && inject_offset >= offset &&
164
- inject_offset < offset + bytes))
165
+ if ((inject_offset == -1 ||
166
+ (bytes && inject_offset >= offset &&
167
+ inject_offset < offset + bytes)) &&
168
+ (rule->options.inject.iotype_mask & (1ull << iotype)))
169
{
170
break;
171
}
172
@@ -XXX,XX +XXX,XX @@ blkdebug_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
173
assert(bytes <= bs->bl.max_transfer);
174
}
175
176
- err = rule_check(bs, offset, bytes);
177
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_READ);
178
if (err) {
179
return err;
180
}
181
@@ -XXX,XX +XXX,XX @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
182
assert(bytes <= bs->bl.max_transfer);
183
}
184
185
- err = rule_check(bs, offset, bytes);
186
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE);
187
if (err) {
188
return err;
189
}
190
@@ -XXX,XX +XXX,XX @@ blkdebug_co_pwritev(BlockDriverState *bs, uint64_t offset, uint64_t bytes,
191
192
static int blkdebug_co_flush(BlockDriverState *bs)
193
{
194
- int err = rule_check(bs, 0, 0);
195
+ int err = rule_check(bs, 0, 0, BLKDEBUG_IO_TYPE_FLUSH);
196
197
if (err) {
198
return err;
199
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pwrite_zeroes(BlockDriverState *bs,
200
assert(bytes <= bs->bl.max_pwrite_zeroes);
201
}
202
203
- err = rule_check(bs, offset, bytes);
204
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_WRITE_ZEROES);
205
if (err) {
206
return err;
207
}
208
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_pdiscard(BlockDriverState *bs,
209
assert(bytes <= bs->bl.max_pdiscard);
210
}
211
212
- err = rule_check(bs, offset, bytes);
213
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_DISCARD);
214
if (err) {
215
return err;
216
}
217
--
218
2.21.0
219
220
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
Together with @iotypes and @sector, this can be used to trap e.g. the
2
first read or write access to a certain sector without having to know
3
what happens internally in the block layer, i.e. which "real" events
4
happen right before such an access.
2
5
3
Remove use of block_job_pause/resume from outside blockjob.c, thus
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
4
making them static. The new functions are used by the block layer,
7
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
5
so place them in blockjob_int.h.
8
Message-id: 20190507203508.18026-5-mreitz@redhat.com
9
Signed-off-by: Max Reitz <mreitz@redhat.com>
10
---
11
qapi/block-core.json | 4 +++-
12
block/blkdebug.c | 2 ++
13
2 files changed, 5 insertions(+), 1 deletion(-)
6
14
7
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
diff --git a/qapi/block-core.json b/qapi/block-core.json
8
Reviewed-by: John Snow <jsnow@redhat.com>
9
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
10
Reviewed-by: Jeff Cody <jcody@redhat.com>
11
Message-id: 20170508141310.8674-5-pbonzini@redhat.com
12
Signed-off-by: Jeff Cody <jcody@redhat.com>
13
---
14
block/io.c | 19 ++------
15
blockjob.c | 114 ++++++++++++++++++++++++++-----------------
16
include/block/blockjob.h | 16 ------
17
include/block/blockjob_int.h | 14 ++++++
18
4 files changed, 86 insertions(+), 77 deletions(-)
19
20
diff --git a/block/io.c b/block/io.c
21
index XXXXXXX..XXXXXXX 100644
16
index XXXXXXX..XXXXXXX 100644
22
--- a/block/io.c
17
--- a/qapi/block-core.json
23
+++ b/block/io.c
18
+++ b/qapi/block-core.json
24
@@ -XXX,XX +XXX,XX @@
19
@@ -XXX,XX +XXX,XX @@
25
#include "trace.h"
20
#
26
#include "sysemu/block-backend.h"
21
# @cluster_alloc_space: an allocation of file space for a cluster (since 4.1)
27
#include "block/blockjob.h"
22
#
28
+#include "block/blockjob_int.h"
23
+# @none: triggers once at creation of the blkdebug node (since 4.1)
29
#include "block/block_int.h"
24
+#
30
#include "qemu/cutils.h"
25
# Since: 2.9
31
#include "qapi/error.h"
26
##
32
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_begin(void)
27
{ 'enum': 'BlkdebugEvent', 'prefix': 'BLKDBG',
33
bool waited = true;
28
@@ -XXX,XX +XXX,XX @@
34
BlockDriverState *bs;
29
'pwritev_rmw_tail', 'pwritev_rmw_after_tail', 'pwritev',
35
BdrvNextIterator it;
30
'pwritev_zero', 'pwritev_done', 'empty_image_prepare',
36
- BlockJob *job = NULL;
31
'l1_shrink_write_table', 'l1_shrink_free_l2_clusters',
37
GSList *aio_ctxs = NULL, *ctx;
32
- 'cor_write', 'cluster_alloc_space'] }
38
33
+ 'cor_write', 'cluster_alloc_space', 'none'] }
39
- while ((job = block_job_next(job))) {
34
40
- AioContext *aio_context = blk_get_aio_context(job->blk);
35
##
41
-
36
# @BlkdebugIOType:
42
- aio_context_acquire(aio_context);
37
diff --git a/block/blkdebug.c b/block/blkdebug.c
43
- block_job_pause(job);
38
index XXXXXXX..XXXXXXX 100644
44
- aio_context_release(aio_context);
39
--- a/block/blkdebug.c
45
- }
40
+++ b/block/blkdebug.c
46
+ block_job_pause_all();
41
@@ -XXX,XX +XXX,XX @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags,
47
42
goto out;
48
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
49
AioContext *aio_context = bdrv_get_aio_context(bs);
50
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
51
{
52
BlockDriverState *bs;
53
BdrvNextIterator it;
54
- BlockJob *job = NULL;
55
56
for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) {
57
AioContext *aio_context = bdrv_get_aio_context(bs);
58
@@ -XXX,XX +XXX,XX @@ void bdrv_drain_all_end(void)
59
aio_context_release(aio_context);
60
}
43
}
61
44
62
- while ((job = block_job_next(job))) {
45
+ bdrv_debug_event(bs, BLKDBG_NONE);
63
- AioContext *aio_context = blk_get_aio_context(job->blk);
64
-
65
- aio_context_acquire(aio_context);
66
- block_job_resume(job);
67
- aio_context_release(aio_context);
68
- }
69
+ block_job_resume_all();
70
}
71
72
void bdrv_drain_all(void)
73
diff --git a/blockjob.c b/blockjob.c
74
index XXXXXXX..XXXXXXX 100644
75
--- a/blockjob.c
76
+++ b/blockjob.c
77
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
78
79
static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
80
81
-static char *child_job_get_parent_desc(BdrvChild *c)
82
-{
83
- BlockJob *job = c->opaque;
84
- return g_strdup_printf("%s job '%s'",
85
- BlockJobType_lookup[job->driver->job_type],
86
- job->id);
87
-}
88
-
89
-static const BdrvChildRole child_job = {
90
- .get_parent_desc = child_job_get_parent_desc,
91
- .stay_at_node = true,
92
-};
93
-
94
-static void block_job_drained_begin(void *opaque)
95
-{
96
- BlockJob *job = opaque;
97
- block_job_pause(job);
98
-}
99
-
100
-static void block_job_drained_end(void *opaque)
101
-{
102
- BlockJob *job = opaque;
103
- block_job_resume(job);
104
-}
105
-
106
-static const BlockDevOps block_job_dev_ops = {
107
- .drained_begin = block_job_drained_begin,
108
- .drained_end = block_job_drained_end,
109
-};
110
-
111
BlockJob *block_job_next(BlockJob *job)
112
{
113
if (!job) {
114
@@ -XXX,XX +XXX,XX @@ BlockJob *block_job_get(const char *id)
115
return NULL;
116
}
117
118
+static void block_job_pause(BlockJob *job)
119
+{
120
+ job->pause_count++;
121
+}
122
+
46
+
123
+static void block_job_resume(BlockJob *job)
47
ret = 0;
124
+{
48
out:
125
+ assert(job->pause_count > 0);
49
if (ret < 0) {
126
+ job->pause_count--;
127
+ if (job->pause_count) {
128
+ return;
129
+ }
130
+ block_job_enter(job);
131
+}
132
+
133
static void block_job_ref(BlockJob *job)
134
{
135
++job->refcnt;
136
@@ -XXX,XX +XXX,XX @@ static void block_job_detach_aio_context(void *opaque)
137
block_job_unref(job);
138
}
139
140
+static char *child_job_get_parent_desc(BdrvChild *c)
141
+{
142
+ BlockJob *job = c->opaque;
143
+ return g_strdup_printf("%s job '%s'",
144
+ BlockJobType_lookup[job->driver->job_type],
145
+ job->id);
146
+}
147
+
148
+static const BdrvChildRole child_job = {
149
+ .get_parent_desc = child_job_get_parent_desc,
150
+ .stay_at_node = true,
151
+};
152
+
153
+static void block_job_drained_begin(void *opaque)
154
+{
155
+ BlockJob *job = opaque;
156
+ block_job_pause(job);
157
+}
158
+
159
+static void block_job_drained_end(void *opaque)
160
+{
161
+ BlockJob *job = opaque;
162
+ block_job_resume(job);
163
+}
164
+
165
+static const BlockDevOps block_job_dev_ops = {
166
+ .drained_begin = block_job_drained_begin,
167
+ .drained_end = block_job_drained_end,
168
+};
169
+
170
void block_job_remove_all_bdrv(BlockJob *job)
171
{
172
GSList *l;
173
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp)
174
job->driver->complete(job, errp);
175
}
176
177
-void block_job_pause(BlockJob *job)
178
-{
179
- job->pause_count++;
180
-}
181
-
182
void block_job_user_pause(BlockJob *job)
183
{
184
job->user_paused = true;
185
@@ -XXX,XX +XXX,XX @@ void coroutine_fn block_job_pause_point(BlockJob *job)
186
}
187
}
188
189
-void block_job_resume(BlockJob *job)
190
-{
191
- assert(job->pause_count > 0);
192
- job->pause_count--;
193
- if (job->pause_count) {
194
- return;
195
- }
196
- block_job_enter(job);
197
-}
198
-
199
void block_job_user_resume(BlockJob *job)
200
{
201
if (job && job->user_paused && job->pause_count > 0) {
202
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
203
&error_abort);
204
}
205
206
+void block_job_pause_all(void)
207
+{
208
+ BlockJob *job = NULL;
209
+ while ((job = block_job_next(job))) {
210
+ AioContext *aio_context = blk_get_aio_context(job->blk);
211
+
212
+ aio_context_acquire(aio_context);
213
+ block_job_pause(job);
214
+ aio_context_release(aio_context);
215
+ }
216
+}
217
+
218
+void block_job_resume_all(void)
219
+{
220
+ BlockJob *job = NULL;
221
+ while ((job = block_job_next(job))) {
222
+ AioContext *aio_context = blk_get_aio_context(job->blk);
223
+
224
+ aio_context_acquire(aio_context);
225
+ block_job_resume(job);
226
+ aio_context_release(aio_context);
227
+ }
228
+}
229
+
230
void block_job_event_ready(BlockJob *job)
231
{
232
job->ready = true;
233
diff --git a/include/block/blockjob.h b/include/block/blockjob.h
234
index XXXXXXX..XXXXXXX 100644
235
--- a/include/block/blockjob.h
236
+++ b/include/block/blockjob.h
237
@@ -XXX,XX +XXX,XX @@ void block_job_complete(BlockJob *job, Error **errp);
238
BlockJobInfo *block_job_query(BlockJob *job, Error **errp);
239
240
/**
241
- * block_job_pause:
242
- * @job: The job to be paused.
243
- *
244
- * Asynchronously pause the specified job.
245
- */
246
-void block_job_pause(BlockJob *job);
247
-
248
-/**
249
* block_job_user_pause:
250
* @job: The job to be paused.
251
*
252
@@ -XXX,XX +XXX,XX @@ void block_job_user_pause(BlockJob *job);
253
bool block_job_user_paused(BlockJob *job);
254
255
/**
256
- * block_job_resume:
257
- * @job: The job to be resumed.
258
- *
259
- * Resume the specified job. Must be paired with a preceding block_job_pause.
260
- */
261
-void block_job_resume(BlockJob *job);
262
-
263
-/**
264
* block_job_user_resume:
265
* @job: The job to be resumed.
266
*
267
diff --git a/include/block/blockjob_int.h b/include/block/blockjob_int.h
268
index XXXXXXX..XXXXXXX 100644
269
--- a/include/block/blockjob_int.h
270
+++ b/include/block/blockjob_int.h
271
@@ -XXX,XX +XXX,XX @@ void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns);
272
void block_job_yield(BlockJob *job);
273
274
/**
275
+ * block_job_pause_all:
276
+ *
277
+ * Asynchronously pause all jobs.
278
+ */
279
+void block_job_pause_all(void);
280
+
281
+/**
282
+ * block_job_resume_all:
283
+ *
284
+ * Resume all block jobs. Must be paired with a preceding block_job_pause_all.
285
+ */
286
+void block_job_resume_all(void);
287
+
288
+/**
289
* block_job_early_fail:
290
* @bs: The block device.
291
*
292
--
50
--
293
2.9.3
51
2.21.0
294
52
295
53
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
Signed-off-by: Max Reitz <mreitz@redhat.com>
2
Reviewed-by: Eric Blake <eblake@redhat.com>
3
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
4
Message-id: 20190507203508.18026-6-mreitz@redhat.com
5
Signed-off-by: Max Reitz <mreitz@redhat.com>
6
---
7
qapi/block-core.json | 5 ++++-
8
block/blkdebug.c | 8 ++++++++
9
2 files changed, 12 insertions(+), 1 deletion(-)
2
10
3
We have two different headers for block job operations, blockjob.h
11
diff --git a/qapi/block-core.json b/qapi/block-core.json
4
and blockjob_int.h. The former contains APIs called by the monitor,
5
the latter contains APIs called by the block job drivers and the
6
block layer itself.
7
8
Keep the two APIs separate in the blockjob.c file too. This will
9
be useful when transitioning away from the AioContext lock, because
10
there will be locking policies for the two categories, too---the
11
monitor will have to call new block_job_lock/unlock APIs, while blockjob
12
APIs will take care of this for the users.
13
14
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
15
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
16
Message-id: 20170508141310.8674-6-pbonzini@redhat.com
17
Signed-off-by: Jeff Cody <jcody@redhat.com>
18
---
19
blockjob.c | 390 ++++++++++++++++++++++++++++++++-----------------------------
20
1 file changed, 205 insertions(+), 185 deletions(-)
21
22
diff --git a/blockjob.c b/blockjob.c
23
index XXXXXXX..XXXXXXX 100644
12
index XXXXXXX..XXXXXXX 100644
24
--- a/blockjob.c
13
--- a/qapi/block-core.json
25
+++ b/blockjob.c
14
+++ b/qapi/block-core.json
26
@@ -XXX,XX +XXX,XX @@ struct BlockJobTxn {
15
@@ -XXX,XX +XXX,XX @@
27
16
#
28
static QLIST_HEAD(, BlockJob) block_jobs = QLIST_HEAD_INITIALIZER(block_jobs);
17
# @flush: .bdrv_co_flush_to_disk()
29
18
#
30
+/*
19
+# @block-status: .bdrv_co_block_status()
31
+ * The block job API is composed of two categories of functions.
20
+#
32
+ *
21
# Since: 4.1
33
+ * The first includes functions used by the monitor. The monitor is
22
##
34
+ * peculiar in that it accesses the block job list with block_job_get, and
23
{ 'enum': 'BlkdebugIOType', 'prefix': 'BLKDEBUG_IO_TYPE',
35
+ * therefore needs consistency across block_job_get and the actual operation
24
- 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush' ] }
36
+ * (e.g. block_job_set_speed). The consistency is achieved with
25
+ 'data': [ 'read', 'write', 'write-zeroes', 'discard', 'flush',
37
+ * aio_context_acquire/release. These functions are declared in blockjob.h.
26
+ 'block-status' ] }
38
+ *
27
39
+ * The second includes functions used by the block job drivers and sometimes
28
##
40
+ * by the core block layer. These do not care about locking, because the
29
# @BlkdebugInjectErrorOptions:
41
+ * whole coroutine runs under the AioContext lock, and are declared in
30
diff --git a/block/blkdebug.c b/block/blkdebug.c
42
+ * blockjob_int.h.
31
index XXXXXXX..XXXXXXX 100644
43
+ */
32
--- a/block/blkdebug.c
33
+++ b/block/blkdebug.c
34
@@ -XXX,XX +XXX,XX @@ static int coroutine_fn blkdebug_co_block_status(BlockDriverState *bs,
35
int64_t *map,
36
BlockDriverState **file)
37
{
38
+ int err;
44
+
39
+
45
BlockJob *block_job_next(BlockJob *job)
40
assert(QEMU_IS_ALIGNED(offset | bytes, bs->bl.request_alignment));
46
{
47
if (!job) {
48
@@ -XXX,XX +XXX,XX @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs,
49
return 0;
50
}
51
52
-void *block_job_create(const char *job_id, const BlockJobDriver *driver,
53
- BlockDriverState *bs, uint64_t perm,
54
- uint64_t shared_perm, int64_t speed, int flags,
55
- BlockCompletionFunc *cb, void *opaque, Error **errp)
56
-{
57
- BlockBackend *blk;
58
- BlockJob *job;
59
- int ret;
60
-
61
- if (bs->job) {
62
- error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
63
- return NULL;
64
- }
65
-
66
- if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) {
67
- job_id = bdrv_get_device_name(bs);
68
- if (!*job_id) {
69
- error_setg(errp, "An explicit job ID is required for this node");
70
- return NULL;
71
- }
72
- }
73
-
74
- if (job_id) {
75
- if (flags & BLOCK_JOB_INTERNAL) {
76
- error_setg(errp, "Cannot specify job ID for internal block job");
77
- return NULL;
78
- }
79
-
80
- if (!id_wellformed(job_id)) {
81
- error_setg(errp, "Invalid job ID '%s'", job_id);
82
- return NULL;
83
- }
84
-
85
- if (block_job_get(job_id)) {
86
- error_setg(errp, "Job ID '%s' already in use", job_id);
87
- return NULL;
88
- }
89
- }
90
-
91
- blk = blk_new(perm, shared_perm);
92
- ret = blk_insert_bs(blk, bs, errp);
93
- if (ret < 0) {
94
- blk_unref(blk);
95
- return NULL;
96
- }
97
-
98
- job = g_malloc0(driver->instance_size);
99
- job->driver = driver;
100
- job->id = g_strdup(job_id);
101
- job->blk = blk;
102
- job->cb = cb;
103
- job->opaque = opaque;
104
- job->busy = false;
105
- job->paused = true;
106
- job->pause_count = 1;
107
- job->refcnt = 1;
108
-
109
- error_setg(&job->blocker, "block device is in use by block job: %s",
110
- BlockJobType_lookup[driver->job_type]);
111
- block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
112
- bs->job = job;
113
-
114
- blk_set_dev_ops(blk, &block_job_dev_ops, job);
115
- bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
116
-
117
- QLIST_INSERT_HEAD(&block_jobs, job, job_list);
118
-
119
- blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
120
- block_job_detach_aio_context, job);
121
-
122
- /* Only set speed when necessary to avoid NotSupported error */
123
- if (speed != 0) {
124
- Error *local_err = NULL;
125
-
126
- block_job_set_speed(job, speed, &local_err);
127
- if (local_err) {
128
- block_job_unref(job);
129
- error_propagate(errp, local_err);
130
- return NULL;
131
- }
132
- }
133
- return job;
134
-}
135
-
136
bool block_job_is_internal(BlockJob *job)
137
{
138
return (job->id == NULL);
139
@@ -XXX,XX +XXX,XX @@ void block_job_start(BlockJob *job)
140
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
141
}
142
143
-void block_job_early_fail(BlockJob *job)
144
-{
145
- block_job_unref(job);
146
-}
147
-
148
static void block_job_completed_single(BlockJob *job)
149
{
150
if (!job->ret) {
151
@@ -XXX,XX +XXX,XX @@ static void block_job_completed_txn_success(BlockJob *job)
152
}
153
}
154
155
-void block_job_completed(BlockJob *job, int ret)
156
-{
157
- assert(blk_bs(job->blk)->job == job);
158
- assert(!job->completed);
159
- job->completed = true;
160
- job->ret = ret;
161
- if (!job->txn) {
162
- block_job_completed_single(job);
163
- } else if (ret < 0 || block_job_is_cancelled(job)) {
164
- block_job_completed_txn_abort(job);
165
- } else {
166
- block_job_completed_txn_success(job);
167
- }
168
-}
169
-
170
void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
171
{
172
Error *local_err = NULL;
173
@@ -XXX,XX +XXX,XX @@ void block_job_user_pause(BlockJob *job)
174
block_job_pause(job);
175
}
176
177
-static bool block_job_should_pause(BlockJob *job)
178
-{
179
- return job->pause_count > 0;
180
-}
181
-
182
bool block_job_user_paused(BlockJob *job)
183
{
184
return job->user_paused;
185
}
186
187
-void coroutine_fn block_job_pause_point(BlockJob *job)
188
-{
189
- assert(job && block_job_started(job));
190
-
191
- if (!block_job_should_pause(job)) {
192
- return;
193
- }
194
- if (block_job_is_cancelled(job)) {
195
- return;
196
- }
197
-
198
- if (job->driver->pause) {
199
- job->driver->pause(job);
200
- }
201
-
202
- if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
203
- job->paused = true;
204
- job->busy = false;
205
- qemu_coroutine_yield(); /* wait for block_job_resume() */
206
- job->busy = true;
207
- job->paused = false;
208
- }
209
-
210
- if (job->driver->resume) {
211
- job->driver->resume(job);
212
- }
213
-}
214
-
215
void block_job_user_resume(BlockJob *job)
216
{
217
if (job && job->user_paused && job->pause_count > 0) {
218
@@ -XXX,XX +XXX,XX @@ void block_job_user_resume(BlockJob *job)
219
}
220
}
221
222
-void block_job_enter(BlockJob *job)
223
-{
224
- if (job->co && !job->busy) {
225
- bdrv_coroutine_enter(blk_bs(job->blk), job->co);
226
- }
227
-}
228
-
229
void block_job_cancel(BlockJob *job)
230
{
231
if (block_job_started(job)) {
232
@@ -XXX,XX +XXX,XX @@ void block_job_cancel(BlockJob *job)
233
}
234
}
235
236
-bool block_job_is_cancelled(BlockJob *job)
237
-{
238
- return job->cancelled;
239
-}
240
-
241
void block_job_iostatus_reset(BlockJob *job)
242
{
243
job->iostatus = BLOCK_DEVICE_IO_STATUS_OK;
244
@@ -XXX,XX +XXX,XX @@ int block_job_complete_sync(BlockJob *job, Error **errp)
245
return block_job_finish_sync(job, &block_job_complete, errp);
246
}
247
248
-void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
249
-{
250
- assert(job->busy);
251
-
252
- /* Check cancellation *before* setting busy = false, too! */
253
- if (block_job_is_cancelled(job)) {
254
- return;
255
- }
256
-
257
- job->busy = false;
258
- if (!block_job_should_pause(job)) {
259
- co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
260
- }
261
- job->busy = true;
262
-
263
- block_job_pause_point(job);
264
-}
265
-
266
-void block_job_yield(BlockJob *job)
267
-{
268
- assert(job->busy);
269
-
270
- /* Check cancellation *before* setting busy = false, too! */
271
- if (block_job_is_cancelled(job)) {
272
- return;
273
- }
274
-
275
- job->busy = false;
276
- if (!block_job_should_pause(job)) {
277
- qemu_coroutine_yield();
278
- }
279
- job->busy = true;
280
-
281
- block_job_pause_point(job);
282
-}
283
-
284
BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
285
{
286
BlockJobInfo *info;
287
@@ -XXX,XX +XXX,XX @@ static void block_job_event_completed(BlockJob *job, const char *msg)
288
&error_abort);
289
}
290
291
+/*
292
+ * API for block job drivers and the block layer. These functions are
293
+ * declared in blockjob_int.h.
294
+ */
295
+
41
+
296
+void *block_job_create(const char *job_id, const BlockJobDriver *driver,
42
+ err = rule_check(bs, offset, bytes, BLKDEBUG_IO_TYPE_BLOCK_STATUS);
297
+ BlockDriverState *bs, uint64_t perm,
43
+ if (err) {
298
+ uint64_t shared_perm, int64_t speed, int flags,
44
+ return err;
299
+ BlockCompletionFunc *cb, void *opaque, Error **errp)
300
+{
301
+ BlockBackend *blk;
302
+ BlockJob *job;
303
+ int ret;
304
+
305
+ if (bs->job) {
306
+ error_setg(errp, QERR_DEVICE_IN_USE, bdrv_get_device_name(bs));
307
+ return NULL;
308
+ }
45
+ }
309
+
46
+
310
+ if (job_id == NULL && !(flags & BLOCK_JOB_INTERNAL)) {
47
return bdrv_co_block_status_from_file(bs, want_zero, offset, bytes,
311
+ job_id = bdrv_get_device_name(bs);
48
pnum, map, file);
312
+ if (!*job_id) {
313
+ error_setg(errp, "An explicit job ID is required for this node");
314
+ return NULL;
315
+ }
316
+ }
317
+
318
+ if (job_id) {
319
+ if (flags & BLOCK_JOB_INTERNAL) {
320
+ error_setg(errp, "Cannot specify job ID for internal block job");
321
+ return NULL;
322
+ }
323
+
324
+ if (!id_wellformed(job_id)) {
325
+ error_setg(errp, "Invalid job ID '%s'", job_id);
326
+ return NULL;
327
+ }
328
+
329
+ if (block_job_get(job_id)) {
330
+ error_setg(errp, "Job ID '%s' already in use", job_id);
331
+ return NULL;
332
+ }
333
+ }
334
+
335
+ blk = blk_new(perm, shared_perm);
336
+ ret = blk_insert_bs(blk, bs, errp);
337
+ if (ret < 0) {
338
+ blk_unref(blk);
339
+ return NULL;
340
+ }
341
+
342
+ job = g_malloc0(driver->instance_size);
343
+ job->driver = driver;
344
+ job->id = g_strdup(job_id);
345
+ job->blk = blk;
346
+ job->cb = cb;
347
+ job->opaque = opaque;
348
+ job->busy = false;
349
+ job->paused = true;
350
+ job->pause_count = 1;
351
+ job->refcnt = 1;
352
+
353
+ error_setg(&job->blocker, "block device is in use by block job: %s",
354
+ BlockJobType_lookup[driver->job_type]);
355
+ block_job_add_bdrv(job, "main node", bs, 0, BLK_PERM_ALL, &error_abort);
356
+ bs->job = job;
357
+
358
+ blk_set_dev_ops(blk, &block_job_dev_ops, job);
359
+ bdrv_op_unblock(bs, BLOCK_OP_TYPE_DATAPLANE, job->blocker);
360
+
361
+ QLIST_INSERT_HEAD(&block_jobs, job, job_list);
362
+
363
+ blk_add_aio_context_notifier(blk, block_job_attached_aio_context,
364
+ block_job_detach_aio_context, job);
365
+
366
+ /* Only set speed when necessary to avoid NotSupported error */
367
+ if (speed != 0) {
368
+ Error *local_err = NULL;
369
+
370
+ block_job_set_speed(job, speed, &local_err);
371
+ if (local_err) {
372
+ block_job_unref(job);
373
+ error_propagate(errp, local_err);
374
+ return NULL;
375
+ }
376
+ }
377
+ return job;
378
+}
379
+
380
void block_job_pause_all(void)
381
{
382
BlockJob *job = NULL;
383
@@ -XXX,XX +XXX,XX @@ void block_job_pause_all(void)
384
}
385
}
49
}
386
387
+void block_job_early_fail(BlockJob *job)
388
+{
389
+ block_job_unref(job);
390
+}
391
+
392
+void block_job_completed(BlockJob *job, int ret)
393
+{
394
+ assert(blk_bs(job->blk)->job == job);
395
+ assert(!job->completed);
396
+ job->completed = true;
397
+ job->ret = ret;
398
+ if (!job->txn) {
399
+ block_job_completed_single(job);
400
+ } else if (ret < 0 || block_job_is_cancelled(job)) {
401
+ block_job_completed_txn_abort(job);
402
+ } else {
403
+ block_job_completed_txn_success(job);
404
+ }
405
+}
406
+
407
+static bool block_job_should_pause(BlockJob *job)
408
+{
409
+ return job->pause_count > 0;
410
+}
411
+
412
+void coroutine_fn block_job_pause_point(BlockJob *job)
413
+{
414
+ assert(job && block_job_started(job));
415
+
416
+ if (!block_job_should_pause(job)) {
417
+ return;
418
+ }
419
+ if (block_job_is_cancelled(job)) {
420
+ return;
421
+ }
422
+
423
+ if (job->driver->pause) {
424
+ job->driver->pause(job);
425
+ }
426
+
427
+ if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
428
+ job->paused = true;
429
+ job->busy = false;
430
+ qemu_coroutine_yield(); /* wait for block_job_resume() */
431
+ job->busy = true;
432
+ job->paused = false;
433
+ }
434
+
435
+ if (job->driver->resume) {
436
+ job->driver->resume(job);
437
+ }
438
+}
439
+
440
void block_job_resume_all(void)
441
{
442
BlockJob *job = NULL;
443
@@ -XXX,XX +XXX,XX @@ void block_job_resume_all(void)
444
}
445
}
446
447
+void block_job_enter(BlockJob *job)
448
+{
449
+ if (job->co && !job->busy) {
450
+ bdrv_coroutine_enter(blk_bs(job->blk), job->co);
451
+ }
452
+}
453
+
454
+bool block_job_is_cancelled(BlockJob *job)
455
+{
456
+ return job->cancelled;
457
+}
458
+
459
+void block_job_sleep_ns(BlockJob *job, QEMUClockType type, int64_t ns)
460
+{
461
+ assert(job->busy);
462
+
463
+ /* Check cancellation *before* setting busy = false, too! */
464
+ if (block_job_is_cancelled(job)) {
465
+ return;
466
+ }
467
+
468
+ job->busy = false;
469
+ if (!block_job_should_pause(job)) {
470
+ co_aio_sleep_ns(blk_get_aio_context(job->blk), type, ns);
471
+ }
472
+ job->busy = true;
473
+
474
+ block_job_pause_point(job);
475
+}
476
+
477
+void block_job_yield(BlockJob *job)
478
+{
479
+ assert(job->busy);
480
+
481
+ /* Check cancellation *before* setting busy = false, too! */
482
+ if (block_job_is_cancelled(job)) {
483
+ return;
484
+ }
485
+
486
+ job->busy = false;
487
+ if (!block_job_should_pause(job)) {
488
+ qemu_coroutine_yield();
489
+ }
490
+ job->busy = true;
491
+
492
+ block_job_pause_point(job);
493
+}
494
+
495
void block_job_event_ready(BlockJob *job)
496
{
497
job->ready = true;
498
--
50
--
499
2.9.3
51
2.21.0
500
52
501
53
diff view generated by jsdifflib
New patch
1
1
This test converts a simple image to another, but blkdebug injects
2
block_status and read faults at some offsets. The resulting image
3
should be the same as the input image, except that sectors that could
4
not be read have to be 0.
5
6
Signed-off-by: Max Reitz <mreitz@redhat.com>
7
Message-id: 20190507203508.18026-7-mreitz@redhat.com
8
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
9
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
10
[mreitz: Dropped superfluous printf from _filter_offsets, as suggested
11
by Vladimir; disable test for VDI and IMGOPTSSYNTAX]
12
Signed-off-by: Max Reitz <mreitz@redhat.com>
13
---
14
tests/qemu-iotests/251 | 170 +++++++++++++++++++++++++++++++++++++
15
tests/qemu-iotests/251.out | 43 ++++++++++
16
tests/qemu-iotests/group | 1 +
17
3 files changed, 214 insertions(+)
18
create mode 100755 tests/qemu-iotests/251
19
create mode 100644 tests/qemu-iotests/251.out
20
21
diff --git a/tests/qemu-iotests/251 b/tests/qemu-iotests/251
22
new file mode 100755
23
index XXXXXXX..XXXXXXX
24
--- /dev/null
25
+++ b/tests/qemu-iotests/251
26
@@ -XXX,XX +XXX,XX @@
27
+#!/usr/bin/env bash
28
+#
29
+# Test qemu-img convert --salvage
30
+#
31
+# Copyright (C) 2019 Red Hat, Inc.
32
+#
33
+# This program is free software; you can redistribute it and/or modify
34
+# it under the terms of the GNU General Public License as published by
35
+# the Free Software Foundation; either version 2 of the License, or
36
+# (at your option) any later version.
37
+#
38
+# This program is distributed in the hope that it will be useful,
39
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
40
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
41
+# GNU General Public License for more details.
42
+#
43
+# You should have received a copy of the GNU General Public License
44
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
45
+#
46
+
47
+# creator
48
+owner=mreitz@redhat.com
49
+
50
+seq=$(basename $0)
51
+echo "QA output created by $seq"
52
+
53
+status=1    # failure is the default!
54
+
55
+_cleanup()
56
+{
57
+ _cleanup_test_img
58
+}
59
+trap "_cleanup; exit \$status" 0 1 2 3 15
60
+
61
+# get standard environment, filters and checks
62
+. ./common.rc
63
+. ./common.filter
64
+. ./common.qemu
65
+
66
+_supported_fmt generic
67
+_supported_proto file
68
+_supported_os Linux
69
+
70
+if [ "$IMGOPTSSYNTAX" = "true" ]; then
71
+ # We use json:{} filenames here, so we cannot work with additional options.
72
+ _unsupported_fmt $IMGFMT
73
+else
74
+ # With VDI, the output is ordered differently. Just disable it.
75
+ _unsupported_fmt vdi
76
+fi
77
+
78
+
79
+TEST_IMG="$TEST_IMG.orig" _make_test_img 64M
80
+
81
+$QEMU_IO -c 'write -P 42 0 64M' "$TEST_IMG.orig" | _filter_qemu_io
82
+
83
+
84
+sector_size=512
85
+
86
+# Offsets on which to fail block-status. Keep in ascending order so
87
+# the indexing done by _filter_offsets will appear in ascending order
88
+# in the output as well.
89
+status_fail_offsets="$((16 * 1024 * 1024 + 8192))
90
+ $((33 * 1024 * 1024 + 512))"
91
+
92
+# Offsets on which to fail reads. Keep in ascending order for the
93
+# same reason.
94
+# The second element is shared with $status_fail_offsets on purpose.
95
+# Starting with the third element, we test what happens when a
96
+# continuous range of sectors is inaccessible.
97
+read_fail_offsets="$((32 * 1024 * 1024 - 65536))
98
+ $((33 * 1024 * 1024 + 512))
99
+ $(seq $((34 * 1024 * 1024)) $sector_size \
100
+ $((34 * 1024 * 1024 + 4096 - $sector_size)))"
101
+
102
+
103
+# blkdebug must be above the format layer so it can intercept all
104
+# block-status events
105
+source_img="json:{'driver': 'blkdebug',
106
+ 'image': {
107
+ 'driver': '$IMGFMT',
108
+ 'file': {
109
+ 'driver': 'file',
110
+ 'filename': '$TEST_IMG.orig'
111
+ }
112
+ },
113
+ 'inject-error': ["
114
+
115
+for ofs in $status_fail_offsets
116
+do
117
+ source_img+="{ 'event': 'none',
118
+ 'iotype': 'block-status',
119
+ 'errno': 5,
120
+ 'sector': $((ofs / sector_size)) },"
121
+done
122
+
123
+for ofs in $read_fail_offsets
124
+do
125
+ source_img+="{ 'event': 'none',
126
+ 'iotype': 'read',
127
+ 'errno': 5,
128
+ 'sector': $((ofs / sector_size)) },"
129
+done
130
+
131
+# Remove the trailing comma and terminate @inject-error and json:{}
132
+source_img="${source_img%,} ] }"
133
+
134
+
135
+echo
136
+
137
+
138
+_filter_offsets() {
139
+ filters=
140
+
141
+ index=0
142
+ for ofs in $1
143
+ do
144
+ filters+=" -e s/$ofs/status_fail_offset_$index/"
145
+ index=$((index + 1))
146
+ done
147
+
148
+ index=0
149
+ for ofs in $2
150
+ do
151
+ filters+=" -e s/$ofs/read_fail_offset_$index/"
152
+ index=$((index + 1))
153
+ done
154
+
155
+ sed $filters
156
+}
157
+
158
+# While determining the number of allocated sectors in the input
159
+# image, we should see one block status warning per element of
160
+# $status_fail_offsets.
161
+#
162
+# Then, the image is read. Since the block status is queried in
163
+# basically the same way, the same warnings as in the previous step
164
+# should reappear. Interleaved with those we should see a read
165
+# warning per element of $read_fail_offsets.
166
+# Note that $read_fail_offsets and $status_fail_offsets share an
167
+# element (read_fail_offset_1 == status_fail_offset_1), so
168
+# "status_fail_offset_1" in the output is the same as
169
+# "read_fail_offset_1".
170
+$QEMU_IMG convert --salvage "$source_img" "$TEST_IMG" 2>&1 \
171
+ | _filter_offsets "$status_fail_offsets" "$read_fail_offsets"
172
+
173
+echo
174
+
175
+# The offsets where the block status could not be determined should
176
+# have been treated as containing data and thus should be correct in
177
+# the output image.
178
+# The offsets where reading failed altogether should be 0. Make them
179
+# 0 in the input image, too, so we can compare both images.
180
+for ofs in $read_fail_offsets
181
+do
182
+ $QEMU_IO -c "write -z $ofs $sector_size" "$TEST_IMG.orig" \
183
+ | _filter_qemu_io \
184
+ | _filter_offsets '' "$read_fail_offsets"
185
+done
186
+
187
+echo
188
+
189
+# These should be equal now.
190
+$QEMU_IMG compare "$TEST_IMG.orig" "$TEST_IMG"
191
+
192
+
193
+# success, all done
194
+echo "*** done"
195
+rm -f $seq.full
196
+status=0
197
diff --git a/tests/qemu-iotests/251.out b/tests/qemu-iotests/251.out
198
new file mode 100644
199
index XXXXXXX..XXXXXXX
200
--- /dev/null
201
+++ b/tests/qemu-iotests/251.out
202
@@ -XXX,XX +XXX,XX @@
203
+QA output created by 251
204
+Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=67108864
205
+wrote 67108864/67108864 bytes at offset 0
206
+64 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
207
+
208
+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
209
+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
210
+qemu-img: warning: error while reading block status at offset status_fail_offset_0: Input/output error
211
+qemu-img: warning: error while reading offset read_fail_offset_0: Input/output error
212
+qemu-img: warning: error while reading block status at offset status_fail_offset_1: Input/output error
213
+qemu-img: warning: error while reading offset status_fail_offset_1: Input/output error
214
+qemu-img: warning: error while reading offset read_fail_offset_2: Input/output error
215
+qemu-img: warning: error while reading offset read_fail_offset_3: Input/output error
216
+qemu-img: warning: error while reading offset read_fail_offset_4: Input/output error
217
+qemu-img: warning: error while reading offset read_fail_offset_5: Input/output error
218
+qemu-img: warning: error while reading offset read_fail_offset_6: Input/output error
219
+qemu-img: warning: error while reading offset read_fail_offset_7: Input/output error
220
+qemu-img: warning: error while reading offset read_fail_offset_8: Input/output error
221
+qemu-img: warning: error while reading offset read_fail_offset_9: Input/output error
222
+
223
+wrote 512/512 bytes at offset read_fail_offset_0
224
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
225
+wrote 512/512 bytes at offset read_fail_offset_1
226
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
227
+wrote 512/512 bytes at offset read_fail_offset_2
228
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
229
+wrote 512/512 bytes at offset read_fail_offset_3
230
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
231
+wrote 512/512 bytes at offset read_fail_offset_4
232
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
233
+wrote 512/512 bytes at offset read_fail_offset_5
234
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
235
+wrote 512/512 bytes at offset read_fail_offset_6
236
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
237
+wrote 512/512 bytes at offset read_fail_offset_7
238
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
239
+wrote 512/512 bytes at offset read_fail_offset_8
240
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
241
+wrote 512/512 bytes at offset read_fail_offset_9
242
+512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
243
+
244
+Images are identical.
245
+*** done
246
diff --git a/tests/qemu-iotests/group b/tests/qemu-iotests/group
247
index XXXXXXX..XXXXXXX 100644
248
--- a/tests/qemu-iotests/group
249
+++ b/tests/qemu-iotests/group
250
@@ -XXX,XX +XXX,XX @@
251
248 rw quick
252
249 rw auto quick
253
250 rw auto quick
254
+251 rw auto quick
255
252 rw auto backing quick
256
253 rw auto quick
257
254 rw auto backing quick
258
--
259
2.21.0
260
261
diff view generated by jsdifflib
1
From: Paolo Bonzini <pbonzini@redhat.com>
1
We do not support this combination (yet), so this should yield an error
2
message.
2
3
3
!job is always checked prior to the call, drop it from here.
4
Signed-off-by: Max Reitz <mreitz@redhat.com>
5
Tested-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
6
Reviewed-by: Vladimir Sementsov-Ogievskiy <vsementsov@virtuozzo.com>
7
Message-id: 20190507203508.18026-8-mreitz@redhat.com
8
Signed-off-by: Max Reitz <mreitz@redhat.com>
9
---
10
tests/qemu-iotests/082 | 1 +
11
tests/qemu-iotests/082.out | 3 +++
12
2 files changed, 4 insertions(+)
4
13
5
Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com>
14
diff --git a/tests/qemu-iotests/082 b/tests/qemu-iotests/082
6
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
15
index XXXXXXX..XXXXXXX 100755
7
Reviewed-by: Jeff Cody <jcody@redhat.com>
16
--- a/tests/qemu-iotests/082
8
Message-id: 20170508141310.8674-2-pbonzini@redhat.com
17
+++ b/tests/qemu-iotests/082
9
Signed-off-by: Jeff Cody <jcody@redhat.com>
18
@@ -XXX,XX +XXX,XX @@ echo === convert: -C and other options ===
10
---
19
run_qemu_img convert -C -S 4k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
11
blockjob.c | 2 +-
20
run_qemu_img convert -C -S 8k -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
12
1 file changed, 1 insertion(+), 1 deletion(-)
21
run_qemu_img convert -C -c -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
13
22
+run_qemu_img convert -C --salvage -O $IMGFMT "$TEST_IMG" "$TEST_IMG".target
14
diff --git a/blockjob.c b/blockjob.c
23
24
echo
25
echo === amend: Options specified more than once ===
26
diff --git a/tests/qemu-iotests/082.out b/tests/qemu-iotests/082.out
15
index XXXXXXX..XXXXXXX 100644
27
index XXXXXXX..XXXXXXX 100644
16
--- a/blockjob.c
28
--- a/tests/qemu-iotests/082.out
17
+++ b/blockjob.c
29
+++ b/tests/qemu-iotests/082.out
18
@@ -XXX,XX +XXX,XX @@ static bool block_job_should_pause(BlockJob *job)
30
@@ -XXX,XX +XXX,XX @@ qemu-img: Cannot enable copy offloading when -S is used
19
31
Testing: convert -C -c -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
20
bool block_job_user_paused(BlockJob *job)
32
qemu-img: Cannot enable copy offloading when -c is used
21
{
33
22
- return job ? job->user_paused : 0;
34
+Testing: convert -C --salvage -O qcow2 TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.target
23
+ return job->user_paused;
35
+qemu-img: Cannot use copy offloading in salvaging mode
24
}
36
+
25
37
=== amend: Options specified more than once ===
26
void coroutine_fn block_job_pause_point(BlockJob *job)
38
39
Testing: amend -f foo -f qcow2 -o lazy_refcounts=on TEST_DIR/t.qcow2
27
--
40
--
28
2.9.3
41
2.21.0
29
42
30
43
diff view generated by jsdifflib