root / block / commit.c @ dc7588c1
History | View | Annotate | Download (7.3 kB)
1 | 747ff602 | Jeff Cody | /*
|
---|---|---|---|
2 | 747ff602 | Jeff Cody | * Live block commit
|
3 | 747ff602 | Jeff Cody | *
|
4 | 747ff602 | Jeff Cody | * Copyright Red Hat, Inc. 2012
|
5 | 747ff602 | Jeff Cody | *
|
6 | 747ff602 | Jeff Cody | * Authors:
|
7 | 747ff602 | Jeff Cody | * Jeff Cody <jcody@redhat.com>
|
8 | 747ff602 | Jeff Cody | * Based on stream.c by Stefan Hajnoczi
|
9 | 747ff602 | Jeff Cody | *
|
10 | 747ff602 | Jeff Cody | * This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
11 | 747ff602 | Jeff Cody | * See the COPYING.LIB file in the top-level directory.
|
12 | 747ff602 | Jeff Cody | *
|
13 | 747ff602 | Jeff Cody | */
|
14 | 747ff602 | Jeff Cody | |
15 | 747ff602 | Jeff Cody | #include "trace.h" |
16 | 737e150e | Paolo Bonzini | #include "block/block_int.h" |
17 | 737e150e | Paolo Bonzini | #include "block/blockjob.h" |
18 | 747ff602 | Jeff Cody | #include "qemu/ratelimit.h" |
19 | 747ff602 | Jeff Cody | |
20 | 747ff602 | Jeff Cody | enum {
|
21 | 747ff602 | Jeff Cody | /*
|
22 | 747ff602 | Jeff Cody | * Size of data buffer for populating the image file. This should be large
|
23 | 747ff602 | Jeff Cody | * enough to process multiple clusters in a single call, so that populating
|
24 | 747ff602 | Jeff Cody | * contiguous regions of the image is efficient.
|
25 | 747ff602 | Jeff Cody | */
|
26 | 747ff602 | Jeff Cody | COMMIT_BUFFER_SIZE = 512 * 1024, /* in bytes */ |
27 | 747ff602 | Jeff Cody | }; |
28 | 747ff602 | Jeff Cody | |
29 | 747ff602 | Jeff Cody | #define SLICE_TIME 100000000ULL /* ns */ |
30 | 747ff602 | Jeff Cody | |
31 | 747ff602 | Jeff Cody | typedef struct CommitBlockJob { |
32 | 747ff602 | Jeff Cody | BlockJob common; |
33 | 747ff602 | Jeff Cody | RateLimit limit; |
34 | 747ff602 | Jeff Cody | BlockDriverState *active; |
35 | 747ff602 | Jeff Cody | BlockDriverState *top; |
36 | 747ff602 | Jeff Cody | BlockDriverState *base; |
37 | 92aa5c6d | Paolo Bonzini | BlockdevOnError on_error; |
38 | 747ff602 | Jeff Cody | int base_flags;
|
39 | 747ff602 | Jeff Cody | int orig_overlay_flags;
|
40 | 747ff602 | Jeff Cody | } CommitBlockJob; |
41 | 747ff602 | Jeff Cody | |
42 | 747ff602 | Jeff Cody | static int coroutine_fn commit_populate(BlockDriverState *bs, |
43 | 747ff602 | Jeff Cody | BlockDriverState *base, |
44 | 747ff602 | Jeff Cody | int64_t sector_num, int nb_sectors,
|
45 | 747ff602 | Jeff Cody | void *buf)
|
46 | 747ff602 | Jeff Cody | { |
47 | 747ff602 | Jeff Cody | int ret = 0; |
48 | 747ff602 | Jeff Cody | |
49 | 747ff602 | Jeff Cody | ret = bdrv_read(bs, sector_num, buf, nb_sectors); |
50 | 747ff602 | Jeff Cody | if (ret) {
|
51 | 747ff602 | Jeff Cody | return ret;
|
52 | 747ff602 | Jeff Cody | } |
53 | 747ff602 | Jeff Cody | |
54 | 747ff602 | Jeff Cody | ret = bdrv_write(base, sector_num, buf, nb_sectors); |
55 | 747ff602 | Jeff Cody | if (ret) {
|
56 | 747ff602 | Jeff Cody | return ret;
|
57 | 747ff602 | Jeff Cody | } |
58 | 747ff602 | Jeff Cody | |
59 | 747ff602 | Jeff Cody | return 0; |
60 | 747ff602 | Jeff Cody | } |
61 | 747ff602 | Jeff Cody | |
62 | 747ff602 | Jeff Cody | static void coroutine_fn commit_run(void *opaque) |
63 | 747ff602 | Jeff Cody | { |
64 | 747ff602 | Jeff Cody | CommitBlockJob *s = opaque; |
65 | 747ff602 | Jeff Cody | BlockDriverState *active = s->active; |
66 | 747ff602 | Jeff Cody | BlockDriverState *top = s->top; |
67 | 747ff602 | Jeff Cody | BlockDriverState *base = s->base; |
68 | 6d759117 | Jeff Cody | BlockDriverState *overlay_bs; |
69 | 747ff602 | Jeff Cody | int64_t sector_num, end; |
70 | 747ff602 | Jeff Cody | int ret = 0; |
71 | 747ff602 | Jeff Cody | int n = 0; |
72 | 747ff602 | Jeff Cody | void *buf;
|
73 | 747ff602 | Jeff Cody | int bytes_written = 0; |
74 | 747ff602 | Jeff Cody | int64_t base_len; |
75 | 747ff602 | Jeff Cody | |
76 | 747ff602 | Jeff Cody | ret = s->common.len = bdrv_getlength(top); |
77 | 747ff602 | Jeff Cody | |
78 | 747ff602 | Jeff Cody | |
79 | 747ff602 | Jeff Cody | if (s->common.len < 0) { |
80 | 747ff602 | Jeff Cody | goto exit_restore_reopen;
|
81 | 747ff602 | Jeff Cody | } |
82 | 747ff602 | Jeff Cody | |
83 | 747ff602 | Jeff Cody | ret = base_len = bdrv_getlength(base); |
84 | 747ff602 | Jeff Cody | if (base_len < 0) { |
85 | 747ff602 | Jeff Cody | goto exit_restore_reopen;
|
86 | 747ff602 | Jeff Cody | } |
87 | 747ff602 | Jeff Cody | |
88 | 747ff602 | Jeff Cody | if (base_len < s->common.len) {
|
89 | 747ff602 | Jeff Cody | ret = bdrv_truncate(base, s->common.len); |
90 | 747ff602 | Jeff Cody | if (ret) {
|
91 | 747ff602 | Jeff Cody | goto exit_restore_reopen;
|
92 | 747ff602 | Jeff Cody | } |
93 | 747ff602 | Jeff Cody | } |
94 | 747ff602 | Jeff Cody | |
95 | 747ff602 | Jeff Cody | end = s->common.len >> BDRV_SECTOR_BITS; |
96 | 747ff602 | Jeff Cody | buf = qemu_blockalign(top, COMMIT_BUFFER_SIZE); |
97 | 747ff602 | Jeff Cody | |
98 | 747ff602 | Jeff Cody | for (sector_num = 0; sector_num < end; sector_num += n) { |
99 | 747ff602 | Jeff Cody | uint64_t delay_ns = 0;
|
100 | 747ff602 | Jeff Cody | bool copy;
|
101 | 747ff602 | Jeff Cody | |
102 | 747ff602 | Jeff Cody | wait:
|
103 | 747ff602 | Jeff Cody | /* Note that even when no rate limit is applied we need to yield
|
104 | c57b6656 | Kevin Wolf | * with no pending I/O here so that bdrv_drain_all() returns.
|
105 | 747ff602 | Jeff Cody | */
|
106 | 747ff602 | Jeff Cody | block_job_sleep_ns(&s->common, rt_clock, delay_ns); |
107 | 747ff602 | Jeff Cody | if (block_job_is_cancelled(&s->common)) {
|
108 | 747ff602 | Jeff Cody | break;
|
109 | 747ff602 | Jeff Cody | } |
110 | 747ff602 | Jeff Cody | /* Copy if allocated above the base */
|
111 | 747ff602 | Jeff Cody | ret = bdrv_co_is_allocated_above(top, base, sector_num, |
112 | 747ff602 | Jeff Cody | COMMIT_BUFFER_SIZE / BDRV_SECTOR_SIZE, |
113 | 747ff602 | Jeff Cody | &n); |
114 | 747ff602 | Jeff Cody | copy = (ret == 1);
|
115 | 747ff602 | Jeff Cody | trace_commit_one_iteration(s, sector_num, n, ret); |
116 | 747ff602 | Jeff Cody | if (copy) {
|
117 | 747ff602 | Jeff Cody | if (s->common.speed) {
|
118 | 747ff602 | Jeff Cody | delay_ns = ratelimit_calculate_delay(&s->limit, n); |
119 | 747ff602 | Jeff Cody | if (delay_ns > 0) { |
120 | 747ff602 | Jeff Cody | goto wait;
|
121 | 747ff602 | Jeff Cody | } |
122 | 747ff602 | Jeff Cody | } |
123 | 747ff602 | Jeff Cody | ret = commit_populate(top, base, sector_num, n, buf); |
124 | 747ff602 | Jeff Cody | bytes_written += n * BDRV_SECTOR_SIZE; |
125 | 747ff602 | Jeff Cody | } |
126 | 747ff602 | Jeff Cody | if (ret < 0) { |
127 | 92aa5c6d | Paolo Bonzini | if (s->on_error == BLOCKDEV_ON_ERROR_STOP ||
|
128 | 92aa5c6d | Paolo Bonzini | s->on_error == BLOCKDEV_ON_ERROR_REPORT|| |
129 | 92aa5c6d | Paolo Bonzini | (s->on_error == BLOCKDEV_ON_ERROR_ENOSPC && ret == -ENOSPC)) { |
130 | 747ff602 | Jeff Cody | goto exit_free_buf;
|
131 | 747ff602 | Jeff Cody | } else {
|
132 | 747ff602 | Jeff Cody | n = 0;
|
133 | 747ff602 | Jeff Cody | continue;
|
134 | 747ff602 | Jeff Cody | } |
135 | 747ff602 | Jeff Cody | } |
136 | 747ff602 | Jeff Cody | /* Publish progress */
|
137 | 747ff602 | Jeff Cody | s->common.offset += n * BDRV_SECTOR_SIZE; |
138 | 747ff602 | Jeff Cody | } |
139 | 747ff602 | Jeff Cody | |
140 | 747ff602 | Jeff Cody | ret = 0;
|
141 | 747ff602 | Jeff Cody | |
142 | 747ff602 | Jeff Cody | if (!block_job_is_cancelled(&s->common) && sector_num == end) {
|
143 | 747ff602 | Jeff Cody | /* success */
|
144 | 747ff602 | Jeff Cody | ret = bdrv_drop_intermediate(active, top, base); |
145 | 747ff602 | Jeff Cody | } |
146 | 747ff602 | Jeff Cody | |
147 | 747ff602 | Jeff Cody | exit_free_buf:
|
148 | 747ff602 | Jeff Cody | qemu_vfree(buf); |
149 | 747ff602 | Jeff Cody | |
150 | 747ff602 | Jeff Cody | exit_restore_reopen:
|
151 | 747ff602 | Jeff Cody | /* restore base open flags here if appropriate (e.g., change the base back
|
152 | 747ff602 | Jeff Cody | * to r/o). These reopens do not need to be atomic, since we won't abort
|
153 | 747ff602 | Jeff Cody | * even on failure here */
|
154 | 747ff602 | Jeff Cody | if (s->base_flags != bdrv_get_flags(base)) {
|
155 | 747ff602 | Jeff Cody | bdrv_reopen(base, s->base_flags, NULL);
|
156 | 747ff602 | Jeff Cody | } |
157 | 6d759117 | Jeff Cody | overlay_bs = bdrv_find_overlay(active, top); |
158 | 6d759117 | Jeff Cody | if (overlay_bs && s->orig_overlay_flags != bdrv_get_flags(overlay_bs)) {
|
159 | 747ff602 | Jeff Cody | bdrv_reopen(overlay_bs, s->orig_overlay_flags, NULL);
|
160 | 747ff602 | Jeff Cody | } |
161 | 747ff602 | Jeff Cody | |
162 | 65f46322 | Paolo Bonzini | block_job_completed(&s->common, ret); |
163 | 747ff602 | Jeff Cody | } |
164 | 747ff602 | Jeff Cody | |
165 | 747ff602 | Jeff Cody | static void commit_set_speed(BlockJob *job, int64_t speed, Error **errp) |
166 | 747ff602 | Jeff Cody | { |
167 | 747ff602 | Jeff Cody | CommitBlockJob *s = container_of(job, CommitBlockJob, common); |
168 | 747ff602 | Jeff Cody | |
169 | 747ff602 | Jeff Cody | if (speed < 0) { |
170 | 747ff602 | Jeff Cody | error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
171 | 747ff602 | Jeff Cody | return;
|
172 | 747ff602 | Jeff Cody | } |
173 | 747ff602 | Jeff Cody | ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); |
174 | 747ff602 | Jeff Cody | } |
175 | 747ff602 | Jeff Cody | |
176 | 747ff602 | Jeff Cody | static BlockJobType commit_job_type = {
|
177 | 747ff602 | Jeff Cody | .instance_size = sizeof(CommitBlockJob),
|
178 | 747ff602 | Jeff Cody | .job_type = "commit",
|
179 | 747ff602 | Jeff Cody | .set_speed = commit_set_speed, |
180 | 747ff602 | Jeff Cody | }; |
181 | 747ff602 | Jeff Cody | |
182 | 747ff602 | Jeff Cody | void commit_start(BlockDriverState *bs, BlockDriverState *base,
|
183 | 747ff602 | Jeff Cody | BlockDriverState *top, int64_t speed, |
184 | 92aa5c6d | Paolo Bonzini | BlockdevOnError on_error, BlockDriverCompletionFunc *cb, |
185 | 747ff602 | Jeff Cody | void *opaque, Error **errp)
|
186 | 747ff602 | Jeff Cody | { |
187 | 747ff602 | Jeff Cody | CommitBlockJob *s; |
188 | 747ff602 | Jeff Cody | BlockReopenQueue *reopen_queue = NULL;
|
189 | 747ff602 | Jeff Cody | int orig_overlay_flags;
|
190 | 747ff602 | Jeff Cody | int orig_base_flags;
|
191 | 747ff602 | Jeff Cody | BlockDriverState *overlay_bs; |
192 | 747ff602 | Jeff Cody | Error *local_err = NULL;
|
193 | 747ff602 | Jeff Cody | |
194 | 92aa5c6d | Paolo Bonzini | if ((on_error == BLOCKDEV_ON_ERROR_STOP ||
|
195 | 92aa5c6d | Paolo Bonzini | on_error == BLOCKDEV_ON_ERROR_ENOSPC) && |
196 | 747ff602 | Jeff Cody | !bdrv_iostatus_is_enabled(bs)) { |
197 | 747ff602 | Jeff Cody | error_set(errp, QERR_INVALID_PARAMETER_COMBINATION); |
198 | 747ff602 | Jeff Cody | return;
|
199 | 747ff602 | Jeff Cody | } |
200 | 747ff602 | Jeff Cody | |
201 | 747ff602 | Jeff Cody | /* Once we support top == active layer, remove this check */
|
202 | 747ff602 | Jeff Cody | if (top == bs) {
|
203 | 747ff602 | Jeff Cody | error_setg(errp, |
204 | 747ff602 | Jeff Cody | "Top image as the active layer is currently unsupported");
|
205 | 747ff602 | Jeff Cody | return;
|
206 | 747ff602 | Jeff Cody | } |
207 | 747ff602 | Jeff Cody | |
208 | 747ff602 | Jeff Cody | if (top == base) {
|
209 | 747ff602 | Jeff Cody | error_setg(errp, "Invalid files for merge: top and base are the same");
|
210 | 747ff602 | Jeff Cody | return;
|
211 | 747ff602 | Jeff Cody | } |
212 | 747ff602 | Jeff Cody | |
213 | 747ff602 | Jeff Cody | overlay_bs = bdrv_find_overlay(bs, top); |
214 | 747ff602 | Jeff Cody | |
215 | 747ff602 | Jeff Cody | if (overlay_bs == NULL) { |
216 | 747ff602 | Jeff Cody | error_setg(errp, "Could not find overlay image for %s:", top->filename);
|
217 | 747ff602 | Jeff Cody | return;
|
218 | 747ff602 | Jeff Cody | } |
219 | 747ff602 | Jeff Cody | |
220 | 747ff602 | Jeff Cody | orig_base_flags = bdrv_get_flags(base); |
221 | 747ff602 | Jeff Cody | orig_overlay_flags = bdrv_get_flags(overlay_bs); |
222 | 747ff602 | Jeff Cody | |
223 | 747ff602 | Jeff Cody | /* convert base & overlay_bs to r/w, if necessary */
|
224 | 747ff602 | Jeff Cody | if (!(orig_base_flags & BDRV_O_RDWR)) {
|
225 | 747ff602 | Jeff Cody | reopen_queue = bdrv_reopen_queue(reopen_queue, base, |
226 | 747ff602 | Jeff Cody | orig_base_flags | BDRV_O_RDWR); |
227 | 747ff602 | Jeff Cody | } |
228 | 747ff602 | Jeff Cody | if (!(orig_overlay_flags & BDRV_O_RDWR)) {
|
229 | 747ff602 | Jeff Cody | reopen_queue = bdrv_reopen_queue(reopen_queue, overlay_bs, |
230 | 747ff602 | Jeff Cody | orig_overlay_flags | BDRV_O_RDWR); |
231 | 747ff602 | Jeff Cody | } |
232 | 747ff602 | Jeff Cody | if (reopen_queue) {
|
233 | 747ff602 | Jeff Cody | bdrv_reopen_multiple(reopen_queue, &local_err); |
234 | 747ff602 | Jeff Cody | if (local_err != NULL) { |
235 | 747ff602 | Jeff Cody | error_propagate(errp, local_err); |
236 | 747ff602 | Jeff Cody | return;
|
237 | 747ff602 | Jeff Cody | } |
238 | 747ff602 | Jeff Cody | } |
239 | 747ff602 | Jeff Cody | |
240 | 747ff602 | Jeff Cody | |
241 | 747ff602 | Jeff Cody | s = block_job_create(&commit_job_type, bs, speed, cb, opaque, errp); |
242 | 747ff602 | Jeff Cody | if (!s) {
|
243 | 747ff602 | Jeff Cody | return;
|
244 | 747ff602 | Jeff Cody | } |
245 | 747ff602 | Jeff Cody | |
246 | 747ff602 | Jeff Cody | s->base = base; |
247 | 747ff602 | Jeff Cody | s->top = top; |
248 | 747ff602 | Jeff Cody | s->active = bs; |
249 | 747ff602 | Jeff Cody | |
250 | 747ff602 | Jeff Cody | s->base_flags = orig_base_flags; |
251 | 747ff602 | Jeff Cody | s->orig_overlay_flags = orig_overlay_flags; |
252 | 747ff602 | Jeff Cody | |
253 | 747ff602 | Jeff Cody | s->on_error = on_error; |
254 | 747ff602 | Jeff Cody | s->common.co = qemu_coroutine_create(commit_run); |
255 | 747ff602 | Jeff Cody | |
256 | 747ff602 | Jeff Cody | trace_commit_start(bs, base, top, s, s->common.co, opaque); |
257 | 747ff602 | Jeff Cody | qemu_coroutine_enter(s->common.co, s); |
258 | 747ff602 | Jeff Cody | } |