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