Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/snapshot.c
Show First 20 Lines • Show All 113 Lines • ▼ Show 20 Lines | |||||
#define MB (1024UL * KB) | #define MB (1024UL * KB) | ||||
#define GB (1024UL * MB) | #define GB (1024UL * MB) | ||||
#define SNAPSHOT_CHUNK (4 * MB) | #define SNAPSHOT_CHUNK (4 * MB) | ||||
#define PROG_BUF_SZ (8192) | #define PROG_BUF_SZ (8192) | ||||
#define SNAPSHOT_BUFFER_SIZE (20 * MB) | #define SNAPSHOT_BUFFER_SIZE (20 * MB) | ||||
#define JSON_STRUCT_ARR_KEY "structs" | #define JSON_KERNEL_ARR_KEY "kern_structs" | ||||
corvink: IMHO, we should split such changes into a separate commit. | |||||
Done Inline ActionsWill do. gusev.vitaliy_gmail.com: Will do. | |||||
#define JSON_DEV_ARR_KEY "devices" | #define JSON_DEV_ARR_KEY "devices" | ||||
#define JSON_BASIC_METADATA_KEY "basic metadata" | #define JSON_BASIC_METADATA_KEY "basic metadata" | ||||
#define JSON_SNAPSHOT_REQ_KEY "snapshot_req" | #define JSON_SNAPSHOT_REQ_KEY "device" | ||||
#define JSON_SIZE_KEY "size" | #define JSON_SIZE_KEY "size" | ||||
#define JSON_FILE_OFFSET_KEY "file_offset" | #define JSON_FILE_OFFSET_KEY "file_offset" | ||||
#define JSON_NCPUS_KEY "ncpus" | #define JSON_NCPUS_KEY "ncpus" | ||||
#define JSON_VMNAME_KEY "vmname" | #define JSON_VMNAME_KEY "vmname" | ||||
#define JSON_MEMSIZE_KEY "memsize" | #define JSON_MEMSIZE_KEY "memsize" | ||||
#define JSON_MEMFLAGS_KEY "memflags" | #define JSON_MEMFLAGS_KEY "memflags" | ||||
#define min(a,b) \ | #define min(a,b) \ | ||||
({ \ | ({ \ | ||||
__typeof__ (a) _a = (a); \ | __typeof__ (a) _a = (a); \ | ||||
__typeof__ (b) _b = (b); \ | __typeof__ (b) _b = (b); \ | ||||
_a < _b ? _a : _b; \ | _a < _b ? _a : _b; \ | ||||
}) | }) | ||||
const struct vm_snapshot_dev_info snapshot_devs[] = { | |||||
{ "atkbdc", atkbdc_snapshot, NULL, NULL }, | |||||
{ "virtio-net", pci_snapshot, pci_pause, pci_resume }, | |||||
{ "virtio-blk", pci_snapshot, pci_pause, pci_resume }, | |||||
{ "virtio-rnd", pci_snapshot, NULL, NULL }, | |||||
{ "lpc", pci_snapshot, NULL, NULL }, | |||||
{ "fbuf", pci_snapshot, NULL, NULL }, | |||||
{ "xhci", pci_snapshot, NULL, NULL }, | |||||
{ "e1000", pci_snapshot, NULL, NULL }, | |||||
{ "ahci", pci_snapshot, pci_pause, pci_resume }, | |||||
{ "ahci-hd", pci_snapshot, pci_pause, pci_resume }, | |||||
{ "ahci-cd", pci_snapshot, pci_pause, pci_resume }, | |||||
}; | |||||
const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | ||||
{ "vhpet", STRUCT_VHPET }, | { "vhpet", STRUCT_VHPET }, | ||||
{ "vm", STRUCT_VM }, | { "vm", STRUCT_VM }, | ||||
{ "vmx", STRUCT_VMX }, | { "vmx", STRUCT_VMX }, | ||||
{ "vioapic", STRUCT_VIOAPIC }, | { "vioapic", STRUCT_VIOAPIC }, | ||||
{ "vlapic", STRUCT_VLAPIC }, | { "vlapic", STRUCT_VLAPIC }, | ||||
{ "vmcx", STRUCT_VMCX }, | { "vmcx", STRUCT_VMCX }, | ||||
{ "vatpit", STRUCT_VATPIT }, | { "vatpit", STRUCT_VATPIT }, | ||||
{ "vatpic", STRUCT_VATPIC }, | { "vatpic", STRUCT_VATPIC }, | ||||
{ "vpmtmr", STRUCT_VPMTMR }, | { "vpmtmr", STRUCT_VPMTMR }, | ||||
{ "vrtc", STRUCT_VRTC }, | { "vrtc", STRUCT_VRTC }, | ||||
}; | }; | ||||
static cpuset_t vcpus_active, vcpus_suspended; | static cpuset_t vcpus_active, vcpus_suspended; | ||||
static pthread_mutex_t vcpu_lock; | static pthread_mutex_t vcpu_lock; | ||||
static pthread_cond_t vcpus_idle, vcpus_can_run; | static pthread_cond_t vcpus_idle, vcpus_can_run; | ||||
static bool checkpoint_active; | static bool checkpoint_active; | ||||
struct snapshot_dev { | |||||
LIST_ENTRY(snapshot_dev) dev_link; | |||||
const char *dev_name; | |||||
struct snapshot_ops *dev_ops; | |||||
void *dev_cbdata; | |||||
}; | |||||
static LIST_HEAD(, snapshot_dev) snapshot_devices; | |||||
void | |||||
register_snapshot_dev(const char *name, struct snapshot_ops *ops, | |||||
void *cbdata) | |||||
{ | |||||
struct snapshot_dev *dev; | |||||
assert(ops != NULL && ops->snapshot_cb != NULL); | |||||
dev = calloc(1, sizeof (struct snapshot_dev)); | |||||
Not Done Inline ActionsMissing check for NULL. corvink: Missing check for `NULL`. | |||||
dev->dev_name = name; | |||||
dev->dev_ops = ops; | |||||
dev->dev_cbdata = cbdata; | |||||
LIST_INSERT_HEAD(&snapshot_devices, dev, dev_link); | |||||
} | |||||
/* | /* | ||||
* TODO: Harden this function and all of its callers since 'base_str' is a user | * TODO: Harden this function and all of its callers since 'base_str' is a user | ||||
* provided string. | * provided string. | ||||
*/ | */ | ||||
static char * | static char * | ||||
strcat_extension(const char *base_str, const char *ext) | strcat_extension(const char *base_str, const char *ext) | ||||
{ | { | ||||
char *res; | char *res; | ||||
▲ Show 20 Lines • Show All 237 Lines • ▼ Show 20 Lines | |||||
static void * | static void * | ||||
lookup_struct(enum snapshot_req struct_id, struct restore_state *rstate, | lookup_struct(enum snapshot_req struct_id, struct restore_state *rstate, | ||||
size_t *struct_size) | size_t *struct_size) | ||||
{ | { | ||||
const ucl_object_t *structs = NULL, *obj = NULL; | const ucl_object_t *structs = NULL, *obj = NULL; | ||||
ucl_object_iter_t it = NULL; | ucl_object_iter_t it = NULL; | ||||
int64_t snapshot_req, size, file_offset; | int64_t snapshot_req, size, file_offset; | ||||
structs = ucl_object_lookup(rstate->meta_root_obj, JSON_STRUCT_ARR_KEY); | structs = ucl_object_lookup(rstate->meta_root_obj, JSON_KERNEL_ARR_KEY); | ||||
if (structs == NULL) { | if (structs == NULL) { | ||||
fprintf(stderr, "Failed to find '%s' object.\n", | fprintf(stderr, "Failed to find '%s' object.\n", | ||||
JSON_STRUCT_ARR_KEY); | JSON_KERNEL_ARR_KEY); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
if (ucl_object_type((ucl_object_t *)structs) != UCL_ARRAY) { | if (ucl_object_type((ucl_object_t *)structs) != UCL_ARRAY) { | ||||
fprintf(stderr, "Object '%s' is not an array.\n", | fprintf(stderr, "Object '%s' is not an array.\n", | ||||
JSON_STRUCT_ARR_KEY); | JSON_KERNEL_ARR_KEY); | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
while ((obj = ucl_object_iterate(structs, &it, true)) != NULL) { | while ((obj = ucl_object_iterate(structs, &it, true)) != NULL) { | ||||
snapshot_req = -1; | snapshot_req = -1; | ||||
JSON_GET_INT_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, | JSON_GET_INT_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, | ||||
&snapshot_req, NULL); | &snapshot_req, NULL); | ||||
assert(snapshot_req >= 0); | assert(snapshot_req >= 0); | ||||
▲ Show 20 Lines • Show All 477 Lines • ▼ Show 20 Lines | for (i = 0; i < nitems(snapshot_kern_structs); i++) { | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, | vm_restore_device(struct vmctx *ctx, struct restore_state *rstate, | ||||
const struct vm_snapshot_dev_info *info) | struct snapshot_dev *dev) | ||||
{ | { | ||||
void *dev_ptr; | void *dev_ptr; | ||||
size_t dev_size; | size_t dev_size; | ||||
int ret; | int ret; | ||||
struct vm_snapshot_meta *meta; | struct vm_snapshot_meta *meta; | ||||
dev_ptr = lookup_dev(info->dev_name, rstate, &dev_size); | dev_ptr = lookup_dev(dev->dev_name, rstate, &dev_size); | ||||
if (dev_ptr == NULL) { | if (dev_ptr == NULL) { | ||||
fprintf(stderr, "Failed to lookup dev: %s\r\n", info->dev_name); | fprintf(stderr, "Failed to lookup dev: %s\n", dev->dev_name); | ||||
fprintf(stderr, "Continuing the restore/migration process\r\n"); | fprintf(stderr, "Continuing the restore/migration process\r\n"); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (dev_size == 0) { | if (dev_size == 0) { | ||||
fprintf(stderr, "%s: Device size is 0. " | fprintf(stderr, "%s: Device size is 0. Assume %s is not used\n", | ||||
"Assuming %s is not used\r\n", | __func__, dev->dev_name); | ||||
__func__, info->dev_name); | |||||
return (0); | return (0); | ||||
} | } | ||||
meta = &(struct vm_snapshot_meta) { | meta = &(struct vm_snapshot_meta) { | ||||
.ctx = ctx, | .ctx = ctx, | ||||
.dev_name = info->dev_name, | .dev_name = dev->dev_name, | ||||
.buffer.buf_start = dev_ptr, | .buffer.buf_start = dev_ptr, | ||||
.buffer.buf_size = dev_size, | .buffer.buf_size = dev_size, | ||||
.buffer.buf = dev_ptr, | .buffer.buf = dev_ptr, | ||||
.buffer.buf_rem = dev_size, | .buffer.buf_rem = dev_size, | ||||
.op = VM_SNAPSHOT_RESTORE, | .op = VM_SNAPSHOT_RESTORE, | ||||
}; | }; | ||||
ret = (*info->snapshot_cb)(meta); | ret = (dev->dev_ops->snapshot_cb)(meta, dev->dev_cbdata); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to restore dev: %s\r\n", | fprintf(stderr, "Failed to restore %s: %d\n", | ||||
info->dev_name); | dev->dev_name, ret); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate) | vm_restore_devices(struct vmctx *ctx, struct restore_state *rstate) | ||||
{ | { | ||||
struct snapshot_dev *dev; | |||||
int ret; | int ret; | ||||
int i; | |||||
for (i = 0; i < nitems(snapshot_devs); i++) { | LIST_FOREACH(dev, &snapshot_devices, dev_link) { | ||||
ret = vm_restore_user_dev(ctx, rstate, &snapshot_devs[i]); | ret = vm_restore_device(ctx, rstate, dev); | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
vm_pause_user_devs(struct vmctx *ctx) | vm_pause_devices(struct vmctx *ctx) | ||||
{ | { | ||||
const struct vm_snapshot_dev_info *info; | struct snapshot_dev *dev; | ||||
int ret; | int err; | ||||
int i; | |||||
for (i = 0; i < nitems(snapshot_devs); i++) { | LIST_FOREACH(dev, &snapshot_devices, dev_link) { | ||||
info = &snapshot_devs[i]; | if (dev->dev_ops->pause_cb) | ||||
if (info->pause_cb == NULL) | err = dev->dev_ops->pause_cb(ctx, dev->dev_cbdata); | ||||
continue; | if (err != 0) | ||||
return (err); | |||||
ret = info->pause_cb(ctx, info->dev_name); | |||||
if (ret != 0) | |||||
return (ret); | |||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
int | int | ||||
vm_resume_user_devs(struct vmctx *ctx) | vm_resume_devices(struct vmctx *ctx) | ||||
{ | { | ||||
const struct vm_snapshot_dev_info *info; | struct snapshot_dev *dev; | ||||
int ret; | int err; | ||||
int i; | |||||
for (i = 0; i < nitems(snapshot_devs); i++) { | LIST_FOREACH(dev, &snapshot_devices, dev_link) { | ||||
info = &snapshot_devs[i]; | if (dev->dev_ops->resume_cb) | ||||
if (info->resume_cb == NULL) | err = dev->dev_ops->resume_cb(ctx, dev->dev_cbdata); | ||||
continue; | if (err != 0) | ||||
return (err); | |||||
ret = info->resume_cb(ctx, info->dev_name); | |||||
if (ret != 0) | |||||
return (ret); | |||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, | vm_save_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, | ||||
Not Done Inline ActionsIs this rename necessary? corvink: Is this rename necessary? | |||||
Done Inline ActionsYes because it does "saving" and not save/restore. Using "snapshot" implies a function does "save and restore". gusev.vitaliy_gmail.com: Yes because it does "saving" and not save/restore. Using "snapshot" implies a function does… | |||||
struct vm_snapshot_meta *meta, off_t *offset) | struct vm_snapshot_meta *meta, off_t *offset) | ||||
{ | { | ||||
int ret; | int ret; | ||||
size_t data_size; | size_t data_size; | ||||
ssize_t write_cnt; | ssize_t write_cnt; | ||||
ret = vm_snapshot_req(meta); | ret = vm_snapshot_req(meta); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
Show All 14 Lines | vm_save_kern_struct(int data_fd, xo_handle_t *xop, const char *array_key, | ||||
/* Write metadata. */ | /* Write metadata. */ | ||||
xo_open_instance_h(xop, array_key); | xo_open_instance_h(xop, array_key); | ||||
xo_emit_h(xop, "{:debug_name/%s}\n", meta->dev_name); | xo_emit_h(xop, "{:debug_name/%s}\n", meta->dev_name); | ||||
xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%d}\n", | xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%d}\n", | ||||
meta->dev_req); | meta->dev_req); | ||||
xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); | xo_emit_h(xop, "{:" JSON_SIZE_KEY "/%lu}\n", data_size); | ||||
xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); | xo_emit_h(xop, "{:" JSON_FILE_OFFSET_KEY "/%lu}\n", *offset); | ||||
xo_close_instance_h(xop, JSON_STRUCT_ARR_KEY); | xo_close_instance_h(xop, JSON_KERNEL_ARR_KEY); | ||||
*offset += data_size; | *offset += data_size; | ||||
done: | done: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | vm_save_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | ||||
{ | { | ||||
int ret, i, error; | int ret, i, error; | ||||
size_t offset, buf_size; | size_t offset, buf_size; | ||||
char *buffer; | char *buffer; | ||||
struct vm_snapshot_meta *meta; | struct vm_snapshot_meta *meta; | ||||
error = 0; | error = 0; | ||||
offset = 0; | |||||
buf_size = SNAPSHOT_BUFFER_SIZE; | buf_size = SNAPSHOT_BUFFER_SIZE; | ||||
offset = lseek(data_fd, 0, SEEK_CUR); | |||||
if (offset < 0) { | |||||
perror("Failed to get data file current offset."); | |||||
return (-1); | |||||
} | |||||
buffer = malloc(SNAPSHOT_BUFFER_SIZE * sizeof(char)); | buffer = malloc(SNAPSHOT_BUFFER_SIZE * sizeof(char)); | ||||
if (buffer == NULL) { | if (buffer == NULL) { | ||||
error = ENOMEM; | error = ENOMEM; | ||||
perror("Failed to allocate memory for snapshot buffer"); | perror("Failed to allocate memory for snapshot buffer"); | ||||
goto err_vm_snapshot_kern_data; | goto err_vm_snapshot_kern_data; | ||||
} | } | ||||
meta = &(struct vm_snapshot_meta) { | meta = &(struct vm_snapshot_meta) { | ||||
.ctx = ctx, | .ctx = ctx, | ||||
.buffer.buf_start = buffer, | .buffer.buf_start = buffer, | ||||
.buffer.buf_size = buf_size, | .buffer.buf_size = buf_size, | ||||
.op = VM_SNAPSHOT_SAVE, | .op = VM_SNAPSHOT_SAVE, | ||||
}; | }; | ||||
xo_open_list_h(xop, JSON_STRUCT_ARR_KEY); | xo_open_list_h(xop, JSON_KERNEL_ARR_KEY); | ||||
for (i = 0; i < nitems(snapshot_kern_structs); i++) { | for (i = 0; i < nitems(snapshot_kern_structs); i++) { | ||||
meta->dev_name = snapshot_kern_structs[i].struct_name; | meta->dev_name = snapshot_kern_structs[i].struct_name; | ||||
meta->dev_req = snapshot_kern_structs[i].req; | meta->dev_req = snapshot_kern_structs[i].req; | ||||
memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); | memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); | ||||
meta->buffer.buf = meta->buffer.buf_start; | meta->buffer.buf = meta->buffer.buf_start; | ||||
meta->buffer.buf_rem = meta->buffer.buf_size; | meta->buffer.buf_rem = meta->buffer.buf_size; | ||||
ret = vm_snapshot_kern_struct(data_fd, xop, JSON_DEV_ARR_KEY, | ret = vm_save_kern_struct(data_fd, xop, JSON_DEV_ARR_KEY, | ||||
meta, &offset); | meta, &offset); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
error = -1; | error = -1; | ||||
goto err_vm_snapshot_kern_data; | goto err_vm_snapshot_kern_data; | ||||
} | } | ||||
} | } | ||||
xo_close_list_h(xop, JSON_STRUCT_ARR_KEY); | xo_close_list_h(xop, JSON_KERNEL_ARR_KEY); | ||||
err_vm_snapshot_kern_data: | err_vm_snapshot_kern_data: | ||||
if (buffer != NULL) | if (buffer != NULL) | ||||
free(buffer); | free(buffer); | ||||
return (error); | return (error); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) | vm_save_basic_metadata(struct vmctx *ctx, xo_handle_t *xop, size_t memsz) | ||||
{ | { | ||||
xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); | xo_open_container_h(xop, JSON_BASIC_METADATA_KEY); | ||||
xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); | xo_emit_h(xop, "{:" JSON_NCPUS_KEY "/%ld}\n", guest_ncpus); | ||||
xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vm_get_name(ctx)); | xo_emit_h(xop, "{:" JSON_VMNAME_KEY "/%s}\n", vm_get_name(ctx)); | ||||
xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); | xo_emit_h(xop, "{:" JSON_MEMSIZE_KEY "/%lu}\n", memsz); | ||||
xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", vm_get_memflags(ctx)); | xo_emit_h(xop, "{:" JSON_MEMFLAGS_KEY "/%d}\n", vm_get_memflags(ctx)); | ||||
xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); | xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); | ||||
Show All 24 Lines | vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, | ||||
xo_close_instance_h(xop, array_key); | xo_close_instance_h(xop, array_key); | ||||
*offset += data_size; | *offset += data_size; | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, | vm_save_device(struct snapshot_dev *dev, | ||||
int data_fd, xo_handle_t *xop, | int data_fd, xo_handle_t *xop, | ||||
struct vm_snapshot_meta *meta, off_t *offset) | struct vm_snapshot_meta *meta, off_t *offset) | ||||
{ | { | ||||
int ret; | int ret; | ||||
ret = (*info->snapshot_cb)(meta); | meta->dev_name = dev->dev_name; | ||||
memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); | |||||
meta->buffer.buf = meta->buffer.buf_start; | |||||
meta->buffer.buf_rem = meta->buffer.buf_size; | |||||
ret = (dev->dev_ops->snapshot_cb)(meta, dev->dev_cbdata); | |||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", | fprintf(stderr, "Failed to snapshot %s: %d\n", | ||||
meta->dev_name, ret); | meta->dev_name, ret); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, | ret = vm_snapshot_dev_write_data(data_fd, xop, JSON_DEV_ARR_KEY, meta, | ||||
offset); | offset); | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_user_devs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | vm_save_devices(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | ||||
{ | { | ||||
int ret, i; | int ret; | ||||
off_t offset; | off_t offset; | ||||
void *buffer; | void *buffer; | ||||
size_t buf_size; | size_t buf_size; | ||||
struct vm_snapshot_meta *meta; | struct vm_snapshot_meta *meta; | ||||
struct snapshot_dev *dev; | |||||
buf_size = SNAPSHOT_BUFFER_SIZE; | buf_size = SNAPSHOT_BUFFER_SIZE; | ||||
offset = lseek(data_fd, 0, SEEK_CUR); | offset = lseek(data_fd, 0, SEEK_CUR); | ||||
if (offset < 0) { | if (offset < 0) { | ||||
perror("Failed to get data file current offset."); | perror("Failed to get data file current offset."); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
Show All 12 Lines | meta = &(struct vm_snapshot_meta) { | ||||
.buffer.buf_size = buf_size, | .buffer.buf_size = buf_size, | ||||
.op = VM_SNAPSHOT_SAVE, | .op = VM_SNAPSHOT_SAVE, | ||||
}; | }; | ||||
xo_open_list_h(xop, JSON_DEV_ARR_KEY); | xo_open_list_h(xop, JSON_DEV_ARR_KEY); | ||||
/* Restore other devices that support this feature */ | /* Restore other devices that support this feature */ | ||||
for (i = 0; i < nitems(snapshot_devs); i++) { | LIST_FOREACH(dev, &snapshot_devices, dev_link) { | ||||
meta->dev_name = snapshot_devs[i].dev_name; | ret = vm_save_device(dev, data_fd, xop, meta, &offset); | ||||
memset(meta->buffer.buf_start, 0, meta->buffer.buf_size); | |||||
meta->buffer.buf = meta->buffer.buf_start; | |||||
meta->buffer.buf_rem = meta->buffer.buf_size; | |||||
ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, | |||||
meta, &offset); | |||||
if (ret != 0) | if (ret != 0) | ||||
goto snapshot_err; | goto snapshot_err; | ||||
} | } | ||||
xo_close_list_h(xop, JSON_DEV_ARR_KEY); | xo_close_list_h(xop, JSON_DEV_ARR_KEY); | ||||
snapshot_err: | snapshot_err: | ||||
if (buffer != NULL) | if (buffer != NULL) | ||||
▲ Show 20 Lines • Show All 120 Lines • ▼ Show 20 Lines | vm_checkpoint(struct vmctx *ctx, const char *checkpoint_file, bool stop_vm) | ||||
xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); | xop = xo_create_to_file(meta_file, XO_STYLE_JSON, XOF_PRETTY); | ||||
if (xop == NULL) { | if (xop == NULL) { | ||||
perror("Failed to get libxo handle on metadata file."); | perror("Failed to get libxo handle on metadata file."); | ||||
goto done; | goto done; | ||||
} | } | ||||
vm_vcpu_pause(ctx); | vm_vcpu_pause(ctx); | ||||
ret = vm_pause_user_devs(ctx); | ret = vm_pause_devices(ctx); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Could not pause devices\r\n"); | fprintf(stderr, "Could not pause devices\r\n"); | ||||
error = ret; | error = ret; | ||||
goto done; | goto done; | ||||
} | } | ||||
memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); | memsz = vm_snapshot_mem(ctx, fd_checkpoint, 0, true); | ||||
if (memsz == 0) { | if (memsz == 0) { | ||||
perror("Could not write guest memory to file"); | perror("Could not write guest memory to file"); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
ret = vm_snapshot_basic_metadata(ctx, xop, memsz); | ret = vm_save_basic_metadata(ctx, xop, memsz); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); | fprintf(stderr, "Failed to snapshot vm basic metadata.\n"); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
ret = vm_save_kern_structs(ctx, kdata_fd, xop); | |||||
ret = vm_snapshot_kern_structs(ctx, kdata_fd, xop); | |||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to snapshot vm kernel data.\n"); | fprintf(stderr, "Failed to snapshot vm kernel data.\n"); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
ret = vm_snapshot_user_devs(ctx, kdata_fd, xop); | ret = vm_save_devices(ctx, kdata_fd, xop); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to snapshot device state.\n"); | fprintf(stderr, "Failed to snapshot device state.\n"); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
xo_finish_h(xop); | xo_finish_h(xop); | ||||
if (stop_vm) { | if (stop_vm) { | ||||
vm_destroy(ctx); | vm_destroy(ctx); | ||||
exit(0); | exit(0); | ||||
} | } | ||||
done: | done: | ||||
ret = vm_resume_user_devs(ctx); | ret = vm_resume_devices(ctx); | ||||
if (ret != 0) | if (ret != 0) | ||||
fprintf(stderr, "Could not resume devices\r\n"); | fprintf(stderr, "Could not resume devices\r\n"); | ||||
vm_vcpu_resume(ctx); | vm_vcpu_resume(ctx); | ||||
if (fd_checkpoint > 0) | if (fd_checkpoint > 0) | ||||
close(fd_checkpoint); | close(fd_checkpoint); | ||||
if (meta_filename != NULL) | if (meta_filename != NULL) | ||||
free(meta_filename); | free(meta_filename); | ||||
if (kdata_filename != NULL) | if (kdata_filename != NULL) | ||||
▲ Show 20 Lines • Show All 279 Lines • Show Last 20 Lines |
IMHO, we should split such changes into a separate commit.