root / block / stream.c @ 469ef350
History | View | Annotate | Download (7.9 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 | 4f1043b4 | Stefan Hajnoczi | |
17 | 4f1043b4 | Stefan Hajnoczi | enum {
|
18 | 4f1043b4 | Stefan Hajnoczi | /*
|
19 | 4f1043b4 | Stefan Hajnoczi | * Size of data buffer for populating the image file. This should be large
|
20 | 4f1043b4 | Stefan Hajnoczi | * enough to process multiple clusters in a single call, so that populating
|
21 | 4f1043b4 | Stefan Hajnoczi | * contiguous regions of the image is efficient.
|
22 | 4f1043b4 | Stefan Hajnoczi | */
|
23 | 4f1043b4 | Stefan Hajnoczi | STREAM_BUFFER_SIZE = 512 * 1024, /* in bytes */ |
24 | 4f1043b4 | Stefan Hajnoczi | }; |
25 | 4f1043b4 | Stefan Hajnoczi | |
26 | 5094a6c0 | Stefan Hajnoczi | #define SLICE_TIME 100000000ULL /* ns */ |
27 | 5094a6c0 | Stefan Hajnoczi | |
28 | 5094a6c0 | Stefan Hajnoczi | typedef struct { |
29 | 5094a6c0 | Stefan Hajnoczi | int64_t next_slice_time; |
30 | 5094a6c0 | Stefan Hajnoczi | uint64_t slice_quota; |
31 | 5094a6c0 | Stefan Hajnoczi | uint64_t dispatched; |
32 | 5094a6c0 | Stefan Hajnoczi | } RateLimit; |
33 | 5094a6c0 | Stefan Hajnoczi | |
34 | 5094a6c0 | Stefan Hajnoczi | static int64_t ratelimit_calculate_delay(RateLimit *limit, uint64_t n)
|
35 | 5094a6c0 | Stefan Hajnoczi | { |
36 | 5094a6c0 | Stefan Hajnoczi | int64_t delay_ns = 0;
|
37 | 5094a6c0 | Stefan Hajnoczi | int64_t now = qemu_get_clock_ns(rt_clock); |
38 | 5094a6c0 | Stefan Hajnoczi | |
39 | 5094a6c0 | Stefan Hajnoczi | if (limit->next_slice_time < now) {
|
40 | 5094a6c0 | Stefan Hajnoczi | limit->next_slice_time = now + SLICE_TIME; |
41 | 5094a6c0 | Stefan Hajnoczi | limit->dispatched = 0;
|
42 | 5094a6c0 | Stefan Hajnoczi | } |
43 | 5094a6c0 | Stefan Hajnoczi | if (limit->dispatched + n > limit->slice_quota) {
|
44 | 5094a6c0 | Stefan Hajnoczi | delay_ns = limit->next_slice_time - now; |
45 | 5094a6c0 | Stefan Hajnoczi | } else {
|
46 | 5094a6c0 | Stefan Hajnoczi | limit->dispatched += n; |
47 | 5094a6c0 | Stefan Hajnoczi | } |
48 | 5094a6c0 | Stefan Hajnoczi | return delay_ns;
|
49 | 5094a6c0 | Stefan Hajnoczi | } |
50 | 5094a6c0 | Stefan Hajnoczi | |
51 | 5094a6c0 | Stefan Hajnoczi | static void ratelimit_set_speed(RateLimit *limit, uint64_t speed) |
52 | 5094a6c0 | Stefan Hajnoczi | { |
53 | 5094a6c0 | Stefan Hajnoczi | limit->slice_quota = speed / (1000000000ULL / SLICE_TIME);
|
54 | 5094a6c0 | Stefan Hajnoczi | } |
55 | 5094a6c0 | Stefan Hajnoczi | |
56 | 4f1043b4 | Stefan Hajnoczi | typedef struct StreamBlockJob { |
57 | 4f1043b4 | Stefan Hajnoczi | BlockJob common; |
58 | 5094a6c0 | Stefan Hajnoczi | RateLimit limit; |
59 | 4f1043b4 | Stefan Hajnoczi | BlockDriverState *base; |
60 | c8c3080f | Marcelo Tosatti | char backing_file_id[1024]; |
61 | 4f1043b4 | Stefan Hajnoczi | } StreamBlockJob; |
62 | 4f1043b4 | Stefan Hajnoczi | |
63 | 4f1043b4 | Stefan Hajnoczi | static int coroutine_fn stream_populate(BlockDriverState *bs, |
64 | 4f1043b4 | Stefan Hajnoczi | int64_t sector_num, int nb_sectors,
|
65 | 4f1043b4 | Stefan Hajnoczi | void *buf)
|
66 | 4f1043b4 | Stefan Hajnoczi | { |
67 | 4f1043b4 | Stefan Hajnoczi | struct iovec iov = {
|
68 | 4f1043b4 | Stefan Hajnoczi | .iov_base = buf, |
69 | 4f1043b4 | Stefan Hajnoczi | .iov_len = nb_sectors * BDRV_SECTOR_SIZE, |
70 | 4f1043b4 | Stefan Hajnoczi | }; |
71 | 4f1043b4 | Stefan Hajnoczi | QEMUIOVector qiov; |
72 | 4f1043b4 | Stefan Hajnoczi | |
73 | 4f1043b4 | Stefan Hajnoczi | qemu_iovec_init_external(&qiov, &iov, 1);
|
74 | 4f1043b4 | Stefan Hajnoczi | |
75 | 4f1043b4 | Stefan Hajnoczi | /* Copy-on-read the unallocated clusters */
|
76 | 4f1043b4 | Stefan Hajnoczi | return bdrv_co_copy_on_readv(bs, sector_num, nb_sectors, &qiov);
|
77 | 4f1043b4 | Stefan Hajnoczi | } |
78 | 4f1043b4 | Stefan Hajnoczi | |
79 | 5a67a104 | Marcelo Tosatti | static void close_unused_images(BlockDriverState *top, BlockDriverState *base, |
80 | 5a67a104 | Marcelo Tosatti | const char *base_id) |
81 | 5a67a104 | Marcelo Tosatti | { |
82 | 5a67a104 | Marcelo Tosatti | BlockDriverState *intermediate; |
83 | 5a67a104 | Marcelo Tosatti | intermediate = top->backing_hd; |
84 | 5a67a104 | Marcelo Tosatti | |
85 | 5a67a104 | Marcelo Tosatti | while (intermediate) {
|
86 | 5a67a104 | Marcelo Tosatti | BlockDriverState *unused; |
87 | 5a67a104 | Marcelo Tosatti | |
88 | 5a67a104 | Marcelo Tosatti | /* reached base */
|
89 | 5a67a104 | Marcelo Tosatti | if (intermediate == base) {
|
90 | 5a67a104 | Marcelo Tosatti | break;
|
91 | 5a67a104 | Marcelo Tosatti | } |
92 | 5a67a104 | Marcelo Tosatti | |
93 | 5a67a104 | Marcelo Tosatti | unused = intermediate; |
94 | 5a67a104 | Marcelo Tosatti | intermediate = intermediate->backing_hd; |
95 | 5a67a104 | Marcelo Tosatti | unused->backing_hd = NULL;
|
96 | 5a67a104 | Marcelo Tosatti | bdrv_delete(unused); |
97 | 5a67a104 | Marcelo Tosatti | } |
98 | 5a67a104 | Marcelo Tosatti | top->backing_hd = base; |
99 | 5a67a104 | Marcelo Tosatti | } |
100 | 5a67a104 | Marcelo Tosatti | |
101 | c8c3080f | Marcelo Tosatti | /*
|
102 | c8c3080f | Marcelo Tosatti | * Given an image chain: [BASE] -> [INTER1] -> [INTER2] -> [TOP]
|
103 | c8c3080f | Marcelo Tosatti | *
|
104 | c8c3080f | Marcelo Tosatti | * Return true if the given sector is allocated in top.
|
105 | c8c3080f | Marcelo Tosatti | * Return false if the given sector is allocated in intermediate images.
|
106 | c8c3080f | Marcelo Tosatti | * Return true otherwise.
|
107 | c8c3080f | Marcelo Tosatti | *
|
108 | c8c3080f | Marcelo Tosatti | * 'pnum' is set to the number of sectors (including and immediately following
|
109 | c8c3080f | Marcelo Tosatti | * the specified sector) that are known to be in the same
|
110 | c8c3080f | Marcelo Tosatti | * allocated/unallocated state.
|
111 | c8c3080f | Marcelo Tosatti | *
|
112 | c8c3080f | Marcelo Tosatti | */
|
113 | c8c3080f | Marcelo Tosatti | static int coroutine_fn is_allocated_base(BlockDriverState *top, |
114 | c8c3080f | Marcelo Tosatti | BlockDriverState *base, |
115 | c8c3080f | Marcelo Tosatti | int64_t sector_num, |
116 | c8c3080f | Marcelo Tosatti | int nb_sectors, int *pnum) |
117 | c8c3080f | Marcelo Tosatti | { |
118 | c8c3080f | Marcelo Tosatti | BlockDriverState *intermediate; |
119 | c8c3080f | Marcelo Tosatti | int ret, n;
|
120 | c8c3080f | Marcelo Tosatti | |
121 | c8c3080f | Marcelo Tosatti | ret = bdrv_co_is_allocated(top, sector_num, nb_sectors, &n); |
122 | c8c3080f | Marcelo Tosatti | if (ret) {
|
123 | c8c3080f | Marcelo Tosatti | *pnum = n; |
124 | c8c3080f | Marcelo Tosatti | return ret;
|
125 | c8c3080f | Marcelo Tosatti | } |
126 | c8c3080f | Marcelo Tosatti | |
127 | c8c3080f | Marcelo Tosatti | /*
|
128 | c8c3080f | Marcelo Tosatti | * Is the unallocated chunk [sector_num, n] also
|
129 | c8c3080f | Marcelo Tosatti | * unallocated between base and top?
|
130 | c8c3080f | Marcelo Tosatti | */
|
131 | c8c3080f | Marcelo Tosatti | intermediate = top->backing_hd; |
132 | c8c3080f | Marcelo Tosatti | |
133 | c8c3080f | Marcelo Tosatti | while (intermediate) {
|
134 | c8c3080f | Marcelo Tosatti | int pnum_inter;
|
135 | c8c3080f | Marcelo Tosatti | |
136 | c8c3080f | Marcelo Tosatti | /* reached base */
|
137 | c8c3080f | Marcelo Tosatti | if (intermediate == base) {
|
138 | c8c3080f | Marcelo Tosatti | *pnum = n; |
139 | c8c3080f | Marcelo Tosatti | return 1; |
140 | c8c3080f | Marcelo Tosatti | } |
141 | c8c3080f | Marcelo Tosatti | ret = bdrv_co_is_allocated(intermediate, sector_num, nb_sectors, |
142 | c8c3080f | Marcelo Tosatti | &pnum_inter); |
143 | c8c3080f | Marcelo Tosatti | if (ret < 0) { |
144 | c8c3080f | Marcelo Tosatti | return ret;
|
145 | c8c3080f | Marcelo Tosatti | } else if (ret) { |
146 | c8c3080f | Marcelo Tosatti | *pnum = pnum_inter; |
147 | c8c3080f | Marcelo Tosatti | return 0; |
148 | c8c3080f | Marcelo Tosatti | } |
149 | c8c3080f | Marcelo Tosatti | |
150 | c8c3080f | Marcelo Tosatti | /*
|
151 | c8c3080f | Marcelo Tosatti | * [sector_num, nb_sectors] is unallocated on top but intermediate
|
152 | c8c3080f | Marcelo Tosatti | * might have
|
153 | c8c3080f | Marcelo Tosatti | *
|
154 | c8c3080f | Marcelo Tosatti | * [sector_num+x, nr_sectors] allocated.
|
155 | c8c3080f | Marcelo Tosatti | */
|
156 | c8c3080f | Marcelo Tosatti | if (n > pnum_inter) {
|
157 | c8c3080f | Marcelo Tosatti | n = pnum_inter; |
158 | c8c3080f | Marcelo Tosatti | } |
159 | c8c3080f | Marcelo Tosatti | |
160 | c8c3080f | Marcelo Tosatti | intermediate = intermediate->backing_hd; |
161 | c8c3080f | Marcelo Tosatti | } |
162 | c8c3080f | Marcelo Tosatti | |
163 | c8c3080f | Marcelo Tosatti | return 1; |
164 | c8c3080f | Marcelo Tosatti | } |
165 | c8c3080f | Marcelo Tosatti | |
166 | 4f1043b4 | Stefan Hajnoczi | static void coroutine_fn stream_run(void *opaque) |
167 | 4f1043b4 | Stefan Hajnoczi | { |
168 | 4f1043b4 | Stefan Hajnoczi | StreamBlockJob *s = opaque; |
169 | 4f1043b4 | Stefan Hajnoczi | BlockDriverState *bs = s->common.bs; |
170 | c8c3080f | Marcelo Tosatti | BlockDriverState *base = s->base; |
171 | 4f1043b4 | Stefan Hajnoczi | int64_t sector_num, end; |
172 | 4f1043b4 | Stefan Hajnoczi | int ret = 0; |
173 | 4f1043b4 | Stefan Hajnoczi | int n;
|
174 | 4f1043b4 | Stefan Hajnoczi | void *buf;
|
175 | 4f1043b4 | Stefan Hajnoczi | |
176 | 4f1043b4 | Stefan Hajnoczi | s->common.len = bdrv_getlength(bs); |
177 | 4f1043b4 | Stefan Hajnoczi | if (s->common.len < 0) { |
178 | 4f1043b4 | Stefan Hajnoczi | block_job_complete(&s->common, s->common.len); |
179 | 4f1043b4 | Stefan Hajnoczi | return;
|
180 | 4f1043b4 | Stefan Hajnoczi | } |
181 | 4f1043b4 | Stefan Hajnoczi | |
182 | 4f1043b4 | Stefan Hajnoczi | end = s->common.len >> BDRV_SECTOR_BITS; |
183 | 4f1043b4 | Stefan Hajnoczi | buf = qemu_blockalign(bs, STREAM_BUFFER_SIZE); |
184 | 4f1043b4 | Stefan Hajnoczi | |
185 | 4f1043b4 | Stefan Hajnoczi | /* Turn on copy-on-read for the whole block device so that guest read
|
186 | 4f1043b4 | Stefan Hajnoczi | * requests help us make progress. Only do this when copying the entire
|
187 | 4f1043b4 | Stefan Hajnoczi | * backing chain since the copy-on-read operation does not take base into
|
188 | 4f1043b4 | Stefan Hajnoczi | * account.
|
189 | 4f1043b4 | Stefan Hajnoczi | */
|
190 | 4f1043b4 | Stefan Hajnoczi | if (!base) {
|
191 | 4f1043b4 | Stefan Hajnoczi | bdrv_enable_copy_on_read(bs); |
192 | 4f1043b4 | Stefan Hajnoczi | } |
193 | 4f1043b4 | Stefan Hajnoczi | |
194 | 4f1043b4 | Stefan Hajnoczi | for (sector_num = 0; sector_num < end; sector_num += n) { |
195 | 5094a6c0 | Stefan Hajnoczi | retry:
|
196 | 4f1043b4 | Stefan Hajnoczi | if (block_job_is_cancelled(&s->common)) {
|
197 | 4f1043b4 | Stefan Hajnoczi | break;
|
198 | 4f1043b4 | Stefan Hajnoczi | } |
199 | 4f1043b4 | Stefan Hajnoczi | |
200 | 3e914655 | Paolo Bonzini | s->common.busy = true;
|
201 | c8c3080f | Marcelo Tosatti | if (base) {
|
202 | c8c3080f | Marcelo Tosatti | ret = is_allocated_base(bs, base, sector_num, |
203 | c8c3080f | Marcelo Tosatti | STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, &n); |
204 | c8c3080f | Marcelo Tosatti | } else {
|
205 | c8c3080f | Marcelo Tosatti | ret = bdrv_co_is_allocated(bs, sector_num, |
206 | c8c3080f | Marcelo Tosatti | STREAM_BUFFER_SIZE / BDRV_SECTOR_SIZE, |
207 | c8c3080f | Marcelo Tosatti | &n); |
208 | c8c3080f | Marcelo Tosatti | } |
209 | 4f1043b4 | Stefan Hajnoczi | trace_stream_one_iteration(s, sector_num, n, ret); |
210 | 4f1043b4 | Stefan Hajnoczi | if (ret == 0) { |
211 | 5094a6c0 | Stefan Hajnoczi | if (s->common.speed) {
|
212 | 5094a6c0 | Stefan Hajnoczi | uint64_t delay_ns = ratelimit_calculate_delay(&s->limit, n); |
213 | 5094a6c0 | Stefan Hajnoczi | if (delay_ns > 0) { |
214 | 3e914655 | Paolo Bonzini | s->common.busy = false;
|
215 | 5094a6c0 | Stefan Hajnoczi | co_sleep_ns(rt_clock, delay_ns); |
216 | 5094a6c0 | Stefan Hajnoczi | |
217 | 5094a6c0 | Stefan Hajnoczi | /* Recheck cancellation and that sectors are unallocated */
|
218 | 5094a6c0 | Stefan Hajnoczi | goto retry;
|
219 | 5094a6c0 | Stefan Hajnoczi | } |
220 | 5094a6c0 | Stefan Hajnoczi | } |
221 | 4f1043b4 | Stefan Hajnoczi | ret = stream_populate(bs, sector_num, n, buf); |
222 | 4f1043b4 | Stefan Hajnoczi | } |
223 | 4f1043b4 | Stefan Hajnoczi | if (ret < 0) { |
224 | 4f1043b4 | Stefan Hajnoczi | break;
|
225 | 4f1043b4 | Stefan Hajnoczi | } |
226 | c8c3080f | Marcelo Tosatti | ret = 0;
|
227 | 4f1043b4 | Stefan Hajnoczi | |
228 | 4f1043b4 | Stefan Hajnoczi | /* Publish progress */
|
229 | 4f1043b4 | Stefan Hajnoczi | s->common.offset += n * BDRV_SECTOR_SIZE; |
230 | 5094a6c0 | Stefan Hajnoczi | |
231 | 5094a6c0 | Stefan Hajnoczi | /* Note that even when no rate limit is applied we need to yield
|
232 | 5094a6c0 | Stefan Hajnoczi | * with no pending I/O here so that qemu_aio_flush() returns.
|
233 | 5094a6c0 | Stefan Hajnoczi | */
|
234 | 3e914655 | Paolo Bonzini | s->common.busy = false;
|
235 | 5094a6c0 | Stefan Hajnoczi | co_sleep_ns(rt_clock, 0);
|
236 | 4f1043b4 | Stefan Hajnoczi | } |
237 | 4f1043b4 | Stefan Hajnoczi | |
238 | 4f1043b4 | Stefan Hajnoczi | if (!base) {
|
239 | 4f1043b4 | Stefan Hajnoczi | bdrv_disable_copy_on_read(bs); |
240 | 4f1043b4 | Stefan Hajnoczi | } |
241 | 4f1043b4 | Stefan Hajnoczi | |
242 | 3e914655 | Paolo Bonzini | if (!block_job_is_cancelled(&s->common) && sector_num == end && ret == 0) { |
243 | c8c3080f | Marcelo Tosatti | const char *base_id = NULL; |
244 | c8c3080f | Marcelo Tosatti | if (base) {
|
245 | c8c3080f | Marcelo Tosatti | base_id = s->backing_file_id; |
246 | c8c3080f | Marcelo Tosatti | } |
247 | c8c3080f | Marcelo Tosatti | ret = bdrv_change_backing_file(bs, base_id, NULL);
|
248 | 5a67a104 | Marcelo Tosatti | close_unused_images(bs, base, base_id); |
249 | 4f1043b4 | Stefan Hajnoczi | } |
250 | 4f1043b4 | Stefan Hajnoczi | |
251 | 4f1043b4 | Stefan Hajnoczi | qemu_vfree(buf); |
252 | 4f1043b4 | Stefan Hajnoczi | block_job_complete(&s->common, ret); |
253 | 4f1043b4 | Stefan Hajnoczi | } |
254 | 4f1043b4 | Stefan Hajnoczi | |
255 | 882ec7ce | Stefan Hajnoczi | static void stream_set_speed(BlockJob *job, int64_t speed, Error **errp) |
256 | 5094a6c0 | Stefan Hajnoczi | { |
257 | 5094a6c0 | Stefan Hajnoczi | StreamBlockJob *s = container_of(job, StreamBlockJob, common); |
258 | 5094a6c0 | Stefan Hajnoczi | |
259 | 882ec7ce | Stefan Hajnoczi | if (speed < 0) { |
260 | 882ec7ce | Stefan Hajnoczi | error_set(errp, QERR_INVALID_PARAMETER, "speed");
|
261 | 9e6636c7 | Stefan Hajnoczi | return;
|
262 | 5094a6c0 | Stefan Hajnoczi | } |
263 | 882ec7ce | Stefan Hajnoczi | ratelimit_set_speed(&s->limit, speed / BDRV_SECTOR_SIZE); |
264 | 5094a6c0 | Stefan Hajnoczi | } |
265 | 5094a6c0 | Stefan Hajnoczi | |
266 | 4f1043b4 | Stefan Hajnoczi | static BlockJobType stream_job_type = {
|
267 | 4f1043b4 | Stefan Hajnoczi | .instance_size = sizeof(StreamBlockJob),
|
268 | 4f1043b4 | Stefan Hajnoczi | .job_type = "stream",
|
269 | 5094a6c0 | Stefan Hajnoczi | .set_speed = stream_set_speed, |
270 | 4f1043b4 | Stefan Hajnoczi | }; |
271 | 4f1043b4 | Stefan Hajnoczi | |
272 | fd7f8c65 | Stefan Hajnoczi | void stream_start(BlockDriverState *bs, BlockDriverState *base,
|
273 | c83c66c3 | Stefan Hajnoczi | const char *base_id, int64_t speed, |
274 | c83c66c3 | Stefan Hajnoczi | BlockDriverCompletionFunc *cb, |
275 | fd7f8c65 | Stefan Hajnoczi | void *opaque, Error **errp)
|
276 | 4f1043b4 | Stefan Hajnoczi | { |
277 | 4f1043b4 | Stefan Hajnoczi | StreamBlockJob *s; |
278 | 4f1043b4 | Stefan Hajnoczi | Coroutine *co; |
279 | 4f1043b4 | Stefan Hajnoczi | |
280 | c83c66c3 | Stefan Hajnoczi | s = block_job_create(&stream_job_type, bs, speed, cb, opaque, errp); |
281 | 4f1043b4 | Stefan Hajnoczi | if (!s) {
|
282 | fd7f8c65 | Stefan Hajnoczi | return;
|
283 | 4f1043b4 | Stefan Hajnoczi | } |
284 | 4f1043b4 | Stefan Hajnoczi | |
285 | 4f1043b4 | Stefan Hajnoczi | s->base = base; |
286 | c8c3080f | Marcelo Tosatti | if (base_id) {
|
287 | c8c3080f | Marcelo Tosatti | pstrcpy(s->backing_file_id, sizeof(s->backing_file_id), base_id);
|
288 | c8c3080f | Marcelo Tosatti | } |
289 | 4f1043b4 | Stefan Hajnoczi | |
290 | 4f1043b4 | Stefan Hajnoczi | co = qemu_coroutine_create(stream_run); |
291 | 4f1043b4 | Stefan Hajnoczi | trace_stream_start(bs, base, s, co, opaque); |
292 | 4f1043b4 | Stefan Hajnoczi | qemu_coroutine_enter(co, s); |
293 | 4f1043b4 | Stefan Hajnoczi | } |