Commit 5dafc53f1fb091d242f2179ffcb43bb28af36d1e
1 parent
f5049756
Refactor QEMUFile for live migration
To support live migration, we override QEMUFile so that instead of writing to disk, the save/restore state happens over a network connection. This patch makes QEMUFile read/write operations function pointers so that we can override them for live migration. Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@5352 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
237 additions
and
56 deletions
hw/hw.h
... | ... | @@ -7,9 +7,36 @@ |
7 | 7 | |
8 | 8 | /* VM Load/Save */ |
9 | 9 | |
10 | +/* This function writes a chunk of data to a file at the given position. | |
11 | + * The pos argument can be ignored if the file is only being used for | |
12 | + * streaming. The handler should try to write all of the data it can. | |
13 | + */ | |
14 | +typedef void (QEMUFilePutBufferFunc)(void *opaque, const uint8_t *buf, | |
15 | + int64_t pos, int size); | |
16 | + | |
17 | +/* Read a chunk of data from a file at the given position. The pos argument | |
18 | + * can be ignored if the file is only be used for streaming. The number of | |
19 | + * bytes actually read should be returned. | |
20 | + */ | |
21 | +typedef int (QEMUFileGetBufferFunc)(void *opaque, uint8_t *buf, | |
22 | + int64_t pos, int size); | |
23 | + | |
24 | +/* Close a file and return an error code */ | |
25 | +typedef int (QEMUFileCloseFunc)(void *opaque); | |
26 | + | |
27 | +/* Called to determine if the file has exceeded it's bandwidth allocation. The | |
28 | + * bandwidth capping is a soft limit, not a hard limit. | |
29 | + */ | |
30 | +typedef int (QEMUFileRateLimit)(void *opaque); | |
31 | + | |
32 | +QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, | |
33 | + QEMUFileGetBufferFunc *get_buffer, | |
34 | + QEMUFileCloseFunc *close, | |
35 | + QEMUFileRateLimit *rate_limit); | |
10 | 36 | QEMUFile *qemu_fopen(const char *filename, const char *mode); |
37 | +QEMUFile *qemu_fopen_fd(int fd); | |
11 | 38 | void qemu_fflush(QEMUFile *f); |
12 | -void qemu_fclose(QEMUFile *f); | |
39 | +int qemu_fclose(QEMUFile *f); | |
13 | 40 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size); |
14 | 41 | void qemu_put_byte(QEMUFile *f, int v); |
15 | 42 | void qemu_put_be16(QEMUFile *f, unsigned int v); |
... | ... | @@ -20,6 +47,12 @@ int qemu_get_byte(QEMUFile *f); |
20 | 47 | unsigned int qemu_get_be16(QEMUFile *f); |
21 | 48 | unsigned int qemu_get_be32(QEMUFile *f); |
22 | 49 | uint64_t qemu_get_be64(QEMUFile *f); |
50 | +int qemu_file_rate_limit(QEMUFile *f); | |
51 | + | |
52 | +/* Try to send any outstanding data. This function is useful when output is | |
53 | + * halted due to rate limiting or EAGAIN errors occur as it can be used to | |
54 | + * resume output. */ | |
55 | +void qemu_file_put_notify(QEMUFile *f); | |
23 | 56 | |
24 | 57 | static inline void qemu_put_be64s(QEMUFile *f, const uint64_t *pv) |
25 | 58 | { | ... | ... |
vl.c
... | ... | @@ -6198,11 +6198,12 @@ void qemu_del_wait_object(HANDLE handle, WaitObjectFunc *func, void *opaque) |
6198 | 6198 | #define IO_BUF_SIZE 32768 |
6199 | 6199 | |
6200 | 6200 | struct QEMUFile { |
6201 | - FILE *outfile; | |
6202 | - BlockDriverState *bs; | |
6203 | - int is_file; | |
6204 | - int is_writable; | |
6205 | - int64_t base_offset; | |
6201 | + QEMUFilePutBufferFunc *put_buffer; | |
6202 | + QEMUFileGetBufferFunc *get_buffer; | |
6203 | + QEMUFileCloseFunc *close; | |
6204 | + QEMUFileRateLimit *rate_limit; | |
6205 | + void *opaque; | |
6206 | + | |
6206 | 6207 | int64_t buf_offset; /* start of buffer when writing, end of buffer |
6207 | 6208 | when reading */ |
6208 | 6209 | int buf_index; |
... | ... | @@ -6210,58 +6211,198 @@ struct QEMUFile { |
6210 | 6211 | uint8_t buf[IO_BUF_SIZE]; |
6211 | 6212 | }; |
6212 | 6213 | |
6214 | +typedef struct QEMUFileFD | |
6215 | +{ | |
6216 | + int fd; | |
6217 | + QEMUFile *file; | |
6218 | +} QEMUFileFD; | |
6219 | + | |
6220 | +static void fd_put_notify(void *opaque) | |
6221 | +{ | |
6222 | + QEMUFileFD *s = opaque; | |
6223 | + | |
6224 | + /* Remove writable callback and do a put notify */ | |
6225 | + qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); | |
6226 | + qemu_file_put_notify(s->file); | |
6227 | +} | |
6228 | + | |
6229 | +static int fd_put_buffer(void *opaque, const uint8_t *buf, | |
6230 | + int64_t pos, int size) | |
6231 | +{ | |
6232 | + QEMUFileFD *s = opaque; | |
6233 | + ssize_t len; | |
6234 | + | |
6235 | + do { | |
6236 | + len = write(s->fd, buf, size); | |
6237 | + } while (len == -1 && errno == EINTR); | |
6238 | + | |
6239 | + if (len == -1) | |
6240 | + len = -errno; | |
6241 | + | |
6242 | + /* When the fd becomes writable again, register a callback to do | |
6243 | + * a put notify */ | |
6244 | + if (len == -EAGAIN) | |
6245 | + qemu_set_fd_handler2(s->fd, NULL, NULL, fd_put_notify, s); | |
6246 | + | |
6247 | + return len; | |
6248 | +} | |
6249 | + | |
6250 | +static int fd_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) | |
6251 | +{ | |
6252 | + QEMUFileFD *s = opaque; | |
6253 | + ssize_t len; | |
6254 | + | |
6255 | + do { | |
6256 | + len = read(s->fd, buf, size); | |
6257 | + } while (len == -1 && errno == EINTR); | |
6258 | + | |
6259 | + if (len == -1) | |
6260 | + len = -errno; | |
6261 | + | |
6262 | + return len; | |
6263 | +} | |
6264 | + | |
6265 | +static int fd_close(void *opaque) | |
6266 | +{ | |
6267 | + QEMUFileFD *s = opaque; | |
6268 | + qemu_free(s); | |
6269 | + return 0; | |
6270 | +} | |
6271 | + | |
6272 | +QEMUFile *qemu_fopen_fd(int fd) | |
6273 | +{ | |
6274 | + QEMUFileFD *s = qemu_mallocz(sizeof(QEMUFileFD)); | |
6275 | + | |
6276 | + if (s == NULL) | |
6277 | + return NULL; | |
6278 | + | |
6279 | + s->fd = fd; | |
6280 | + s->file = qemu_fopen_ops(s, fd_put_buffer, fd_get_buffer, fd_close, NULL); | |
6281 | + return s->file; | |
6282 | +} | |
6283 | + | |
6284 | +typedef struct QEMUFileStdio | |
6285 | +{ | |
6286 | + FILE *outfile; | |
6287 | +} QEMUFileStdio; | |
6288 | + | |
6289 | +static void file_put_buffer(void *opaque, const uint8_t *buf, | |
6290 | + int64_t pos, int size) | |
6291 | +{ | |
6292 | + QEMUFileStdio *s = opaque; | |
6293 | + fseek(s->outfile, pos, SEEK_SET); | |
6294 | + fwrite(buf, 1, size, s->outfile); | |
6295 | +} | |
6296 | + | |
6297 | +static int file_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) | |
6298 | +{ | |
6299 | + QEMUFileStdio *s = opaque; | |
6300 | + fseek(s->outfile, pos, SEEK_SET); | |
6301 | + return fread(buf, 1, size, s->outfile); | |
6302 | +} | |
6303 | + | |
6304 | +static int file_close(void *opaque) | |
6305 | +{ | |
6306 | + QEMUFileStdio *s = opaque; | |
6307 | + fclose(s->outfile); | |
6308 | + qemu_free(s); | |
6309 | + return 0; | |
6310 | +} | |
6311 | + | |
6213 | 6312 | QEMUFile *qemu_fopen(const char *filename, const char *mode) |
6214 | 6313 | { |
6215 | - QEMUFile *f; | |
6314 | + QEMUFileStdio *s; | |
6216 | 6315 | |
6217 | - f = qemu_mallocz(sizeof(QEMUFile)); | |
6218 | - if (!f) | |
6316 | + s = qemu_mallocz(sizeof(QEMUFileStdio)); | |
6317 | + if (!s) | |
6219 | 6318 | return NULL; |
6220 | - if (!strcmp(mode, "wb")) { | |
6221 | - f->is_writable = 1; | |
6222 | - } else if (!strcmp(mode, "rb")) { | |
6223 | - f->is_writable = 0; | |
6224 | - } else { | |
6225 | - goto fail; | |
6226 | - } | |
6227 | - f->outfile = fopen(filename, mode); | |
6228 | - if (!f->outfile) | |
6319 | + | |
6320 | + s->outfile = fopen(filename, mode); | |
6321 | + if (!s->outfile) | |
6229 | 6322 | goto fail; |
6230 | - f->is_file = 1; | |
6231 | - return f; | |
6232 | - fail: | |
6233 | - if (f->outfile) | |
6234 | - fclose(f->outfile); | |
6235 | - qemu_free(f); | |
6323 | + | |
6324 | + if (!strcmp(mode, "wb")) | |
6325 | + return qemu_fopen_ops(s, file_put_buffer, NULL, file_close, NULL); | |
6326 | + else if (!strcmp(mode, "rb")) | |
6327 | + return qemu_fopen_ops(s, NULL, file_get_buffer, file_close, NULL); | |
6328 | + | |
6329 | +fail: | |
6330 | + if (s->outfile) | |
6331 | + fclose(s->outfile); | |
6332 | + qemu_free(s); | |
6236 | 6333 | return NULL; |
6237 | 6334 | } |
6238 | 6335 | |
6239 | -static QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) | |
6336 | +typedef struct QEMUFileBdrv | |
6337 | +{ | |
6338 | + BlockDriverState *bs; | |
6339 | + int64_t base_offset; | |
6340 | +} QEMUFileBdrv; | |
6341 | + | |
6342 | +static void bdrv_put_buffer(void *opaque, const uint8_t *buf, | |
6343 | + int64_t pos, int size) | |
6344 | +{ | |
6345 | + QEMUFileBdrv *s = opaque; | |
6346 | + bdrv_pwrite(s->bs, s->base_offset + pos, buf, size); | |
6347 | +} | |
6348 | + | |
6349 | +static int bdrv_get_buffer(void *opaque, uint8_t *buf, int64_t pos, int size) | |
6350 | +{ | |
6351 | + QEMUFileBdrv *s = opaque; | |
6352 | + return bdrv_pread(s->bs, s->base_offset + pos, buf, size); | |
6353 | +} | |
6354 | + | |
6355 | +static int bdrv_fclose(void *opaque) | |
6356 | +{ | |
6357 | + QEMUFileBdrv *s = opaque; | |
6358 | + qemu_free(s); | |
6359 | + return 0; | |
6360 | +} | |
6361 | + | |
6362 | +QEMUFile *qemu_fopen_bdrv(BlockDriverState *bs, int64_t offset, int is_writable) | |
6363 | +{ | |
6364 | + QEMUFileBdrv *s; | |
6365 | + | |
6366 | + s = qemu_mallocz(sizeof(QEMUFileBdrv)); | |
6367 | + if (!s) | |
6368 | + return NULL; | |
6369 | + | |
6370 | + s->bs = bs; | |
6371 | + s->base_offset = offset; | |
6372 | + | |
6373 | + if (is_writable) | |
6374 | + return qemu_fopen_ops(s, bdrv_put_buffer, NULL, bdrv_fclose, NULL); | |
6375 | + | |
6376 | + return qemu_fopen_ops(s, NULL, bdrv_get_buffer, bdrv_fclose, NULL); | |
6377 | +} | |
6378 | + | |
6379 | +QEMUFile *qemu_fopen_ops(void *opaque, QEMUFilePutBufferFunc *put_buffer, | |
6380 | + QEMUFileGetBufferFunc *get_buffer, | |
6381 | + QEMUFileCloseFunc *close, | |
6382 | + QEMUFileRateLimit *rate_limit) | |
6240 | 6383 | { |
6241 | 6384 | QEMUFile *f; |
6242 | 6385 | |
6243 | 6386 | f = qemu_mallocz(sizeof(QEMUFile)); |
6244 | 6387 | if (!f) |
6245 | 6388 | return NULL; |
6246 | - f->is_file = 0; | |
6247 | - f->bs = bs; | |
6248 | - f->is_writable = is_writable; | |
6249 | - f->base_offset = offset; | |
6389 | + | |
6390 | + f->opaque = opaque; | |
6391 | + f->put_buffer = put_buffer; | |
6392 | + f->get_buffer = get_buffer; | |
6393 | + f->close = close; | |
6394 | + f->rate_limit = rate_limit; | |
6395 | + | |
6250 | 6396 | return f; |
6251 | 6397 | } |
6252 | 6398 | |
6253 | 6399 | void qemu_fflush(QEMUFile *f) |
6254 | 6400 | { |
6255 | - if (!f->is_writable) | |
6401 | + if (!f->put_buffer) | |
6256 | 6402 | return; |
6403 | + | |
6257 | 6404 | if (f->buf_index > 0) { |
6258 | - if (f->is_file) { | |
6259 | - fseek(f->outfile, f->buf_offset, SEEK_SET); | |
6260 | - fwrite(f->buf, 1, f->buf_index, f->outfile); | |
6261 | - } else { | |
6262 | - bdrv_pwrite(f->bs, f->base_offset + f->buf_offset, | |
6263 | - f->buf, f->buf_index); | |
6264 | - } | |
6405 | + f->put_buffer(f->opaque, f->buf, f->buf_offset, f->buf_index); | |
6265 | 6406 | f->buf_offset += f->buf_index; |
6266 | 6407 | f->buf_index = 0; |
6267 | 6408 | } |
... | ... | @@ -6271,32 +6412,31 @@ static void qemu_fill_buffer(QEMUFile *f) |
6271 | 6412 | { |
6272 | 6413 | int len; |
6273 | 6414 | |
6274 | - if (f->is_writable) | |
6415 | + if (!f->get_buffer) | |
6275 | 6416 | return; |
6276 | - if (f->is_file) { | |
6277 | - fseek(f->outfile, f->buf_offset, SEEK_SET); | |
6278 | - len = fread(f->buf, 1, IO_BUF_SIZE, f->outfile); | |
6279 | - if (len < 0) | |
6280 | - len = 0; | |
6281 | - } else { | |
6282 | - len = bdrv_pread(f->bs, f->base_offset + f->buf_offset, | |
6283 | - f->buf, IO_BUF_SIZE); | |
6284 | - if (len < 0) | |
6285 | - len = 0; | |
6286 | - } | |
6417 | + | |
6418 | + len = f->get_buffer(f->opaque, f->buf, f->buf_offset, IO_BUF_SIZE); | |
6419 | + if (len < 0) | |
6420 | + len = 0; | |
6421 | + | |
6287 | 6422 | f->buf_index = 0; |
6288 | 6423 | f->buf_size = len; |
6289 | 6424 | f->buf_offset += len; |
6290 | 6425 | } |
6291 | 6426 | |
6292 | -void qemu_fclose(QEMUFile *f) | |
6427 | +int qemu_fclose(QEMUFile *f) | |
6293 | 6428 | { |
6294 | - if (f->is_writable) | |
6295 | - qemu_fflush(f); | |
6296 | - if (f->is_file) { | |
6297 | - fclose(f->outfile); | |
6298 | - } | |
6429 | + int ret = 0; | |
6430 | + qemu_fflush(f); | |
6431 | + if (f->close) | |
6432 | + ret = f->close(f->opaque); | |
6299 | 6433 | qemu_free(f); |
6434 | + return ret; | |
6435 | +} | |
6436 | + | |
6437 | +void qemu_file_put_notify(QEMUFile *f) | |
6438 | +{ | |
6439 | + f->put_buffer(f->opaque, NULL, 0, 0); | |
6300 | 6440 | } |
6301 | 6441 | |
6302 | 6442 | void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, int size) |
... | ... | @@ -6370,7 +6510,7 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) |
6370 | 6510 | /* SEEK_END not supported */ |
6371 | 6511 | return -1; |
6372 | 6512 | } |
6373 | - if (f->is_writable) { | |
6513 | + if (f->put_buffer) { | |
6374 | 6514 | qemu_fflush(f); |
6375 | 6515 | f->buf_offset = pos; |
6376 | 6516 | } else { |
... | ... | @@ -6381,6 +6521,14 @@ int64_t qemu_fseek(QEMUFile *f, int64_t pos, int whence) |
6381 | 6521 | return pos; |
6382 | 6522 | } |
6383 | 6523 | |
6524 | +int qemu_file_rate_limit(QEMUFile *f) | |
6525 | +{ | |
6526 | + if (f->rate_limit) | |
6527 | + return f->rate_limit(f->opaque); | |
6528 | + | |
6529 | + return 0; | |
6530 | +} | |
6531 | + | |
6384 | 6532 | void qemu_put_be16(QEMUFile *f, unsigned int v) |
6385 | 6533 | { |
6386 | 6534 | qemu_put_byte(f, v >> 8); | ... | ... |