root / block / stream.c @ 0834c9ea
History | View | Annotate | Download (5.8 kB)
1 | 4f1043b4 | Stefan Hajnoczi | /*
|
---|---|---|---|
2 | 4f1043b4 | Stefan Hajnoczi | * Image streaming
|
3 | 4f1043b4 | Stefan Hajnoczi | *
|
4 | 4f1043b4 | Stefan Hajnoczi | * Copyright IBM, Corp. 2011
|
5 | 4f1043b4 | Stefan Hajnoczi | *
|
6 | 4f1043b4 | Stefan Hajnoczi | * Authors:
|
7 | 4f1043b4 | Stefan Hajnoczi | * Stefan Hajnoczi <stefanha@linux.vnet.ibm.com>
|
8 | 4f1043b4 | Stefan Hajnoczi | *
|
9 | 4f1043b4 | Stefan Hajnoczi | * This work is licensed under the terms of the GNU LGPL, version 2 or later.
|
10 | 4f1043b4 | Stefan Hajnoczi | * See the COPYING.LIB file in the top-level directory.
|
11 | 4f1043b4 | Stefan Hajnoczi | *
|
12 | 4f1043b4 | Stefan Hajnoczi | */
|
13 | 4f1043b4 | Stefan Hajnoczi | |
14 | 4f1043b4 | Stefan Hajnoczi | #include "trace.h" |
15 | 4f1043b4 | Stefan Hajnoczi | #include "block_int.h" |
16 | 6ef228fc | Paolo Bonzini | #include "qemu/ratelimit.h" |
17 | 4f1043b4 | Stefan Hajnoczi | |
18 | 4f1043b4 | Stefan Hajnoczi | enum {
|
19 | 4f1043b4 | Stefan Hajnoczi | /*
|
20 | 4f1043b4 | Stefan Hajnoczi | * Size of data buffer for populating the image file. This should be large
|
21 | 4f1043b4 | Stefan Hajnoczi | * enough to process multiple clusters in a single call, so that populating
|
22 | 4f1043b4 | Stefan Hajnoczi | * contiguous regions of the image is efficient.
|
23 | 4f1043b4 | Stefan Hajnoczi | */
|
24 | 4f1043b4 | Stefan Hajnoczi | STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ |
25 | 4f1043b4 | Stefan Hajnoczi | }; |
26 | 4f1043b4 | Stefan Hajnoczi | |
27 | 5094a6c0 | Stefan Hajnoczi | #define SLICE_TIME 100000000ULL /* ns */ |
28 | 5094a6c0 | Stefan Hajnoczi | |
29 | 4f1043b4 | Stefan Hajnoczi | typedef struct StreamBlockJob { |
30 | 4f1043b4 | Stefan Hajnoczi | BlockJob common; |
31 | 5094a6c0 | Stefan Hajnoczi | RateLimit limit; |
32 | 4f1043b4 | Stefan Hajnoczi | BlockDriverState *base; |
33 | c8c3080f | Marcelo Tosatti | char backing_file_id[1024]; |
34 | 4f1043b4 | Stefan Hajnoczi | } StreamBlockJob; |
35 | 4f1043b4 | Stefan Hajnoczi | |
36 | 4f1043b4 | Stefan Hajnoczi | static int coroutine_fn stream_populate(BlockDriverState *bs, |
37 | 4f1043b4 | Stefan Hajnoczi | int64_t sector_num, int nb_sectors,
|
38 | 4f1043b4 | Stefan Hajnoczi | void *buf)
|
39 | 4f1043b4 | Stefan Hajnoczi | { |
40 | 4f1043b4 | Stefan Hajnoczi | struct iovec iov = {
|
41 | 4f1043b4 | Stefan Hajnoczi | .iov_base = buf, |
42 | 4f1043b4 | Stefan Hajnoczi | .iov_len = nb_sectors * BDRV_SECTOR_SIZE, |
43 | 4f1043b4 | Stefan Hajnoczi | }; |
44 | 4f1043b4 | Stefan Hajnoczi | QEMUIOVector qiov; |
45 | 4f1043b4 | Stefan Hajnoczi | |
46 | 4f1043b4 | Stefan Hajnoczi | qemu_iovec_init_external(&qiov, &iov, 1);
|
47 | 4f1043b4 | Stefan Hajnoczi | |
48 | 4f1043b4 | Stefan Hajnoczi | /* Copy-on-read the unallocated clusters */
|
49 | 4f1043b4 | Stefan Hajnoczi | return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov);
|
50 | 4f1043b4 | Stefan Hajnoczi | } |
51 | 4f1043b4 | Stefan Hajnoczi | |
52 | 5a67a104 | Marcelo Tosatti | static void close_unused_images(BlockDriverState *top, BlockDriverState *base, |
53 | 5a67a104 | Marcelo Tosatti | const char *base_id) |
54 | 5a67a104 | Marcelo Tosatti | { |
55 | 5a67a104 | Marcelo Tosatti | BlockDriverState *intermediate; |
56 | 5a67a104 | Marcelo Tosatti | intermediate = top->backing_hd; |
57 | 5a67a104 | Marcelo Tosatti | |
58 | 5a67a104 | Marcelo Tosatti | while (intermediate) {
|
59 | 5a67a104 | Marcelo Tosatti | BlockDriverState *unused; |
60 | 5a67a104 | Marcelo Tosatti | |
61 | 5a67a104 | Marcelo Tosatti | /* reached base */
|
62 | 5a67a104 | Marcelo Tosatti | if (intermediate == base) {
|
63 | 5a67a104 | Marcelo Tosatti | break;
|
64 | 5a67a104 | Marcelo Tosatti | } |
65 | 5a67a104 | Marcelo Tosatti | |
66 | 5a67a104 | Marcelo Tosatti | unused = intermediate; |
67 | 5a67a104 | Marcelo Tosatti | intermediate = intermediate->backing_hd; |
68 | 5a67a104 | Marcelo Tosatti | unused->backing_hd = NULL;
|
69 | 5a67a104 | Marcelo Tosatti | bdrv_delete(unused); |
70 | 5a67a104 | Marcelo Tosatti | } |
71 | 5a67a104 | Marcelo Tosatti | top->backing_hd = base; |
72 | 5a67a104 | Marcelo Tosatti | } |
73 | 5a67a104 | Marcelo Tosatti | |
74 | 4f1043b4 | Stefan Hajnoczi | static void coroutine_fn stream_run(void *opaque) |
75 | 4f1043b4 | Stefan Hajnoczi | { |
76 | 4f1043b4 | Stefan Hajnoczi | StreamBlockJob *s = opaque; |
77 | 4f1043b4 | Stefan Hajnoczi | BlockDriverState *bs = s->common.bs; |
78 | c8c3080f | Marcelo Tosatti | BlockDriverState *base = s->base; |
79 | 4f1043b4 | Stefan Hajnoczi | int64_t sector_num, end; |
80 | 4f1043b4 | Stefan Hajnoczi | int ret = 0; |
81 | 04120e3b | Anthony Liguori | int n = 0; |
82 | 4f1043b4 | Stefan Hajnoczi | void *buf;
|
83 | 4f1043b4 | Stefan Hajnoczi | |
84 | 4f1043b4 | Stefan Hajnoczi | s->common.len = bdrv_getlength(bs); |
85 | 4f1043b4 | Stefan Hajnoczi | if (s->common.len < 0) { |
86 | 4f1043b4 | Stefan Hajnoczi | block_job_complete(&s->common, s->common.len); |
87 | 4f1043b4 | Stefan Hajnoczi | return;
|
88 | 4f1043b4 | Stefan Hajnoczi | } |
89 | 4f1043b4 | Stefan Hajnoczi | |
90 | 4f1043b4 | Stefan Hajnoczi | end = s->common.len >> BDRV_SECTOR_BITS; |
91 | 4f1043b4 | Stefan Hajnoczi | buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); |
92 | 4f1043b4 | Stefan Hajnoczi | |
93 | 4f1043b4 | Stefan Hajnoczi | /* Turn on copy-on-read for the whole block device so that guest read
|
94 | 4f1043b4 | Stefan Hajnoczi | * requests help us make progress. Only do this when copying the entire
|
95 | 4f1043b4 | Stefan Hajnoczi | * backing chain since the copy-on-read operation does not take base into
|
96 | 4f1043b4 | Stefan Hajnoczi | * account.
|
97 | 4f1043b4 | Stefan Hajnoczi | */
|
98 | 4f1043b4 | Stefan Hajnoczi | if (!base) {
|
99 | 4f1043b4 | Stefan Hajnoczi | bdrv_enable_copy_on_read(bs); |
100 | 4f1043b4 | Stefan Hajnoczi | } |
101 | 4f1043b4 | Stefan Hajnoczi | |
102 | 4f1043b4 | Stefan Hajnoczi | for (sector_num = 0; sector_num < end; sector_num += n) { |
103 | 4513eafe | Paolo Bonzini | uint64_t delay_ns = 0;
|
104 | f9749f28 | Paolo Bonzini | bool copy;
|
105 | 4513eafe | Paolo Bonzini | |
106 | 4513eafe | Paolo Bonzini | wait:
|
107 | 4513eafe | Paolo Bonzini | /* Note that even when no rate limit is applied we need to yield
|
108 | 4513eafe | Paolo Bonzini | * with no pending I/O here so that qemu_aio_flush() returns.
|
109 | 4513eafe | Paolo Bonzini | */
|
110 | 4513eafe | Paolo Bonzini | block_job_sleep_ns(&s->common, rt_clock, delay_ns); |
111 | 4f1043b4 | Stefan Hajnoczi | if (block_job_is_cancelled(&s->common)) {
|
112 | 4f1043b4 | Stefan Hajnoczi | break;
|
113 | 4f1043b4 | Stefan Hajnoczi | } |
114 | 4f1043b4 | Stefan Hajnoczi | |
115 | f9749f28 | Paolo Bonzini | ret = bdrv_co_is_allocated(bs, sector_num, |
116 | f9749f28 | Paolo Bonzini | STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n); |
117 | f9749f28 | Paolo Bonzini | if (ret == 1) { |
118 | f9749f28 | Paolo Bonzini | /* Allocated in the top, no need to copy. */
|
119 | f9749f28 | Paolo Bonzini | copy = false;
|
120 | f9749f28 | Paolo Bonzini | } else {
|
121 | f9749f28 | Paolo Bonzini | /* Copy if allocated in the intermediate images. Limit to the
|
122 | f9749f28 | Paolo Bonzini | * known-unallocated area [sector_num, sector_num+n). */
|
123 | 188a7bbf | Paolo Bonzini | ret = bdrv_co_is_allocated_above(bs->backing_hd, base, |
124 | 188a7bbf | Paolo Bonzini | sector_num, n, &n); |
125 | 571cd9dc | Stefan Hajnoczi | |
126 | 571cd9dc | Stefan Hajnoczi | /* Finish early if end of backing file has been reached */
|
127 | 571cd9dc | Stefan Hajnoczi | if (ret == 0 && n == 0) { |
128 | 571cd9dc | Stefan Hajnoczi | n = end - sector_num; |
129 | 571cd9dc | Stefan Hajnoczi | } |
130 | 571cd9dc | Stefan Hajnoczi | |
131 | f9749f28 | Paolo Bonzini | copy = (ret == 1);
|
132 | f9749f28 | Paolo Bonzini | } |
133 | 4f1043b4 | Stefan Hajnoczi | trace_stream_one_iteration(s, sector_num, n, ret); |
134 | f9749f28 | Paolo Bonzini | if (ret >= 0 && copy) { |
135 | 5094a6c0 | Stefan Hajnoczi | if (s->common.speed) {
|
136 | 4513eafe | Paolo Bonzini | delay_ns = ratelimit_calculate_delay(&s->limit, n); |
137 | 5094a6c0 | Stefan Hajnoczi | if (delay_ns > 0) { |
138 | 4513eafe | Paolo Bonzini | goto wait;
|
139 | 5094a6c0 | Stefan Hajnoczi | } |
140 | 5094a6c0 | Stefan Hajnoczi | } |
141 | 4f1043b4 | Stefan Hajnoczi | ret = stream_populate(bs, sector_num, n, buf); |
142 | 4f1043b4 | Stefan Hajnoczi | } |
143 | 4f1043b4 | Stefan Hajnoczi | if (ret < 0) { |
144 | 4f1043b4 | Stefan Hajnoczi | break;
|
145 | 4f1043b4 | Stefan Hajnoczi | } |
146 | c8c3080f | Marcelo Tosatti | ret = 0;
|
147 | 4f1043b4 | Stefan Hajnoczi | |
148 | 4f1043b4 | Stefan Hajnoczi | /* Publish progress */
|
149 | 4f1043b4 | Stefan Hajnoczi | s->common.offset += n * BDRV_SECTOR_SIZE; |
150 | 4f1043b4 | Stefan Hajnoczi | } |
151 | 4f1043b4 | Stefan Hajnoczi | |
152 | 4f1043b4 | Stefan Hajnoczi | if (!base) {
|
153 | 4f1043b4 | Stefan Hajnoczi | bdrv_disable_copy_on_read(bs); |
154 | 4f1043b4 | Stefan Hajnoczi | } |
155 | 4f1043b4 | Stefan Hajnoczi | |
156 | 3e914655 | Paolo Bonzini | if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) { |
157 | f6133def | Paolo Bonzini | const char *base_id = NULL, *base_fmt = NULL; |
158 | c8c3080f | Marcelo Tosatti | if (base) {
|
159 | c8c3080f | Marcelo Tosatti | base_id = s->backing_file_id; |
160 | f6133def | Paolo Bonzini | if (base->drv) {
|
161 | f6133def | Paolo Bonzini | base_fmt = base->drv->format_name; |
162 | f6133def | Paolo Bonzini | } |
163 | c8c3080f | Marcelo Tosatti | } |
164 | f6133def | Paolo Bonzini | ret = bdrv_change_backing_file(bs, base_id, base_fmt); |
165 | 5a67a104 | Marcelo Tosatti | close_unused_images(bs, base, base_id); |
166 | 4f1043b4 | Stefan Hajnoczi | } |
167 | 4f1043b4 | Stefan Hajnoczi | |
168 | 4f1043b4 | Stefan Hajnoczi | qemu_vfree(buf); |
169 | 4f1043b4 | Stefan Hajnoczi | block_job_complete(&s->common, ret); |
170 | 4f1043b4 | Stefan Hajnoczi | } |
171 | 4f1043b4 | Stefan Hajnoczi | |
172 | 882ec7ce | Stefan Hajnoczi | static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) |
173 | 5094a6c0 | Stefan Hajnoczi | { |
174 | 5094a6c0 | Stefan Hajnoczi | StreamBlockJob *s = container_of(job, StreamBlockJob, common); |
175 | 5094a6c0 | Stefan Hajnoczi | |
176 | 882ec7ce | Stefan Hajnoczi | if (speed < 0) { |
177 | 882ec7ce | Stefan Hajnoczi | error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
178 | 9e6636c7 | Stefan Hajnoczi | return;
|
179 | 5094a6c0 | Stefan Hajnoczi | } |
180 | 6ef228fc | Paolo Bonzini | ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE, SLICE_TIME); |
181 | 5094a6c0 | Stefan Hajnoczi | } |
182 | 5094a6c0 | Stefan Hajnoczi | |
183 | 4f1043b4 | Stefan Hajnoczi | static BlockJobType stream_job_type = {
|
184 | 4f1043b4 | Stefan Hajnoczi | .instance_size = sizeof(StreamBlockJob),
|
185 | 4f1043b4 | Stefan Hajnoczi | .job_type = "stream",
|
186 | 5094a6c0 | Stefan Hajnoczi | .set_speed = stream_set_speed, |
187 | 4f1043b4 | Stefan Hajnoczi | }; |
188 | 4f1043b4 | Stefan Hajnoczi | |
189 | fd7f8c65 | Stefan Hajnoczi | void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
190 | c83c66c3 | Stefan Hajnoczi | const char *base_id, int64_t speed, |
191 | c83c66c3 | Stefan Hajnoczi | BlockDriverCompletionFunc *cb, |
192 | fd7f8c65 | Stefan Hajnoczi | void *opaque, Error **errp)
|
193 | 4f1043b4 | Stefan Hajnoczi | { |
194 | 4f1043b4 | Stefan Hajnoczi | StreamBlockJob *s; |
195 | 4f1043b4 | Stefan Hajnoczi | |
196 | c83c66c3 | Stefan Hajnoczi | s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp); |
197 | 4f1043b4 | Stefan Hajnoczi | if (!s) {
|
198 | fd7f8c65 | Stefan Hajnoczi | return;
|
199 | 4f1043b4 | Stefan Hajnoczi | } |
200 | 4f1043b4 | Stefan Hajnoczi | |
201 | 4f1043b4 | Stefan Hajnoczi | s->base = base; |
202 | c8c3080f | Marcelo Tosatti | if (base_id) {
|
203 | c8c3080f | Marcelo Tosatti | pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
|
204 | c8c3080f | Marcelo Tosatti | } |
205 | 4f1043b4 | Stefan Hajnoczi | |
206 | fa4478d5 | Paolo Bonzini | s->common.co = qemu_coroutine_create(stream_run); |
207 | fa4478d5 | Paolo Bonzini | trace_stream_start(bs, base, s, s->common.co, opaque); |
208 | fa4478d5 | Paolo Bonzini | qemu_coroutine_enter(s->common.co, s); |
209 | 4f1043b4 | Stefan Hajnoczi | } |