]>
Commit | Line | Data |
---|---|---|
977184db DDAG |
1 | /* |
2 | * QEMU System Emulator | |
3 | * | |
4 | * Copyright (c) 2003-2008 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include "qemu-common.h" | |
25 | #include "qemu/iov.h" | |
26 | #include "qemu/sockets.h" | |
27 | #include "block/coroutine.h" | |
28 | #include "migration/migration.h" | |
29 | #include "migration/qemu-file.h" | |
30 | #include "migration/qemu-file-internal.h" | |
31 | #include "trace.h" | |
32 | ||
33 | #define QSB_CHUNK_SIZE (1 << 10) | |
34 | #define QSB_MAX_CHUNK_SIZE (16 * QSB_CHUNK_SIZE) | |
35 | ||
36 | /** | |
37 | * Create a QEMUSizedBuffer | |
38 | * This type of buffer uses scatter-gather lists internally and | |
39 | * can grow to any size. Any data array in the scatter-gather list | |
40 | * can hold different amount of bytes. | |
41 | * | |
42 | * @buffer: Optional buffer to copy into the QSB | |
43 | * @len: size of initial buffer; if @buffer is given, buffer must | |
44 | * hold at least len bytes | |
45 | * | |
46 | * Returns a pointer to a QEMUSizedBuffer or NULL on allocation failure | |
47 | */ | |
48 | QEMUSizedBuffer *qsb_create(const uint8_t *buffer, size_t len) | |
49 | { | |
50 | QEMUSizedBuffer *qsb; | |
51 | size_t alloc_len, num_chunks, i, to_copy; | |
52 | size_t chunk_size = (len > QSB_MAX_CHUNK_SIZE) | |
53 | ? QSB_MAX_CHUNK_SIZE | |
54 | : QSB_CHUNK_SIZE; | |
55 | ||
56 | num_chunks = DIV_ROUND_UP(len ? len : QSB_CHUNK_SIZE, chunk_size); | |
57 | alloc_len = num_chunks * chunk_size; | |
58 | ||
59 | qsb = g_try_new0(QEMUSizedBuffer, 1); | |
60 | if (!qsb) { | |
61 | return NULL; | |
62 | } | |
63 | ||
64 | qsb->iov = g_try_new0(struct iovec, num_chunks); | |
65 | if (!qsb->iov) { | |
66 | g_free(qsb); | |
67 | return NULL; | |
68 | } | |
69 | ||
70 | qsb->n_iov = num_chunks; | |
71 | ||
72 | for (i = 0; i < num_chunks; i++) { | |
73 | qsb->iov[i].iov_base = g_try_malloc0(chunk_size); | |
74 | if (!qsb->iov[i].iov_base) { | |
75 | /* qsb_free is safe since g_free can cope with NULL */ | |
76 | qsb_free(qsb); | |
77 | return NULL; | |
78 | } | |
79 | ||
80 | qsb->iov[i].iov_len = chunk_size; | |
81 | if (buffer) { | |
82 | to_copy = (len - qsb->used) > chunk_size | |
83 | ? chunk_size : (len - qsb->used); | |
84 | memcpy(qsb->iov[i].iov_base, &buffer[qsb->used], to_copy); | |
85 | qsb->used += to_copy; | |
86 | } | |
87 | } | |
88 | ||
89 | qsb->size = alloc_len; | |
90 | ||
91 | return qsb; | |
92 | } | |
93 | ||
94 | /** | |
95 | * Free the QEMUSizedBuffer | |
96 | * | |
97 | * @qsb: The QEMUSizedBuffer to free | |
98 | */ | |
99 | void qsb_free(QEMUSizedBuffer *qsb) | |
100 | { | |
101 | size_t i; | |
102 | ||
103 | if (!qsb) { | |
104 | return; | |
105 | } | |
106 | ||
107 | for (i = 0; i < qsb->n_iov; i++) { | |
108 | g_free(qsb->iov[i].iov_base); | |
109 | } | |
110 | g_free(qsb->iov); | |
111 | g_free(qsb); | |
112 | } | |
113 | ||
114 | /** | |
115 | * Get the number of used bytes in the QEMUSizedBuffer | |
116 | * | |
117 | * @qsb: A QEMUSizedBuffer | |
118 | * | |
119 | * Returns the number of bytes currently used in this buffer | |
120 | */ | |
121 | size_t qsb_get_length(const QEMUSizedBuffer *qsb) | |
122 | { | |
123 | return qsb->used; | |
124 | } | |
125 | ||
126 | /** | |
127 | * Set the length of the buffer; the primary usage of this | |
128 | * function is to truncate the number of used bytes in the buffer. | |
129 | * The size will not be extended beyond the current number of | |
130 | * allocated bytes in the QEMUSizedBuffer. | |
131 | * | |
132 | * @qsb: A QEMUSizedBuffer | |
133 | * @new_len: The new length of bytes in the buffer | |
134 | * | |
135 | * Returns the number of bytes the buffer was truncated or extended | |
136 | * to. | |
137 | */ | |
138 | size_t qsb_set_length(QEMUSizedBuffer *qsb, size_t new_len) | |
139 | { | |
140 | if (new_len <= qsb->size) { | |
141 | qsb->used = new_len; | |
142 | } else { | |
143 | qsb->used = qsb->size; | |
144 | } | |
145 | return qsb->used; | |
146 | } | |
147 | ||
148 | /** | |
149 | * Get the iovec that holds the data for a given position @pos. | |
150 | * | |
151 | * @qsb: A QEMUSizedBuffer | |
152 | * @pos: The index of a byte in the buffer | |
153 | * @d_off: Pointer to an offset that this function will indicate | |
154 | * at what position within the returned iovec the byte | |
155 | * is to be found | |
156 | * | |
157 | * Returns the index of the iovec that holds the byte at the given | |
158 | * index @pos in the byte stream; a negative number if the iovec | |
159 | * for the given position @pos does not exist. | |
160 | */ | |
161 | static ssize_t qsb_get_iovec(const QEMUSizedBuffer *qsb, | |
162 | off_t pos, off_t *d_off) | |
163 | { | |
164 | ssize_t i; | |
165 | off_t curr = 0; | |
166 | ||
167 | if (pos > qsb->used) { | |
168 | return -1; | |
169 | } | |
170 | ||
171 | for (i = 0; i < qsb->n_iov; i++) { | |
172 | if (curr + qsb->iov[i].iov_len > pos) { | |
173 | *d_off = pos - curr; | |
174 | return i; | |
175 | } | |
176 | curr += qsb->iov[i].iov_len; | |
177 | } | |
178 | return -1; | |
179 | } | |
180 | ||
181 | /* | |
182 | * Convert the QEMUSizedBuffer into a flat buffer. | |
183 | * | |
184 | * Note: If at all possible, try to avoid this function since it | |
185 | * may unnecessarily copy memory around. | |
186 | * | |
187 | * @qsb: pointer to QEMUSizedBuffer | |
188 | * @start: offset to start at | |
189 | * @count: number of bytes to copy | |
190 | * @buf: a pointer to a buffer to write into (at least @count bytes) | |
191 | * | |
192 | * Returns the number of bytes copied into the output buffer | |
193 | */ | |
194 | ssize_t qsb_get_buffer(const QEMUSizedBuffer *qsb, off_t start, | |
195 | size_t count, uint8_t *buffer) | |
196 | { | |
197 | const struct iovec *iov; | |
198 | size_t to_copy, all_copy; | |
199 | ssize_t index; | |
200 | off_t s_off; | |
201 | off_t d_off = 0; | |
202 | char *s; | |
203 | ||
204 | if (start > qsb->used) { | |
205 | return 0; | |
206 | } | |
207 | ||
208 | all_copy = qsb->used - start; | |
209 | if (all_copy > count) { | |
210 | all_copy = count; | |
211 | } else { | |
212 | count = all_copy; | |
213 | } | |
214 | ||
215 | index = qsb_get_iovec(qsb, start, &s_off); | |
216 | if (index < 0) { | |
217 | return 0; | |
218 | } | |
219 | ||
220 | while (all_copy > 0) { | |
221 | iov = &qsb->iov[index]; | |
222 | ||
223 | s = iov->iov_base; | |
224 | ||
225 | to_copy = iov->iov_len - s_off; | |
226 | if (to_copy > all_copy) { | |
227 | to_copy = all_copy; | |
228 | } | |
229 | memcpy(&buffer[d_off], &s[s_off], to_copy); | |
230 | ||
231 | d_off += to_copy; | |
232 | all_copy -= to_copy; | |
233 | ||
234 | s_off = 0; | |
235 | index++; | |
236 | } | |
237 | ||
238 | return count; | |
239 | } | |
240 | ||
241 | /** | |
242 | * Grow the QEMUSizedBuffer to the given size and allocate | |
243 | * memory for it. | |
244 | * | |
245 | * @qsb: A QEMUSizedBuffer | |
246 | * @new_size: The new size of the buffer | |
247 | * | |
248 | * Return: | |
249 | * a negative error code in case of memory allocation failure | |
250 | * or | |
251 | * the new size of the buffer. The returned size may be greater or equal | |
252 | * to @new_size. | |
253 | */ | |
254 | static ssize_t qsb_grow(QEMUSizedBuffer *qsb, size_t new_size) | |
255 | { | |
256 | size_t needed_chunks, i; | |
257 | ||
258 | if (qsb->size < new_size) { | |
259 | struct iovec *new_iov; | |
260 | size_t size_diff = new_size - qsb->size; | |
261 | size_t chunk_size = (size_diff > QSB_MAX_CHUNK_SIZE) | |
262 | ? QSB_MAX_CHUNK_SIZE : QSB_CHUNK_SIZE; | |
263 | ||
264 | needed_chunks = DIV_ROUND_UP(size_diff, chunk_size); | |
265 | ||
266 | new_iov = g_try_new(struct iovec, qsb->n_iov + needed_chunks); | |
267 | if (new_iov == NULL) { | |
268 | return -ENOMEM; | |
269 | } | |
270 | ||
271 | /* Allocate new chunks as needed into new_iov */ | |
272 | for (i = qsb->n_iov; i < qsb->n_iov + needed_chunks; i++) { | |
273 | new_iov[i].iov_base = g_try_malloc0(chunk_size); | |
274 | new_iov[i].iov_len = chunk_size; | |
275 | if (!new_iov[i].iov_base) { | |
276 | size_t j; | |
277 | ||
278 | /* Free previously allocated new chunks */ | |
279 | for (j = qsb->n_iov; j < i; j++) { | |
280 | g_free(new_iov[j].iov_base); | |
281 | } | |
282 | g_free(new_iov); | |
283 | ||
284 | return -ENOMEM; | |
285 | } | |
286 | } | |
287 | ||
288 | /* | |
289 | * Now we can't get any allocation errors, copy over to new iov | |
290 | * and switch. | |
291 | */ | |
292 | for (i = 0; i < qsb->n_iov; i++) { | |
293 | new_iov[i] = qsb->iov[i]; | |
294 | } | |
295 | ||
296 | qsb->n_iov += needed_chunks; | |
297 | g_free(qsb->iov); | |
298 | qsb->iov = new_iov; | |
299 | qsb->size += (needed_chunks * chunk_size); | |
300 | } | |
301 | ||
302 | return qsb->size; | |
303 | } | |
304 | ||
305 | /** | |
306 | * Write into the QEMUSizedBuffer at a given position and a given | |
307 | * number of bytes. This function will automatically grow the | |
308 | * QEMUSizedBuffer. | |
309 | * | |
310 | * @qsb: A QEMUSizedBuffer | |
311 | * @source: A byte array to copy data from | |
312 | * @pos: The position within the @qsb to write data to | |
313 | * @size: The number of bytes to copy into the @qsb | |
314 | * | |
315 | * Returns @size or a negative error code in case of memory allocation failure, | |
316 | * or with an invalid 'pos' | |
317 | */ | |
318 | ssize_t qsb_write_at(QEMUSizedBuffer *qsb, const uint8_t *source, | |
319 | off_t pos, size_t count) | |
320 | { | |
321 | ssize_t rc = qsb_grow(qsb, pos + count); | |
322 | size_t to_copy; | |
323 | size_t all_copy = count; | |
324 | const struct iovec *iov; | |
325 | ssize_t index; | |
326 | char *dest; | |
327 | off_t d_off, s_off = 0; | |
328 | ||
329 | if (rc < 0) { | |
330 | return rc; | |
331 | } | |
332 | ||
333 | if (pos + count > qsb->used) { | |
334 | qsb->used = pos + count; | |
335 | } | |
336 | ||
337 | index = qsb_get_iovec(qsb, pos, &d_off); | |
338 | if (index < 0) { | |
339 | return -EINVAL; | |
340 | } | |
341 | ||
342 | while (all_copy > 0) { | |
343 | iov = &qsb->iov[index]; | |
344 | ||
345 | dest = iov->iov_base; | |
346 | ||
347 | to_copy = iov->iov_len - d_off; | |
348 | if (to_copy > all_copy) { | |
349 | to_copy = all_copy; | |
350 | } | |
351 | ||
352 | memcpy(&dest[d_off], &source[s_off], to_copy); | |
353 | ||
354 | s_off += to_copy; | |
355 | all_copy -= to_copy; | |
356 | ||
357 | d_off = 0; | |
358 | index++; | |
359 | } | |
360 | ||
361 | return count; | |
362 | } | |
363 | ||
364 | /** | |
365 | * Create a deep copy of the given QEMUSizedBuffer. | |
366 | * | |
367 | * @qsb: A QEMUSizedBuffer | |
368 | * | |
369 | * Returns a clone of @qsb or NULL on allocation failure | |
370 | */ | |
371 | QEMUSizedBuffer *qsb_clone(const QEMUSizedBuffer *qsb) | |
372 | { | |
373 | QEMUSizedBuffer *out = qsb_create(NULL, qsb_get_length(qsb)); | |
374 | size_t i; | |
375 | ssize_t res; | |
376 | off_t pos = 0; | |
377 | ||
378 | if (!out) { | |
379 | return NULL; | |
380 | } | |
381 | ||
382 | for (i = 0; i < qsb->n_iov; i++) { | |
383 | res = qsb_write_at(out, qsb->iov[i].iov_base, | |
384 | pos, qsb->iov[i].iov_len); | |
385 | if (res < 0) { | |
386 | qsb_free(out); | |
387 | return NULL; | |
388 | } | |
389 | pos += res; | |
390 | } | |
391 | ||
392 | return out; | |
393 | } | |
394 | ||
395 | typedef struct QEMUBuffer { | |
396 | QEMUSizedBuffer *qsb; | |
397 | QEMUFile *file; | |
398 | } QEMUBuffer; | |
399 | ||
400 | static int buf_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) | |
401 | { | |
402 | QEMUBuffer *s = opaque; | |
403 | ssize_t len = qsb_get_length(s->qsb) - pos; | |
404 | ||
405 | if (len <= 0) { | |
406 | return 0; | |
407 | } | |
408 | ||
409 | if (len > size) { | |
410 | len = size; | |
411 | } | |
412 | return qsb_get_buffer(s->qsb, pos, len, buf); | |
413 | } | |
414 | ||
415 | static int buf_put_buffer(void *opaque, const uint8_t *buf, | |
416 | int64_t pos, int size) | |
417 | { | |
418 | QEMUBuffer *s = opaque; | |
419 | ||
420 | return qsb_write_at(s->qsb, buf, pos, size); | |
421 | } | |
422 | ||
423 | static int buf_close(void *opaque) | |
424 | { | |
425 | QEMUBuffer *s = opaque; | |
426 | ||
427 | qsb_free(s->qsb); | |
428 | ||
429 | g_free(s); | |
430 | ||
431 | return 0; | |
432 | } | |
433 | ||
434 | const QEMUSizedBuffer *qemu_buf_get(QEMUFile *f) | |
435 | { | |
436 | QEMUBuffer *p; | |
437 | ||
438 | qemu_fflush(f); | |
439 | ||
440 | p = f->opaque; | |
441 | ||
442 | return p->qsb; | |
443 | } | |
444 | ||
445 | static const QEMUFileOps buf_read_ops = { | |
446 | .get_buffer = buf_get_buffer, | |
447 | .close = buf_close, | |
448 | }; | |
449 | ||
450 | static const QEMUFileOps buf_write_ops = { | |
451 | .put_buffer = buf_put_buffer, | |
452 | .close = buf_close, | |
453 | }; | |
454 | ||
455 | QEMUFile *qemu_bufopen(const char *mode, QEMUSizedBuffer *input) | |
456 | { | |
457 | QEMUBuffer *s; | |
458 | ||
459 | if (mode == NULL || (mode[0] != 'r' && mode[0] != 'w') || | |
460 | mode[1] != '\0') { | |
461 | error_report("qemu_bufopen: Argument validity check failed"); | |
462 | return NULL; | |
463 | } | |
464 | ||
465 | s = g_malloc0(sizeof(QEMUBuffer)); | |
466 | if (mode[0] == 'r') { | |
467 | s->qsb = input; | |
468 | } | |
469 | ||
470 | if (s->qsb == NULL) { | |
471 | s->qsb = qsb_create(NULL, 0); | |
472 | } | |
473 | if (!s->qsb) { | |
474 | g_free(s); | |
475 | error_report("qemu_bufopen: qsb_create failed"); | |
476 | return NULL; | |
477 | } | |
478 | ||
479 | ||
480 | if (mode[0] == 'r') { | |
481 | s->file = qemu_fopen_ops(s, &buf_read_ops); | |
482 | } else { | |
483 | s->file = qemu_fopen_ops(s, &buf_write_ops); | |
484 | } | |
485 | return s->file; | |
486 | } |