Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/snapshot.c
Show First 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | |||||
#include <unistd.h> | #include <unistd.h> | ||||
#include <assert.h> | #include <assert.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <pthread.h> | #include <pthread.h> | ||||
#include <pthread_np.h> | #include <pthread_np.h> | ||||
#include <sysexits.h> | #include <sysexits.h> | ||||
#include <stdbool.h> | #include <stdbool.h> | ||||
#include <sys/ioctl.h> | #include <sys/ioctl.h> | ||||
#include <machine/vmm.h> | #include <machine/vmm.h> | ||||
#ifndef WITHOUT_CAPSICUM | #ifndef WITHOUT_CAPSICUM | ||||
#include <machine/vmm_dev.h> | #include <machine/vmm_dev.h> | ||||
#endif | #endif | ||||
#include <machine/vmm_snapshot.h> | #include <machine/vmm_snapshot.h> | ||||
#include <vmmapi.h> | #include <vmmapi.h> | ||||
#include "bhyverun.h" | #include "bhyverun.h" | ||||
Show All 14 Lines | |||||
#include "snapshot.h" | #include "snapshot.h" | ||||
#include "xmsr.h" | #include "xmsr.h" | ||||
#include "spinup_ap.h" | #include "spinup_ap.h" | ||||
#include "rtc.h" | #include "rtc.h" | ||||
#include <libxo/xo.h> | #include <libxo/xo.h> | ||||
#include <ucl.h> | #include <ucl.h> | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
#include <openssl/evp.h> | |||||
#include "../lib/libc/stdlib/hsearch.h" | |||||
#endif | |||||
struct spinner_info { | struct spinner_info { | ||||
const size_t *crtval; | const size_t *crtval; | ||||
const size_t maxval; | const size_t maxval; | ||||
const size_t total; | const size_t total; | ||||
}; | }; | ||||
extern int guest_ncpus; | extern int guest_ncpus; | ||||
static struct winsize winsize; | static struct winsize winsize; | ||||
static sig_t old_winch_handler; | static sig_t old_winch_handler; | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
struct type_info { | |||||
char type_name[24]; | |||||
char fmt_str[24]; | |||||
unsigned char size; | |||||
}; | |||||
static struct hsearch_data *types_htable; | |||||
#endif | |||||
#define KB (1024UL) | #define KB (1024UL) | ||||
#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_STRUCT_ARR_KEY "structs" | ||||
#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 "snapshot_req" | ||||
#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 JSON_VERSION_KEY "version" | |||||
#define JSON_PARAMS_KEY "device_params" | |||||
#define JSON_PARAM_KEY "param_name" | |||||
#define JSON_PARAM_DATA_KEY "param_data" | |||||
#define JSON_PARAM_DATA_SIZE_KEY "data_size" | |||||
#define JSON_VERSION_KEY "version" | |||||
#define JSON_PARAMS_KEY "device_params" | |||||
#define JSON_PARAM_KEY "param_name" | |||||
#define JSON_PARAM_DATA_KEY "param_data" | |||||
#define JSON_PARAM_DATA_SIZE_KEY "data_size" | |||||
#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[] = { | const struct vm_snapshot_dev_info snapshot_devs[] = { | ||||
{ "atkbdc", atkbdc_snapshot, NULL, NULL }, | { "atkbdc", atkbdc_snapshot, NULL, NULL }, | ||||
{ "virtio-net", pci_snapshot, pci_pause, pci_resume }, | { "virtio-net", pci_snapshot, pci_pause, pci_resume }, | ||||
{ "virtio-blk", 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 }, | { "lpc", pci_snapshot, NULL, NULL }, | ||||
{ "fbuf", pci_snapshot, NULL, NULL }, | { "fbuf", pci_snapshot, NULL, NULL }, | ||||
{ "xhci", pci_snapshot, NULL, NULL }, | { "xhci", pci_snapshot, NULL, NULL }, | ||||
{ "e1000", pci_snapshot, NULL, NULL }, | { "e1000", pci_snapshot, NULL, NULL }, | ||||
{ "ahci", pci_snapshot, pci_pause, pci_resume }, | { "ahci", pci_snapshot, pci_pause, pci_resume }, | ||||
{ "ahci-hd", pci_snapshot, pci_pause, pci_resume }, | { "ahci-hd", pci_snapshot, pci_pause, pci_resume }, | ||||
{ "ahci-cd", pci_snapshot, pci_pause, pci_resume }, | { "ahci-cd", pci_snapshot, NULL, NULL }, | ||||
}; | }; | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
int vhpet_snapshot(struct vm_snapshot_meta *meta); | |||||
int vm_snapshot_vm(struct vm_snapshot_meta *meta); | |||||
int vmx_snapshot(struct vm_snapshot_meta *meta); | |||||
int vioapic_snapshot(struct vm_snapshot_meta *meta); | |||||
int vlapic_snapshot(struct vm_snapshot_meta *meta); | |||||
int vmx_vmcx_snapshot(struct vm_snapshot_meta *meta); | |||||
int vatpit_snapshot(struct vm_snapshot_meta *meta); | |||||
int vatpic_snapshot(struct vm_snapshot_meta *meta); | |||||
int vpmtmr_snapshot(struct vm_snapshot_meta *meta); | |||||
int vrtc_snapshot(struct vm_snapshot_meta *meta); | |||||
const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | ||||
{ "vhpet", STRUCT_VHPET }, | { "vhpet", STRUCT_VHPET, vhpet_snapshot }, | ||||
{ "vm", STRUCT_VM }, | { "vm", STRUCT_VM, vm_snapshot_vm }, | ||||
{ "vmx", STRUCT_VMX }, | { "vmx", STRUCT_VMX, vmx_snapshot }, | ||||
{ "vioapic", STRUCT_VIOAPIC }, | { "vioapic", STRUCT_VIOAPIC, vioapic_snapshot }, | ||||
{ "vlapic", STRUCT_VLAPIC }, | { "vlapic", STRUCT_VLAPIC, vlapic_snapshot }, | ||||
{ "vmcx", STRUCT_VMCX }, | { "vmcx", STRUCT_VMCX, vmx_vmcx_snapshot }, | ||||
{ "vatpit", STRUCT_VATPIT }, | { "vatpit", STRUCT_VATPIT, vatpit_snapshot }, | ||||
{ "vatpic", STRUCT_VATPIC }, | { "vatpic", STRUCT_VATPIC, vatpic_snapshot }, | ||||
{ "vpmtmr", STRUCT_VPMTMR }, | { "vpmtmr", STRUCT_VPMTMR, vpmtmr_snapshot }, | ||||
{ "vrtc", STRUCT_VRTC }, | { "vrtc", STRUCT_VRTC, vrtc_snapshot }, | ||||
}; | }; | ||||
#else | |||||
const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | |||||
{ "vhpet", STRUCT_VHPET, vhpet_snapshot }, | |||||
{ "vm", STRUCT_VM, vm_snapshot_vm }, | |||||
{ "vmx", STRUCT_VMX, vmx_snapshot }, | |||||
{ "vioapic", STRUCT_VIOAPIC, vioapic_snapshot }, | |||||
{ "vlapic", STRUCT_VLAPIC, vlapic_snapshot }, | |||||
{ "vmcx", STRUCT_VMCX, vmx_vmcx_snapshot }, | |||||
{ "vatpit", STRUCT_VATPIT, vatpit_snapshot }, | |||||
{ "vatpic", STRUCT_VATPIC, vatpic_snapshot }, | |||||
{ "vpmtmr", STRUCT_VPMTMR, vpmtmr_snapshot }, | |||||
{ "vrtc", STRUCT_VRTC, vrtc_snapshot }, | |||||
}; | |||||
#endif | |||||
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; | ||||
/* | #ifdef JSON_SNAPSHOT_V2 | ||||
* TODO: Harden this function and all of its callers since 'base_str' is a user | |||||
* provided string. | static void | ||||
*/ | write_param_array(struct vm_snapshot_meta *meta, xo_handle_t *xop); | ||||
static int | |||||
vm_snapshot_dev_intern_arr(xo_handle_t *xop, int ident, int index, | |||||
struct vm_snapshot_device_info **curr_el); | |||||
static int | |||||
emit_data(xo_handle_t *xop, struct vm_snapshot_device_info *elem); | |||||
static int | |||||
create_types_hashtable(); | |||||
static int | |||||
add_device_info(struct vm_snapshot_device_info *field_info, char *field_name, | |||||
const char *arr_name, int index, volatile void *data, | |||||
char *type, size_t data_size) | |||||
{ | |||||
if (arr_name != NULL) { | |||||
field_info->intern_arr_name = strdup(arr_name); | |||||
if (field_info->intern_arr_name == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (-1); | |||||
} | |||||
} else | |||||
field_info->intern_arr_name = NULL; | |||||
field_info->field_name = strdup(field_name); | |||||
if (field_info->field_name == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (-1); | |||||
} | |||||
field_info->index = index; | |||||
if (data_size != 0 && data != NULL) { | |||||
field_info->field_data = calloc(data_size + 1, sizeof(char)); | |||||
if (field_info->field_data == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (-1); | |||||
} | |||||
memcpy(field_info->field_data, (uint8_t *)data, data_size); | |||||
field_info->data_size = data_size; | |||||
} | |||||
if (type != NULL) { | |||||
field_info->type = strdup(type); | |||||
if (field_info->type == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (-1); | |||||
} | |||||
} | |||||
return (0); | |||||
} | |||||
static int | |||||
alloc_device_info_elem(struct list_device_info *list, char *field_name, | |||||
volatile void *data, char *type, size_t data_size) | |||||
{ | |||||
const char *arr_name = NULL; | |||||
char *t; | |||||
struct vm_snapshot_device_info *aux; | |||||
int index; | |||||
int ret; | |||||
ret = 0; | |||||
aux = calloc(1, sizeof(struct vm_snapshot_device_info)); | |||||
if (aux == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (-1); | |||||
} | |||||
aux->ident = list->ident; | |||||
aux->create_instance = list->create_instance; | |||||
if (aux->ident > 0) | |||||
arr_name = list->intern_arr_names[aux->ident - 1]; | |||||
if (list->auto_index != -1) | |||||
index = list->auto_index; | |||||
else | |||||
index = list->index; | |||||
t = type; | |||||
if (list->type != NULL) | |||||
t = list->type; | |||||
ret = add_device_info(aux, field_name, arr_name, index, data, t, data_size); | |||||
if (ret != 0) | |||||
return (ret); | |||||
list->type = NULL; | |||||
if (list->first == NULL) { | |||||
list->first = aux; | |||||
list->last = list->first; | |||||
} else if (list->first == list->last) { | |||||
list->first->next_field = aux; | |||||
list->last = aux; | |||||
} else { | |||||
list->last->next_field = aux; | |||||
list->last = list->last->next_field; | |||||
} | |||||
return (ret); | |||||
} | |||||
void | |||||
remove_first_elem(struct list_device_info *list) | |||||
{ | |||||
struct vm_snapshot_device_info *aux; | |||||
aux = list->first; | |||||
list->first = aux->next_field; | |||||
free(aux); | |||||
} | |||||
void | |||||
free_device_info_list(struct list_device_info *list) | |||||
{ | |||||
struct vm_snapshot_device_info *curr_el, *aux; | |||||
curr_el = list->first; | |||||
while (curr_el != NULL) { | |||||
free(curr_el->intern_arr_name); | |||||
free(curr_el->field_name); | |||||
free(curr_el->field_data); | |||||
aux = curr_el->next_field; | |||||
free(curr_el); | |||||
curr_el = aux; | |||||
} | |||||
list->ident = 0; | |||||
memset(list->intern_arr_names, 0, IDENT_LEVEL * sizeof(char *)); | |||||
list->type = NULL; | |||||
list->first = NULL; | |||||
list->last = NULL; | |||||
} | |||||
#endif | |||||
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; | ||||
size_t base_len, ext_len; | size_t base_len, ext_len; | ||||
base_len = strnlen(base_str, NAME_MAX); | base_len = strnlen(base_str, NAME_MAX); | ||||
ext_len = strnlen(ext, NAME_MAX); | ext_len = strnlen(ext, NAME_MAX); | ||||
▲ Show 20 Lines • Show All 66 Lines • ▼ Show 20 Lines | load_vmmem_file(const char *filename, struct restore_state *rstate) | ||||
return (0); | return (0); | ||||
err_load_vmmem: | err_load_vmmem: | ||||
if (rstate->vmmem_fd > 0) | if (rstate->vmmem_fd > 0) | ||||
close(rstate->vmmem_fd); | close(rstate->vmmem_fd); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
static int | static int | ||||
load_kdata_file(const char *filename, struct restore_state *rstate) | load_kdata_file(const char *filename, struct restore_state *rstate) | ||||
{ | { | ||||
struct stat sb; | struct stat sb; | ||||
int err; | int err; | ||||
rstate->kdata_fd = open(filename, O_RDONLY); | rstate->kdata_fd = open(filename, O_RDONLY); | ||||
if (rstate->kdata_fd < 0) { | if (rstate->kdata_fd < 0) { | ||||
Show All 23 Lines | load_kdata_file(const char *filename, struct restore_state *rstate) | ||||
return (0); | return (0); | ||||
err_load_kdata: | err_load_kdata: | ||||
if (rstate->kdata_fd > 0) | if (rstate->kdata_fd > 0) | ||||
close(rstate->kdata_fd); | close(rstate->kdata_fd); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
#endif | |||||
static int | static int | ||||
load_metadata_file(const char *filename, struct restore_state *rstate) | load_metadata_file(const char *filename, struct restore_state *rstate) | ||||
{ | { | ||||
const ucl_object_t *obj; | const ucl_object_t *obj; | ||||
struct ucl_parser *parser; | struct ucl_parser *parser; | ||||
int err; | int err; | ||||
parser = ucl_parser_new(UCL_PARSER_DEFAULT); | parser = ucl_parser_new(UCL_PARSER_DEFAULT); | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | load_restore_file(const char *filename, struct restore_state *rstate) | ||||
rstate->kdata_map = MAP_FAILED; | rstate->kdata_map = MAP_FAILED; | ||||
err = load_vmmem_file(filename, rstate); | err = load_vmmem_file(filename, rstate); | ||||
if (err != 0) { | if (err != 0) { | ||||
fprintf(stderr, "Failed to load guest RAM file.\n"); | fprintf(stderr, "Failed to load guest RAM file.\n"); | ||||
goto err_restore; | goto err_restore; | ||||
} | } | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
kdata_filename = strcat_extension(filename, ".kern"); | kdata_filename = strcat_extension(filename, ".kern"); | ||||
if (kdata_filename == NULL) { | if (kdata_filename == NULL) { | ||||
fprintf(stderr, "Failed to construct kernel data filename.\n"); | fprintf(stderr, "Failed to construct kernel data filename.\n"); | ||||
goto err_restore; | goto err_restore; | ||||
} | } | ||||
err = load_kdata_file(kdata_filename, rstate); | err = load_kdata_file(kdata_filename, rstate); | ||||
if (err != 0) { | if (err != 0) { | ||||
fprintf(stderr, "Failed to load guest kernel data file.\n"); | fprintf(stderr, "Failed to load guest kernel data file.\n"); | ||||
goto err_restore; | goto err_restore; | ||||
} | } | ||||
#endif | |||||
meta_filename = strcat_extension(filename, ".meta"); | meta_filename = strcat_extension(filename, ".meta"); | ||||
if (meta_filename == NULL) { | if (meta_filename == NULL) { | ||||
fprintf(stderr, "Failed to construct kernel metadata filename.\n"); | fprintf(stderr, "Failed to construct kernel metadata filename.\n"); | ||||
goto err_restore; | goto err_restore; | ||||
} | } | ||||
err = load_metadata_file(meta_filename, rstate); | err = load_metadata_file(meta_filename, rstate); | ||||
if (err != 0) { | if (err != 0) { | ||||
Show All 35 Lines | if (obj__ == NULL) { \ | ||||
return (ret); \ | return (ret); \ | ||||
} \ | } \ | ||||
if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ | if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ | ||||
fprintf(stderr, "Cannot convert '%s' value to string.", key); \ | fprintf(stderr, "Cannot convert '%s' value to string.", key); \ | ||||
return (ret); \ | return (ret); \ | ||||
} \ | } \ | ||||
} while(0) | } while(0) | ||||
#define JSON_GET_STRING_VALUE_OR_RETURN(key, obj, result_ptr, ret) \ | |||||
do { \ | |||||
const ucl_object_t *obj__; \ | |||||
obj__ = ucl_object_lookup(obj, (key)); \ | |||||
if (obj__ == NULL) { \ | |||||
fprintf(stderr, "Missing key: '%s'", (key)); \ | |||||
return (ret); \ | |||||
} \ | |||||
if (!ucl_object_tostring_safe(obj__, result_ptr)) { \ | |||||
fprintf(stderr, "Cannot convert '%s' value to string.", (key)); \ | |||||
return (ret); \ | |||||
} \ | |||||
} while(0) | |||||
#ifdef JSON_SNAPSHOT_V2 | |||||
int | |||||
extract_type(char **type, const ucl_object_t *obj) | |||||
{ | |||||
char *key_copy = NULL; | |||||
char *aux = NULL; | |||||
const char delim[2] = "$"; | |||||
key_copy = strdup(obj->key); | |||||
assert(key_copy != NULL); | |||||
/* Param name */ | |||||
strtok(key_copy, delim); | |||||
aux = strtok(NULL, delim); | |||||
assert(aux != NULL); | |||||
*type = strdup(aux); | |||||
assert(*type != NULL); | |||||
free(key_copy); | |||||
return (0); | |||||
} | |||||
int | |||||
restore_data(const ucl_object_t *obj, struct list_device_info *list) | |||||
{ | |||||
int ret; | |||||
const char *enc_data; | |||||
const char *str_data; | |||||
char *dec_data; | |||||
int enc_bytes; | |||||
int dec_bytes; | |||||
int64_t data_size; | |||||
int64_t int_data; | |||||
char *type; | |||||
char *endptr; | |||||
ret = 0; | |||||
extract_type(&type, obj); | |||||
if (!strcmp(type, "int8") || | |||||
!strcmp(type, "uint8") || | |||||
!strcmp(type, "int16") || | |||||
!strcmp(type, "uint16") || | |||||
!strcmp(type, "int32") || | |||||
!strcmp(type, "uint32")) { | |||||
int_data = 0; | |||||
if (!ucl_object_toint_safe(obj, &int_data)) { | |||||
fprintf(stderr, "%s: Cannot convert '%s' value to int_t at line %d.\r\n", | |||||
__func__, obj->key, __LINE__); | |||||
ret = -1; | |||||
goto done; | |||||
} | |||||
alloc_device_info_elem(list, (char *)obj->key, &int_data, NULL, sizeof(int_data)); | |||||
} else if (!strcmp(type, "int64") || | |||||
!strcmp(type, "uint64")) { | |||||
str_data = NULL; | |||||
if (!ucl_object_tostring_safe(obj, &str_data)) { | |||||
fprintf(stderr, "%s: Cannot convert '%s' value to string.\r\n", | |||||
__func__, obj->key); | |||||
ret = -1; | |||||
goto done; | |||||
} | |||||
assert(str_data != NULL); | |||||
errno = 0; | |||||
int_data = (int64_t)strtoul(str_data, &endptr, 10); | |||||
if ((errno != 0) || (endptr == str_data)) { | |||||
fprintf(stderr, "%s: Cannot convert '%s' value to int.\r\n", | |||||
__func__, str_data); | |||||
ret = ((errno == 0) ? -1 : errno); | |||||
goto done; | |||||
} | |||||
alloc_device_info_elem(list, (char *)obj->key, &int_data, NULL, sizeof(int_data)); | |||||
} else if (!strcmp(type, "int64") || | |||||
!strcmp(type, "uint64")) { | |||||
sscanf(obj->value.sv, "%lx", &int_data); | |||||
alloc_device_info_elem(list, (char *)obj->key, &int_data, NULL, sizeof(int_data)); | |||||
} else { | |||||
enc_data = NULL; | |||||
if (!ucl_object_tostring_safe(obj, &enc_data)) { | |||||
fprintf(stderr, "Cannot convert '%s' value to string.\r\n", obj->key); | |||||
ret = -1; | |||||
goto done; | |||||
} | |||||
assert(enc_data != NULL); | |||||
data_size = strlen(enc_data); | |||||
enc_bytes = (data_size >> 2) * 3; | |||||
dec_data = NULL; | |||||
dec_data = malloc((enc_bytes + 2) * sizeof(char)); | |||||
assert(dec_data != NULL); | |||||
dec_bytes = EVP_DecodeBlock(dec_data, enc_data, data_size); | |||||
assert(dec_bytes > 0); | |||||
alloc_device_info_elem(list, (char *)obj->key, dec_data, NULL, (size_t)data_size); | |||||
} | |||||
done: | |||||
free(type); | |||||
return (ret); | |||||
} | |||||
int | |||||
intern_arr_restore(const char *intern_arr_name, struct list_device_info *list, | |||||
const ucl_object_t *obj) | |||||
{ | |||||
const ucl_object_t *param = NULL, *intern_obj = NULL; | |||||
ucl_object_iter_t it = NULL, iit = NULL; | |||||
int is_list; | |||||
int ret = 0; | |||||
/* Check if the received instance contains an array */ | |||||
while ((param = ucl_object_iterate(obj, &it, true)) != NULL) { | |||||
while ((intern_obj = ucl_object_iterate(param, &iit, true)) != NULL) { | |||||
is_list = (ucl_object_type(intern_obj) == UCL_ARRAY); | |||||
if (!is_list) | |||||
ret = restore_data(intern_obj, list); | |||||
else | |||||
ret = intern_arr_restore(intern_obj->key, list, intern_obj); | |||||
if (ret != 0) | |||||
goto done; | |||||
} | |||||
} | |||||
done: | |||||
return (ret); | |||||
} | |||||
static int | |||||
lookup_struct(enum snapshot_req struct_id, struct restore_state *rstate, | |||||
struct list_device_info *list) | |||||
{ | |||||
const ucl_object_t *structs = NULL, *obj = NULL; | |||||
const ucl_object_t *dev_params = NULL; | |||||
ucl_object_iter_t it = NULL; | |||||
int64_t snapshot_req; | |||||
structs = ucl_object_lookup(rstate->meta_root_obj, JSON_STRUCT_ARR_KEY); | |||||
if (structs == NULL) { | |||||
fprintf(stderr, "Failed to find '%s' object.\r\n", | |||||
JSON_STRUCT_ARR_KEY); | |||||
return (-1); | |||||
} | |||||
if (ucl_object_type((ucl_object_t *)structs) != UCL_ARRAY) { | |||||
fprintf(stderr, "Object '%s' is not an array.\r\n", | |||||
JSON_STRUCT_ARR_KEY); | |||||
return (-1); | |||||
} | |||||
while ((obj = ucl_object_iterate(structs, &it, true)) != NULL) { | |||||
snapshot_req = -1; | |||||
JSON_GET_INT_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, | |||||
&snapshot_req, -1); | |||||
assert(snapshot_req >= 0); | |||||
if ((enum snapshot_req) snapshot_req == struct_id) { | |||||
dev_params = ucl_object_lookup(obj, JSON_PARAMS_KEY); | |||||
if (dev_params == NULL) { | |||||
fprintf(stderr, "Failed to find '%s' object.\r\n", | |||||
JSON_PARAMS_KEY); | |||||
return(-EINVAL); | |||||
} | |||||
if (ucl_object_type((ucl_object_t *)dev_params) != UCL_ARRAY) { | |||||
fprintf(stderr, "Object '%s' is not an array.\r\n", | |||||
JSON_PARAMS_KEY); | |||||
return (-EINVAL); | |||||
} | |||||
/* Iterate through device parameters */ | |||||
intern_arr_restore(JSON_PARAMS_KEY, list, dev_params); | |||||
return (0); | |||||
} | |||||
} | |||||
return (-1); | |||||
} | |||||
int | |||||
lookup_check_dev(const char *dev_name, struct restore_state *rstate, | |||||
const ucl_object_t *obj, | |||||
struct list_device_info *list) | |||||
{ | |||||
const ucl_object_t *dev_params = NULL; | |||||
const char *snapshot_req; | |||||
snapshot_req = NULL; | |||||
JSON_GET_STRING_OR_RETURN(JSON_SNAPSHOT_REQ_KEY, obj, | |||||
&snapshot_req, -EINVAL); | |||||
if (snapshot_req == NULL) { | |||||
fprintf(stderr, "%s: Could not extract device name\r\n", __func__); | |||||
return (-1); | |||||
} | |||||
if (!strcmp(snapshot_req, dev_name)) { | |||||
dev_params = ucl_object_lookup(obj, JSON_PARAMS_KEY); | |||||
if (dev_params == NULL) { | |||||
fprintf(stderr, "Failed to find '%s' object.\n", | |||||
JSON_PARAMS_KEY); | |||||
return(-EINVAL); | |||||
} | |||||
if (ucl_object_type((ucl_object_t *)dev_params) != UCL_ARRAY) { | |||||
fprintf(stderr, "Object '%s' is not an array.\n", | |||||
JSON_PARAMS_KEY); | |||||
return (-EINVAL); | |||||
} | |||||
/* Iterate through device parameters */ | |||||
intern_arr_restore(JSON_PARAMS_KEY, list, dev_params); | |||||
return (0); | |||||
} | |||||
return (-1); | |||||
} | |||||
int | |||||
lookup_dev(const char *dev_name, struct restore_state *rstate, | |||||
struct list_device_info *list) | |||||
{ | |||||
const ucl_object_t *devs = NULL, *obj = NULL; | |||||
ucl_object_iter_t it = NULL; | |||||
int ret; | |||||
devs = ucl_object_lookup(rstate->meta_root_obj, JSON_DEV_ARR_KEY); | |||||
if (devs == NULL) { | |||||
fprintf(stderr, "Failed to find '%s' object.\n", | |||||
JSON_DEV_ARR_KEY); | |||||
return (-EINVAL); | |||||
} | |||||
if (ucl_object_type((ucl_object_t *)devs) != UCL_ARRAY) { | |||||
fprintf(stderr, "Object '%s' is not an array.\n", | |||||
JSON_DEV_ARR_KEY); | |||||
return (-EINVAL); | |||||
} | |||||
while ((obj = ucl_object_iterate(devs, &it, true)) != NULL) { | |||||
ret = lookup_check_dev(dev_name, rstate, obj, list); | |||||
if (ret == 0) | |||||
return (ret); | |||||
} | |||||
return (-1); | |||||
} | |||||
#else | |||||
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; | ||||
▲ Show 20 Lines • Show All 86 Lines • ▼ Show 20 Lines | while ((obj = ucl_object_iterate(devs, &it, true)) != NULL) { | ||||
ret = lookup_check_dev(dev_name, rstate, obj, data_size); | ret = lookup_check_dev(dev_name, rstate, obj, data_size); | ||||
if (ret != NULL) | if (ret != NULL) | ||||
return (ret); | return (ret); | ||||
} | } | ||||
return (NULL); | return (NULL); | ||||
} | } | ||||
#endif | |||||
static const ucl_object_t * | static const ucl_object_t * | ||||
lookup_basic_metadata_object(struct restore_state *rstate) | lookup_basic_metadata_object(struct restore_state *rstate) | ||||
{ | { | ||||
const ucl_object_t *basic_meta_obj = NULL; | const ucl_object_t *basic_meta_obj = NULL; | ||||
basic_meta_obj = ucl_object_lookup(rstate->meta_root_obj, | basic_meta_obj = ucl_object_lookup(rstate->meta_root_obj, | ||||
JSON_BASIC_METADATA_KEY); | JSON_BASIC_METADATA_KEY); | ||||
if (basic_meta_obj == NULL) { | if (basic_meta_obj == NULL) { | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | lookup_memsize(struct restore_state *rstate) | ||||
return ((size_t)memsize); | return ((size_t)memsize); | ||||
} | } | ||||
int | int | ||||
lookup_guest_ncpus(struct restore_state *rstate) | lookup_guest_ncpus(struct restore_state *rstate) | ||||
{ | { | ||||
int64_t ncpus; | int64_t ncpus; | ||||
const ucl_object_t *obj; | const ucl_object_t *obj; obj = lookup_basic_metadata_object(rstate); | ||||
obj = lookup_basic_metadata_object(rstate); | |||||
if (obj == NULL) | if (obj == NULL) | ||||
return (0); | return (0); | ||||
JSON_GET_INT_OR_RETURN(JSON_NCPUS_KEY, obj, &ncpus, 0); | JSON_GET_INT_OR_RETURN(JSON_NCPUS_KEY, obj, &ncpus, 0); | ||||
return ((int)ncpus); | return ((int)ncpus); | ||||
} | } | ||||
static void | static void | ||||
▲ Show 20 Lines • Show All 251 Lines • ▼ Show 20 Lines | restored = vm_snapshot_mem(ctx, rstate->vmmem_fd, rstate->vmmem_len, | ||||
false); | false); | ||||
if (restored != rstate->vmmem_len) | if (restored != rstate->vmmem_len) | ||||
return (-1); | return (-1); | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
static int | static int | ||||
vm_restore_kern_struct(struct vmctx *ctx, struct restore_state *rstate, | vm_restore_kern_struct(struct vmctx *ctx, struct restore_state *rstate, | ||||
const struct vm_snapshot_kern_info *info) | const struct vm_snapshot_kern_info *info) | ||||
{ | { | ||||
void *struct_ptr; | |||||
size_t struct_size; | |||||
int ret; | int ret; | ||||
struct list_device_info list; | |||||
struct vm_snapshot_meta *meta; | struct vm_snapshot_meta *meta; | ||||
void *buffer; | |||||
size_t buf_size; | |||||
struct_ptr = lookup_struct(info->req, rstate, &struct_size); | buf_size = SNAPSHOT_BUFFER_SIZE; | ||||
if (struct_ptr == NULL) { | |||||
buffer = calloc(1, buf_size); | |||||
if (buffer == NULL) { | |||||
perror("Failed to allocate memory for snapshot buffer"); | |||||
ret = ENOSPC; | |||||
goto done; | |||||
} | |||||
memset(&list, 0, sizeof(list)); | |||||
list.first = NULL; | |||||
list.last = NULL; | |||||
ret = lookup_struct(info->req, rstate, &list); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "%s: Failed to lookup struct %s\r\n", | fprintf(stderr, "%s: Failed to lookup struct %s\r\n", | ||||
__func__, info->struct_name); | __func__, info->struct_name); | ||||
ret = -1; | |||||
goto done; | goto done; | ||||
} | } | ||||
if (struct_size == 0) { | if (list.first == NULL) { | ||||
fprintf(stderr, "%s: Kernel struct size was 0 for: %s\r\n", | fprintf(stderr, "%s: Kernel struct size was 0 for: %s\r\n", | ||||
__func__, info->struct_name); | __func__, info->struct_name); | ||||
ret = -1; | ret = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
meta = &(struct vm_snapshot_meta) { | meta = &(struct vm_snapshot_meta) { | ||||
.ctx = ctx, | .ctx = ctx, | ||||
.dev_name = info->struct_name, | .dev_name = info->struct_name, | ||||
.dev_req = info->req, | .dev_req = info->req, | ||||
.buffer.buf_start = struct_ptr, | .buffer.buf_start = buffer, | ||||
.buffer.buf_size = struct_size, | .buffer.buf_size = buf_size, | ||||
.buffer.buf = buffer, | |||||
.buffer.buf_rem = buf_size, | |||||
.buffer.buf = struct_ptr, | .op = VM_SNAPSHOT_RESTORE, | ||||
.buffer.buf_rem = struct_size, | .version = JSON_V2, | ||||
.dev_info_list.ident = 0, | |||||
.dev_info_list.first = list.first, | |||||
.dev_info_list.last = list.last, | |||||
.snapshot_kernel = 1, | |||||
}; | |||||
ret = (*info->snapshot_cb)(meta); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Failed to restore dev: %s\r\n", | |||||
info->struct_name); | |||||
return (-1); | |||||
} | |||||
meta->buffer.buf = meta->buffer.buf_start; | |||||
ret = vm_snapshot_req(meta); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "%s: Failed to restore struct: %s\r\n", | |||||
__func__, info->struct_name); | |||||
goto done; | |||||
} | |||||
done: | |||||
return (ret); | |||||
} | |||||
#else | |||||
static int | |||||
vm_restore_kern_struct(struct vmctx *ctx, struct restore_state *rstate, | |||||
const struct vm_snapshot_kern_info *info) | |||||
{ | |||||
int ret; | |||||
struct list_device_info list; | |||||
struct vm_snapshot_meta *meta; | |||||
void *buffer; | |||||
size_t buf_size; | |||||
buf_size = SNAPSHOT_BUFFER_SIZE; | |||||
buffer = calloc(1, buf_size); | |||||
if (buffer == NULL) { | |||||
perror("Failed to allocate memory for snapshot buffer"); | |||||
ret = ENOSPC; | |||||
goto done; | |||||
} | |||||
memset(&list, 0, sizeof(list)); | |||||
list.first = NULL; | |||||
list.last = NULL; | |||||
ret = lookup_struct(info->req, rstate, &list); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "%s: Failed to lookup struct %s\r\n", | |||||
__func__, info->struct_name); | |||||
goto done; | |||||
} | |||||
if (list.first == 0) { | |||||
fprintf(stderr, "%s: Kernel struct size was 0 for: %s\r\n", | |||||
__func__, info->struct_name); | |||||
ret = -1; | |||||
goto done; | |||||
} | |||||
meta = &(struct vm_snapshot_meta) { | |||||
.ctx = ctx, | |||||
.dev_name = info->struct_name, | |||||
.dev_req = info->req, | |||||
.buffer.buf_start = buffer, | |||||
.buffer.buf_size = buf_size, | |||||
.buffer.buf = buffer, | |||||
.buffer.buf_rem = buf_size, | |||||
.op = VM_SNAPSHOT_RESTORE, | .op = VM_SNAPSHOT_RESTORE, | ||||
.version = JSON_V1, | |||||
}; | }; | ||||
ret = vm_snapshot_req(meta); | ret = vm_snapshot_req(meta); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "%s: Failed to restore struct: %s\r\n", | fprintf(stderr, "%s: Failed to restore struct: %s\r\n", | ||||
__func__, info->struct_name); | __func__, info->struct_name); | ||||
goto done; | goto done; | ||||
} | } | ||||
done: | done: | ||||
return (ret); | return (ret); | ||||
} | } | ||||
#endif | |||||
int | int | ||||
vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) | vm_restore_kern_structs(struct vmctx *ctx, struct restore_state *rstate) | ||||
{ | { | ||||
int ret; | int ret; | ||||
int i; | int i; | ||||
for (i = 0; i < nitems(snapshot_kern_structs); i++) { | for (i = 0; i < nitems(snapshot_kern_structs); i++) { | ||||
ret = vm_restore_kern_struct(ctx, rstate, | ret = vm_restore_kern_struct(ctx, rstate, | ||||
&snapshot_kern_structs[i]); | &snapshot_kern_structs[i]); | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
int | int | ||||
vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, | vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, | ||||
const struct vm_snapshot_dev_info *info) | const struct vm_snapshot_dev_info *info) | ||||
{ | { | ||||
int ret; | |||||
struct list_device_info list; | |||||
struct vm_snapshot_meta *meta; | |||||
memset(&list, 0, sizeof(list)); | |||||
list.first = NULL; | |||||
list.last = NULL; | |||||
ret = lookup_dev(info->dev_name, rstate, &list); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Failed to lookup dev: %s\r\n", info->dev_name); | |||||
fprintf(stderr, "Continuing the restore/migration process\r\n"); | |||||
return (0); | |||||
} | |||||
meta = &(struct vm_snapshot_meta) { | |||||
.ctx = ctx, | |||||
.dev_name = info->dev_name, | |||||
.op = VM_SNAPSHOT_RESTORE, | |||||
.version = JSON_V2, | |||||
.dev_info_list.ident = 0, | |||||
.dev_info_list.first = list.first, | |||||
.dev_info_list.last = list.last, | |||||
}; | |||||
ret = (*info->snapshot_cb)(meta); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Failed to restore dev: %s\r\n", | |||||
info->dev_name); | |||||
return (-1); | |||||
} | |||||
return (0); | |||||
} | |||||
#else | |||||
int | |||||
vm_restore_user_dev(struct vmctx *ctx, struct restore_state *rstate, | |||||
const struct vm_snapshot_dev_info *info) | |||||
{ | |||||
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(info->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\r\n", info->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. " | ||||
"Assuming %s is not used\r\n", | "Assuming %s is not used\r\n", | ||||
__func__, info->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 = info->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, | ||||
.version = JSON_V1, | |||||
}; | }; | ||||
ret = (*info->snapshot_cb)(meta); | ret = (*info->snapshot_cb)(meta); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to restore dev: %s\r\n", | fprintf(stderr, "Failed to restore dev: %s\r\n", | ||||
info->dev_name); | info->dev_name); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
#endif | |||||
int | int | ||||
vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate) | vm_restore_user_devs(struct vmctx *ctx, struct restore_state *rstate) | ||||
{ | { | ||||
int ret; | int ret; | ||||
int i; | int i; | ||||
for (i = 0; i < nitems(snapshot_devs); i++) { | for (i = 0; i < nitems(snapshot_devs); i++) { | ||||
ret = vm_restore_user_dev(ctx, rstate, &snapshot_devs[i]); | ret = vm_restore_user_dev(ctx, rstate, &snapshot_devs[i]); | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | return (ret); | ||||
fprintf(stderr, "%s restored successfully\r\n", snapshot_devs[i].dev_name); | |||||
} | } | ||||
return 0; | return 0; | ||||
} | } | ||||
int | int | ||||
vm_pause_user_devs(struct vmctx *ctx) | vm_pause_user_devs(struct vmctx *ctx) | ||||
{ | { | ||||
Show All 30 Lines | for (i = 0; i < nitems(snapshot_devs); i++) { | ||||
if (ret != 0) | if (ret != 0) | ||||
return (ret); | 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_snapshot_kern_struct(const struct vm_snapshot_kern_info *info, | ||||
int data_fd, xo_handle_t *xop, const char *array_key, | |||||
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) { | ||||
fprintf(stderr, "%s: Failed to snapshot struct %s\r\n", | fprintf(stderr, "%s: Failed to snapshot struct %s\r\n", | ||||
__func__, meta->dev_name); | __func__, meta->dev_name); | ||||
ret = -1; | ret = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
if (meta->version == JSON_V1) { | |||||
data_size = vm_get_snapshot_size(meta); | data_size = vm_get_snapshot_size(meta); | ||||
write_cnt = write(data_fd, meta->buffer.buf_start, data_size); | write_cnt = write(data_fd, meta->buffer.buf_start, data_size); | ||||
if (write_cnt != data_size) { | if (write_cnt != data_size) { | ||||
perror("Failed to write all snapshotted data."); | perror("Failed to write all snapshotted data."); | ||||
ret = -1; | ret = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
} | |||||
meta->buffer.buf = meta->buffer.buf_start; | |||||
fprintf(stderr, "%s: %s has size %ld\r\n", __func__, meta->dev_name, data_size); | |||||
if (!strcmp(meta->dev_name, "vhpet")) | |||||
vhpet_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vm")) | |||||
vm_snapshot_vm(meta); | |||||
else if (!strcmp(meta->dev_name, "vlapic")) | |||||
vlapic_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vioapic")) | |||||
vioapic_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vatpit")) | |||||
vatpit_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vatpic")) | |||||
vatpic_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vpmtmr")) | |||||
vpmtmr_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vrtc")) | |||||
vrtc_snapshot(meta); | |||||
else if (!strcmp(meta->dev_name, "vmx")) | |||||
vmx_snapshot(meta); | |||||
if (meta->version == JSON_V1) { | |||||
data_size = vm_get_snapshot_size(meta); | |||||
write_cnt = write(data_fd, meta->buffer.buf_start, data_size); | |||||
if (write_cnt != data_size) { | |||||
perror("Failed to write all snapshotted data."); | |||||
ret = -1; | |||||
goto done; | |||||
} | |||||
} | |||||
meta->buffer.buf = meta->buffer.buf_start; | |||||
/* 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); | if (meta->version == JSON_V1) { | ||||
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); | |||||
*offset += data_size; | *offset += data_size; | ||||
} else if (meta->version == JSON_V2) { | |||||
ret = (*info->snapshot_cb)(meta); | |||||
if (ret != 0) { | |||||
fprintf(stderr, "Failed to restore dev: %s\r\n", | |||||
info->struct_name); | |||||
return (-1); | |||||
} | |||||
write_param_array(meta, xop); | |||||
} | |||||
xo_close_instance_h(xop, array_key); | |||||
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_snapshot_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | ||||
{ | { | ||||
int ret, i, error; | int ret, i, error; | ||||
Show All 14 Lines | vm_snapshot_kern_structs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | ||||
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, | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
.version = JSON_V2, | |||||
.dev_info_list.ident = 0, | |||||
.dev_info_list.index = -1, | |||||
.dev_info_list.type = NULL, | |||||
.dev_info_list.create_instance = 1, | |||||
.dev_info_list.auto_index = -1, | |||||
.dev_info_list.first = NULL, | |||||
.dev_info_list.last = NULL, | |||||
.snapshot_kernel = 1, | |||||
#else | |||||
.version = JSON_V1, | |||||
#endif | |||||
}; | }; | ||||
/* Prepare types hashtable */ | |||||
ret = create_types_hashtable(); | |||||
if (ret != 0) { | |||||
error = -1; | |||||
goto err_vm_snapshot_kern_data; | |||||
} | |||||
/* Prepare types hashtable */ | |||||
ret = create_types_hashtable(); | |||||
if (ret != 0) { | |||||
error = -1; | |||||
goto err_vm_snapshot_kern_data; | |||||
} | |||||
xo_open_list_h(xop, JSON_STRUCT_ARR_KEY); | xo_open_list_h(xop, JSON_STRUCT_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, | if (meta->version == JSON_V2) { | ||||
meta, &offset); | free_device_info_list(&meta->dev_info_list); | ||||
meta->snapshot_kernel = 1; | |||||
} | |||||
ret = vm_snapshot_kern_struct(&snapshot_kern_structs[i], data_fd, | |||||
xop, JSON_STRUCT_ARR_KEY, 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_STRUCT_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_snapshot_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)); | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
xo_emit_h(xop, "{:" JSON_VERSION_KEY "/%d}\n", JSON_V1); | |||||
#else | |||||
xo_emit_h(xop, "{:" JSON_VERSION_KEY "/%d}\n", JSON_V2); | |||||
#endif | |||||
xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); | xo_close_container_h(xop, JSON_BASIC_METADATA_KEY); | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef JSON_SNAPSHOT_V2 | |||||
static int | static int | ||||
create_indexed_arr_name(char *intern_arr, int number, char **indexed_name) | |||||
{ | |||||
int ret; | |||||
ret = asprintf(indexed_name, "%s@%d", intern_arr, number); | |||||
if (ret < 0) | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (ret); | |||||
} | |||||
static int | |||||
create_type_info(struct type_info **ti, const char *name, | |||||
const char *fmt_str, unsigned char size) | |||||
{ | |||||
int ret; | |||||
ret = 0; | |||||
*ti = calloc(1, sizeof(struct type_info)); | |||||
if (*ti == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
ret = ENOMEM; | |||||
} | |||||
strcpy((*ti)->type_name, name); | |||||
strcpy((*ti)->fmt_str, fmt_str); | |||||
(*ti)->size = size; | |||||
return (ret); | |||||
} | |||||
static int | |||||
create_types_hashtable() | |||||
{ | |||||
int ret, i, j; | |||||
struct type_info *ti; | |||||
ENTRY item; | |||||
ENTRY *res = NULL; | |||||
const char *types[] = { "int8", "uint8", "int16", "uint16", | |||||
"int32", "uint32", "int64", "uint64" }; | |||||
const char *fmt_strs[] = { "/%%hhd}\\n", "/%%hhu}\\n", "/%%hd}\\n", | |||||
"/%%hu}\\n", "/%%d}\\n", "/%%u}\\n", "/%%s}\\n", "/%%s}\\n" }; | |||||
const unsigned char type_sizes[] = { sizeof(int8_t), sizeof(uint8_t), | |||||
sizeof(int16_t), sizeof(uint16_t), | |||||
sizeof(int32_t), sizeof(uint32_t), | |||||
sizeof(int64_t), sizeof(uint64_t) }; | |||||
ret = 0; | |||||
types_htable = calloc(1, sizeof(*types_htable)); | |||||
if (types_htable == NULL) { | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
ret = ENOMEM; | |||||
goto done; | |||||
} | |||||
if (!hcreate_r(32, types_htable)) { | |||||
ret = errno; | |||||
goto done; | |||||
} | |||||
for (i = 0; i < 8; ++i) { | |||||
ret = create_type_info(&ti, types[i], fmt_strs[i], type_sizes[i]); | |||||
if (ret != 0) { | |||||
j = i; | |||||
goto done; | |||||
} | |||||
item.key = (char *)ti->type_name; | |||||
item.data = ti; | |||||
if (!hsearch_r(item, ENTER, &res, types_htable)) { | |||||
j = i; | |||||
fprintf(stderr, "%s: Could not add data into hashtable(line %d)\r\n", | |||||
__func__, __LINE__); | |||||
ret = errno; | |||||
goto done; | |||||
} | |||||
} | |||||
return (ret); | |||||
done: | |||||
free(types_htable); | |||||
types_htable = NULL; | |||||
for (i = 0; i < j; ++i) { | |||||
item.key = (char *)types[i]; | |||||
if (!hsearch_r(item, FIND, &res, types_htable)) { | |||||
fprintf(stderr, | |||||
"%s: Could not find key %s in hashtable(line %d)\r\n", | |||||
__func__, item.key, __LINE__); | |||||
continue; | |||||
} | |||||
free(res->data); | |||||
} | |||||
hdestroy_r(types_htable); | |||||
return (ret); | |||||
} | |||||
static void | |||||
destroy_types_hashtable() | |||||
{ | |||||
int i; | |||||
ENTRY item; | |||||
ENTRY *res = NULL; | |||||
const char *types[] = { "int8", "uint8", "int16", "uint16", | |||||
"int32", "uint32", "int64", "uint64" }; | |||||
for (i = 0; i < 8; ++i) { | |||||
item.key = (char *)types[i]; | |||||
if (!hsearch_r(item, FIND, &res, types_htable)) { | |||||
fprintf(stderr, | |||||
"%s: Could not find key %s in hashtable(line %d)\r\n", | |||||
__func__, item.key, __LINE__); | |||||
continue; | |||||
} | |||||
free(res->data); | |||||
} | |||||
hdestroy_r(types_htable); | |||||
} | |||||
static int | |||||
get_type_format_string(char **res, char *key_part, char *type) | |||||
{ | |||||
int ret; | |||||
struct type_info *ti; | |||||
ENTRY item; | |||||
ENTRY *ires = NULL; | |||||
item.key = type; | |||||
if (hsearch_r(item, FIND, &ires, types_htable)) { | |||||
ti = (struct type_info *)(ires->data); | |||||
ret = asprintf(res, "%s%s", key_part, ti->fmt_str); | |||||
} else | |||||
ret = asprintf(res, "%s%s", key_part, "/%%s}\\n"); | |||||
if (ret < 0) | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", __func__, __LINE__); | |||||
return (ret); | |||||
} | |||||
static int | |||||
create_key_string(struct vm_snapshot_device_info *elem, char **res_str) | |||||
{ | |||||
int ret; | |||||
char *fmt = NULL; | |||||
ret = 0; | |||||
if (!elem->create_instance && (elem->index != -1)) { | |||||
ret = get_type_format_string(&fmt, "{:%s%d$%s", elem->type); | |||||
ret = asprintf(res_str, fmt, elem->field_name, elem->index, elem->type); | |||||
} else { | |||||
ret = get_type_format_string(&fmt, "{:%s$%s", elem->type); | |||||
ret = asprintf(res_str, fmt, elem->field_name, elem->type); | |||||
} | |||||
free(fmt); | |||||
return (ret); | |||||
} | |||||
static int | |||||
emit_data(xo_handle_t *xop, struct vm_snapshot_device_info *elem) | |||||
{ | |||||
int ret; | |||||
char *enc_data = NULL; | |||||
char *fmt; | |||||
char *lv_str; | |||||
int enc_bytes = 0; | |||||
uint64_t int_data; | |||||
unsigned long ds; | |||||
ENTRY item; | |||||
ENTRY *res = NULL; | |||||
ret = 0; | |||||
create_key_string(elem, &fmt); | |||||
item.key = elem->type; | |||||
if (hsearch_r(item, FIND, &res, types_htable)) { | |||||
memcpy(&int_data, elem->field_data, | |||||
((struct type_info *)res->data)->size); | |||||
lv_str = NULL; | |||||
if (!strcmp(elem->type, "int64")) | |||||
ret = asprintf(&lv_str, "%ld", int_data); | |||||
else if (!strcmp(elem->type, "uint64")) | |||||
ret = asprintf(&lv_str, "%lu", int_data); | |||||
if (ret < 0) | |||||
goto done; | |||||
if (lv_str != NULL) | |||||
xo_emit_h(xop, fmt, lv_str); | |||||
else | |||||
xo_emit_h(xop, fmt, int_data); | |||||
} else { | |||||
ds = elem->data_size; | |||||
enc_data = malloc(4 * (ds + 2) / 3); | |||||
assert(enc_data != NULL); | |||||
enc_bytes = EVP_EncodeBlock(enc_data, (const char *)elem->field_data, ds); | |||||
assert(enc_bytes != 0); | |||||
xo_emit_h(xop, fmt, enc_data); | |||||
free(enc_data); | |||||
} | |||||
done: | |||||
free(fmt); | |||||
return (ret); | |||||
} | |||||
static int | |||||
vm_snapshot_dev_intern_arr_index(xo_handle_t *xop, int ident, int index, | |||||
struct vm_snapshot_device_info **curr_el) | |||||
{ | |||||
char *intern_arr = NULL; | |||||
char *indexed_name = NULL; | |||||
int ret = 0; | |||||
intern_arr = (*curr_el)->intern_arr_name; | |||||
create_indexed_arr_name(intern_arr, index, &indexed_name); | |||||
xo_open_list_h(xop, indexed_name); | |||||
xo_open_instance_h(xop, indexed_name); | |||||
while (*curr_el != NULL) { | |||||
/* Check if there is an internal array */ | |||||
if ((*curr_el)->ident > ident) { | |||||
ret = vm_snapshot_dev_intern_arr(xop, (*curr_el)->ident, (*curr_el)->index, curr_el); | |||||
continue; | |||||
} | |||||
/* Check if index changed and if there is no array at the same | |||||
* indentation level as the current one for this index */ | |||||
if ((index != (*curr_el)->index) && (ret == 0)) | |||||
break; | |||||
/* Reset the return value for the first branch inside the loop */ | |||||
ret = 0; | |||||
/* Write data */ | |||||
emit_data(xop, *curr_el); | |||||
*curr_el = (*curr_el)->next_field; | |||||
} | |||||
xo_close_instance_h(xop, indexed_name); | |||||
xo_close_list_h(xop, indexed_name); | |||||
free(indexed_name); | |||||
indexed_name = NULL; | |||||
return (ret); | |||||
} | |||||
static int | |||||
vm_snapshot_dev_intern_arr(xo_handle_t *xop, int ident, int index, | |||||
struct vm_snapshot_device_info **curr_el) | |||||
{ | |||||
char *intern_arr = NULL; | |||||
int ret = 0; | |||||
intern_arr = (*curr_el)->intern_arr_name; | |||||
xo_open_list_h(xop, intern_arr); | |||||
xo_open_instance_h(xop, intern_arr); | |||||
while (*curr_el != NULL) { | |||||
/* Check if the current array has no more elements */ | |||||
if ((*curr_el)->ident < ident) | |||||
break; | |||||
/* Check if there is an array on the same indentation level */ | |||||
if (strcmp((*curr_el)->intern_arr_name, intern_arr) && | |||||
(*curr_el)->ident == ident && | |||||
ret == 0) { | |||||
ret = 1; | |||||
break; | |||||
} | |||||
/* Check if there is an internal array */ | |||||
if ((*curr_el)->ident > ident) { | |||||
ret = vm_snapshot_dev_intern_arr(xop, (*curr_el)->ident, (*curr_el)->index, curr_el); | |||||
continue; | |||||
} | |||||
/* Check if for the current array indexing is present */ | |||||
if (((*curr_el)->index != -1) && ((*curr_el)->create_instance == 1)) { | |||||
vm_snapshot_dev_intern_arr_index(xop, (*curr_el)->ident, (*curr_el)->index, curr_el); | |||||
continue; | |||||
} | |||||
ret = 0; | |||||
/* Write data inside the array */ | |||||
emit_data(xop, *curr_el); | |||||
*curr_el = (*curr_el)->next_field; | |||||
} | |||||
xo_close_instance_h(xop, intern_arr); | |||||
xo_close_list_h(xop, intern_arr); | |||||
return (ret); | |||||
} | |||||
static void | |||||
write_param_array(struct vm_snapshot_meta *meta, xo_handle_t *xop) | |||||
{ | |||||
struct vm_snapshot_device_info *curr_el; | |||||
curr_el = meta->dev_info_list.first; | |||||
meta->dev_info_list.ident = 0; | |||||
xo_open_list_h(xop, JSON_PARAMS_KEY); | |||||
xo_open_instance_h(xop, JSON_PARAMS_KEY); | |||||
while (curr_el != NULL) { | |||||
if (curr_el->ident > meta->dev_info_list.ident) { | |||||
vm_snapshot_dev_intern_arr(xop, curr_el->ident, curr_el->index, &curr_el); | |||||
continue; | |||||
} | |||||
emit_data(xop, curr_el); | |||||
curr_el = curr_el->next_field; | |||||
} | |||||
xo_close_instance_h(xop, JSON_PARAMS_KEY); | |||||
xo_close_list_h(xop, JSON_PARAMS_KEY); | |||||
} | |||||
#endif | |||||
static int | |||||
vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, | vm_snapshot_dev_write_data(int data_fd, xo_handle_t *xop, const char *array_key, | ||||
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; | ||||
if (meta->version == JSON_V1) { | |||||
data_size = vm_get_snapshot_size(meta); | data_size = vm_get_snapshot_size(meta); | ||||
ret = write(data_fd, meta->buffer.buf_start, data_size); | ret = write(data_fd, meta->buffer.buf_start, data_size); | ||||
if (ret != data_size) { | if (ret != data_size) { | ||||
perror("Failed to write all snapshotted data."); | perror("Failed to write all snapshotted data."); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
*offset += data_size; | |||||
} | |||||
/* Write metadata. */ | /* Write metadata. */ | ||||
xo_open_instance_h(xop, array_key); | xo_open_instance_h(xop, array_key); | ||||
xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); | xo_emit_h(xop, "{:" JSON_SNAPSHOT_REQ_KEY "/%s}\n", meta->dev_name); | ||||
if (meta->version == JSON_V1) { | |||||
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, array_key); | } else if (meta->version == JSON_V2) | ||||
write_param_array(meta, xop); | |||||
*offset += data_size; | xo_close_instance_h(xop, array_key); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, | vm_snapshot_user_dev(const struct vm_snapshot_dev_info *info, | ||||
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); | ret = (*info->snapshot_cb)(meta); | ||||
if (ret != 0) { | if (ret != 0) { | ||||
fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", | fprintf(stderr, "Failed to snapshot %s; ret=%d\r\n", | ||||
meta->dev_name, ret); | meta->dev_name, ret); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
if (meta->version == JSON_V2) | |||||
if (meta->dev_info_list.first == NULL) | |||||
return (0); | |||||
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_snapshot_user_devs(struct vmctx *ctx, int data_fd, xo_handle_t *xop) | ||||
{ | { | ||||
int ret, i; | int ret, i; | ||||
off_t offset; | off_t offset; | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
void *buffer; | void *buffer; | ||||
size_t buf_size; | size_t buf_size; | ||||
#endif | |||||
struct vm_snapshot_meta *meta; | struct vm_snapshot_meta *meta; | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
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); | ||||
} | } | ||||
buffer = malloc(buf_size); | buffer = malloc(buf_size); | ||||
if (buffer == NULL) { | if (buffer == NULL) { | ||||
perror("Failed to allocate memory for snapshot buffer"); | perror("Failed to allocate memory for snapshot buffer"); | ||||
ret = ENOSPC; | ret = ENOSPC; | ||||
goto snapshot_err; | goto snapshot_err; | ||||
} | } | ||||
#endif | |||||
offset = 0; | |||||
meta = &(struct vm_snapshot_meta) { | meta = &(struct vm_snapshot_meta) { | ||||
.ctx = ctx, | .ctx = ctx, | ||||
.op = VM_SNAPSHOT_SAVE, | |||||
#ifndef JSON_SNAPSHOT_V2 | |||||
.buffer.buf_start = buffer, | .buffer.buf_start = buffer, | ||||
.buffer.buf_size = buf_size, | .buffer.buf_size = buf_size, | ||||
.version = JSON_V1, | |||||
.op = VM_SNAPSHOT_SAVE, | #else | ||||
.version = JSON_V2, | |||||
.dev_info_list.ident = 0, | |||||
.dev_info_list.index = -1, | |||||
.dev_info_list.create_instance = 1, | |||||
.dev_info_list.auto_index = -1, | |||||
.dev_info_list.first = NULL, | |||||
.dev_info_list.last = NULL, | |||||
#endif | |||||
}; | }; | ||||
/* Prepare the hashtable for types */ | |||||
ret = create_types_hashtable(); | |||||
if (ret != 0) | |||||
goto snapshot_err; | |||||
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++) { | for (i = 0; i < nitems(snapshot_devs); i++) { | ||||
fprintf(stderr, "Creating snapshot for %s device\r\n", snapshot_devs[i].dev_name); | |||||
meta->dev_name = snapshot_devs[i].dev_name; | meta->dev_name = snapshot_devs[i].dev_name; | ||||
if (meta->version == JSON_V1) { | |||||
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; | ||||
} else if (meta->version == JSON_V2) | |||||
free_device_info_list(&meta->dev_info_list); | |||||
if (meta->version == JSON_V2) | |||||
free_device_info_list(&meta->dev_info_list); | |||||
ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, | ret = vm_snapshot_user_dev(&snapshot_devs[i], data_fd, xop, | ||||
meta, &offset); | 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); | ||||
/* Clear types hashtable */ | |||||
destroy_types_hashtable(); | |||||
snapshot_err: | snapshot_err: | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
if (buffer != NULL) | if (buffer != NULL) | ||||
free(buffer); | free(buffer); | ||||
#endif | |||||
return (ret); | return (ret); | ||||
} | } | ||||
void | void | ||||
checkpoint_cpu_add(int vcpu) | checkpoint_cpu_add(int vcpu) | ||||
{ | { | ||||
pthread_mutex_lock(&vcpu_lock); | pthread_mutex_lock(&vcpu_lock); | ||||
▲ Show 20 Lines • Show All 67 Lines • ▼ Show 20 Lines | |||||
vm_checkpoint(struct vmctx *ctx, const char *checkpoint_file, bool stop_vm) | vm_checkpoint(struct vmctx *ctx, const char *checkpoint_file, bool stop_vm) | ||||
{ | { | ||||
int fd_checkpoint = 0, kdata_fd = 0; | int fd_checkpoint = 0, kdata_fd = 0; | ||||
int ret = 0; | int ret = 0; | ||||
int error = 0; | int error = 0; | ||||
size_t memsz; | size_t memsz; | ||||
xo_handle_t *xop = NULL; | xo_handle_t *xop = NULL; | ||||
char *meta_filename = NULL; | char *meta_filename = NULL; | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
char *kdata_filename = NULL; | char *kdata_filename = NULL; | ||||
#endif | |||||
FILE *meta_file = NULL; | FILE *meta_file = NULL; | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
kdata_filename = strcat_extension(checkpoint_file, ".kern"); | kdata_filename = strcat_extension(checkpoint_file, ".kern"); | ||||
if (kdata_filename == NULL) { | if (kdata_filename == NULL) { | ||||
fprintf(stderr, "Failed to construct kernel data filename.\n"); | fprintf(stderr, "Failed to construct kernel data filename.\n"); | ||||
return (-1); | return (-1); | ||||
} | } | ||||
kdata_fd = open(kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); | kdata_fd = open(kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0700); | ||||
if (kdata_fd < 0) { | if (kdata_fd < 0) { | ||||
perror("Failed to open kernel data snapshot file."); | perror("Failed to open kernel data snapshot file."); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
#endif | |||||
fd_checkpoint = open(checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); | fd_checkpoint = open(checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); | ||||
if (fd_checkpoint < 0) { | if (fd_checkpoint < 0) { | ||||
perror("Failed to create checkpoint file"); | perror("Failed to create checkpoint file"); | ||||
error = -1; | error = -1; | ||||
goto done; | goto done; | ||||
} | } | ||||
Show All 33 Lines | #endif | ||||
ret = vm_snapshot_basic_metadata(ctx, xop, memsz); | ret = vm_snapshot_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_snapshot_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_snapshot_user_devs(ctx, kdata_fd, xop); | ||||
Show All 14 Lines | done: | ||||
ret = vm_resume_user_devs(ctx); | ret = vm_resume_user_devs(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); | ||||
#ifndef JSON_SNAPSHOT_V2 | |||||
if (kdata_filename != NULL) | if (kdata_filename != NULL) | ||||
free(kdata_filename); | free(kdata_filename); | ||||
#endif | |||||
if (xop != NULL) | if (xop != NULL) | ||||
xo_destroy(xop); | xo_destroy(xop); | ||||
if (meta_file != NULL) | if (meta_file != NULL) | ||||
fclose(meta_file); | fclose(meta_file); | ||||
if (kdata_fd > 0) | if (kdata_fd > 0) | ||||
close(kdata_fd); | close(kdata_fd); | ||||
return (error); | return (error); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 130 Lines • ▼ Show 20 Lines | fail: | ||||
free(checkpoint_info); | free(checkpoint_info); | ||||
if (socket_fd > 0) | if (socket_fd > 0) | ||||
close(socket_fd); | close(socket_fd); | ||||
unlink(addr.sun_path); | unlink(addr.sun_path); | ||||
return (err); | return (err); | ||||
} | } | ||||
#define BUFFER_SUB_REM(buffer, size) \ | |||||
do { \ | |||||
buffer->buf += (size); \ | |||||
buffer->buf_rem -= (size); \ | |||||
} while(0) | |||||
#define CHK_SIZE_AND_ADD_ELEM(list, buffer, data_size, field_name, type, RET, LABEL) \ | |||||
do { \ | |||||
int32_t ds; \ | |||||
void *kdata = NULL; \ | |||||
\ | |||||
memcpy((uint8_t *) &ds, (buffer)->buf, sizeof(int32_t)); \ | |||||
if (ds != data_size) { \ | |||||
fprintf(stderr, \ | |||||
"%s: Size mismatch for parameter %s, expected %d but got %ld\r\n", \ | |||||
__func__, field_name, ds, data_size); \ | |||||
(RET) = -1; \ | |||||
goto LABEL; \ | |||||
} \ | |||||
BUFFER_SUB_REM(buffer, sizeof(int32_t)); \ | |||||
kdata = calloc(1, data_size); \ | |||||
if (kdata == NULL) { \ | |||||
fprintf(stderr, "%s: Could not alloc memory at line %d\r\n", \ | |||||
__func__, __LINE__); \ | |||||
(RET) = ENOMEM; \ | |||||
goto LABEL; \ | |||||
} \ | |||||
memcpy((uint8_t *) kdata, (buffer)->buf, data_size); \ | |||||
\ | |||||
alloc_device_info_elem(list, field_name, kdata, type, data_size); \ | |||||
\ | |||||
BUFFER_SUB_REM(buffer, data_size); \ | |||||
free(kdata); \ | |||||
} while(0) | |||||
#define ADD_SIZE_AND_DATA_TO_BUFFER(buffer, data_size, field_data) \ | |||||
do { \ | |||||
memcpy(buffer->buf, (uint8_t *)&data_size, sizeof(int32_t)); \ | |||||
BUFFER_SUB_REM(buffer, sizeof(int32_t)); \ | |||||
memcpy(buffer->buf, (field_data), data_size); \ | |||||
BUFFER_SUB_REM(buffer, data_size); \ | |||||
} while(0) | |||||
int | |||||
vm_snapshot_save_fieldname(const char *fullname, volatile void *data, | |||||
char *type, size_t data_size, struct vm_snapshot_meta *meta) | |||||
{ | |||||
int ret; | |||||
size_t len; | |||||
char *ffield_name; | |||||
char *aux; | |||||
char *field_name; | |||||
int op; | |||||
struct vm_snapshot_buffer *buffer; | |||||
struct list_device_info *list; | |||||
struct vm_snapshot_device_info *aux_elem; | |||||
const char delim[5] = "&(>)"; | |||||
buffer = &meta->buffer; | |||||
if (meta->snapshot_kernel) | |||||
if (buffer->buf_rem < data_size + sizeof(int)) { | |||||
fprintf(stderr, "%s: buffer too small\r\n", __func__); | |||||
return (E2BIG); | |||||
} | |||||
ret = 0; | |||||
op = meta->op; | |||||
len = strlen(fullname); | |||||
ffield_name = calloc(len + 1, sizeof(char)); | |||||
assert(ffield_name != NULL); | |||||
memcpy(ffield_name, fullname, len); | |||||
aux = strtok(ffield_name, delim); | |||||
field_name = strtok(NULL, delim); | |||||
if (field_name == NULL) | |||||
field_name = aux; | |||||
list = &meta->dev_info_list; | |||||
if (op == VM_SNAPSHOT_SAVE) { | |||||
if (meta->snapshot_kernel) | |||||
CHK_SIZE_AND_ADD_ELEM(list, buffer, data_size, field_name, type, ret, done); | |||||
else | |||||
alloc_device_info_elem(list, field_name, data, type, data_size); | |||||
if (list->auto_index >= 0) | |||||
list->auto_index++; | |||||
} else if (op == VM_SNAPSHOT_RESTORE) { | |||||
aux_elem = list->first; | |||||
if (aux_elem != NULL) { | |||||
if (meta->snapshot_kernel) { | |||||
ADD_SIZE_AND_DATA_TO_BUFFER(buffer, data_size, | |||||
(uint8_t *) aux_elem->field_data); | |||||
} else | |||||
memcpy((uint8_t *)data, (uint8_t *)aux_elem->field_data, data_size); | |||||
} | |||||
remove_first_elem(list); | |||||
} else { | |||||
ret = EINVAL; | |||||
goto done; | |||||
} | |||||
done: | |||||
free(ffield_name); | |||||
return (ret); | |||||
} | |||||
int | |||||
vm_snapshot_save_fieldname_cmp(const char *fullname, volatile void *data, | |||||
char *type, size_t data_size, struct vm_snapshot_meta *meta) | |||||
{ | |||||
size_t len; | |||||
char *ffield_name; | |||||
char *aux; | |||||
char *field_name; | |||||
int op; | |||||
int ret; | |||||
struct vm_snapshot_buffer *buffer; | |||||
struct list_device_info *list; | |||||
struct vm_snapshot_device_info *aux_elem; | |||||
const char delim[5] = "&(>)"; | |||||
buffer = &meta->buffer; | |||||
if (meta->snapshot_kernel) | |||||
if (buffer->buf_rem < data_size + sizeof(int32_t)) { | |||||
fprintf(stderr, "%s: buffer too small\r\n", __func__); | |||||
return (E2BIG); | |||||
} | |||||
op = meta->op; | |||||
len = strlen(fullname); | |||||
ffield_name = calloc(len + 1, sizeof(char)); | |||||
assert(ffield_name != NULL); | |||||
memcpy(ffield_name, fullname, len); | |||||
aux = strtok(ffield_name, delim); | |||||
field_name = strtok(NULL, delim); | |||||
if (field_name == NULL) | |||||
field_name = aux; | |||||
list = &meta->dev_info_list; | |||||
if (op == VM_SNAPSHOT_SAVE) { | |||||
ret = 0; | |||||
if (meta->snapshot_kernel) | |||||
CHK_SIZE_AND_ADD_ELEM(list, buffer, data_size, field_name, type, ret, done); | |||||
else | |||||
alloc_device_info_elem(list, field_name, data, type, data_size); | |||||
if (list->auto_index >= 0) | |||||
list->auto_index++; | |||||
} else if (op == VM_SNAPSHOT_RESTORE) { | |||||
aux_elem = list->first; | |||||
if (aux_elem != NULL) { | |||||
if (meta->snapshot_kernel) { | |||||
ADD_SIZE_AND_DATA_TO_BUFFER(buffer, data_size, | |||||
(uint8_t *) aux_elem->field_data); | |||||
} else | |||||
ret = memcmp((uint8_t *)data, (uint8_t *)aux_elem->field_data, data_size); | |||||
} | |||||
remove_first_elem(list); | |||||
} else { | |||||
ret = EINVAL; | |||||
goto done; | |||||
} | |||||
done: | |||||
free(ffield_name); | |||||
return (ret); | |||||
} | |||||
void | void | ||||
vm_snapshot_add_intern_list(const char *arr_name, struct vm_snapshot_meta *meta) | |||||
{ | |||||
meta->dev_info_list.intern_arr_names[meta->dev_info_list.ident++] = arr_name; | |||||
} | |||||
void | |||||
vm_snapshot_remove_intern_list(struct vm_snapshot_meta *meta) | |||||
{ | |||||
meta->dev_info_list.intern_arr_names[--meta->dev_info_list.ident] = NULL; | |||||
} | |||||
void | |||||
vm_snapshot_set_intern_arr_index(struct vm_snapshot_meta *meta, int index) | |||||
{ | |||||
meta->dev_info_list.index = index; | |||||
} | |||||
void | |||||
vm_snapshot_clear_intern_arr_index(struct vm_snapshot_meta *meta) | |||||
{ | |||||
meta->dev_info_list.index = -1; | |||||
} | |||||
void vm_snapshot_activate_auto_index(struct vm_snapshot_meta *meta, | |||||
unsigned char create_instance) | |||||
{ | |||||
meta->dev_info_list.create_instance = create_instance; | |||||
meta->dev_info_list.auto_index = 0; | |||||
} | |||||
void vm_snapshot_deactivate_auto_index(struct vm_snapshot_meta *meta) | |||||
{ | |||||
meta->dev_info_list.create_instance = 1; | |||||
meta->dev_info_list.auto_index = -1; | |||||
} | |||||
void check_and_set_non_array_type(char *type, struct vm_snapshot_meta *meta) | |||||
{ | |||||
if ((type != NULL) && strcmp(type, "b64")) | |||||
meta->dev_info_list.type = type; | |||||
} | |||||
void | |||||
vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) | vm_snapshot_buf_err(const char *bufname, const enum vm_snapshot_op op) | ||||
{ | { | ||||
const char *__op; | const char *__op; | ||||
if (op == VM_SNAPSHOT_SAVE) | if (op == VM_SNAPSHOT_SAVE) | ||||
__op = "save"; | __op = "save"; | ||||
else if (op == VM_SNAPSHOT_RESTORE) | else if (op == VM_SNAPSHOT_RESTORE) | ||||
__op = "restore"; | __op = "restore"; | ||||
Show All 21 Lines | vm_snapshot_buf(volatile void *data, size_t data_size, | ||||
if (op == VM_SNAPSHOT_SAVE) | if (op == VM_SNAPSHOT_SAVE) | ||||
memcpy(buffer->buf, (uint8_t *) data, data_size); | memcpy(buffer->buf, (uint8_t *) data, data_size); | ||||
else if (op == VM_SNAPSHOT_RESTORE) | else if (op == VM_SNAPSHOT_RESTORE) | ||||
memcpy((uint8_t *) data, buffer->buf, data_size); | memcpy((uint8_t *) data, buffer->buf, data_size); | ||||
else | else | ||||
return (EINVAL); | return (EINVAL); | ||||
buffer->buf += data_size; | BUFFER_SUB_REM(buffer, data_size); | ||||
buffer->buf_rem -= data_size; | |||||
return (0); | return (0); | ||||
} | } | ||||
size_t | size_t | ||||
vm_get_snapshot_size(struct vm_snapshot_meta *meta) | vm_get_snapshot_size(struct vm_snapshot_meta *meta) | ||||
{ | { | ||||
size_t length; | size_t length; | ||||
▲ Show 20 Lines • Show All 70 Lines • ▼ Show 20 Lines | if (op == VM_SNAPSHOT_SAVE) { | ||||
memcpy(buffer->buf, (uint8_t *) data, data_size); | memcpy(buffer->buf, (uint8_t *) data, data_size); | ||||
} else if (op == VM_SNAPSHOT_RESTORE) { | } else if (op == VM_SNAPSHOT_RESTORE) { | ||||
ret = memcmp((uint8_t *) data, buffer->buf, data_size); | ret = memcmp((uint8_t *) data, buffer->buf, data_size); | ||||
} else { | } else { | ||||
ret = EINVAL; | ret = EINVAL; | ||||
goto done; | goto done; | ||||
} | } | ||||
buffer->buf += data_size; | BUFFER_SUB_REM(buffer, data_size); | ||||
buffer->buf_rem -= data_size; | |||||
done: | done: | ||||
return (ret); | return (ret); | ||||
} | } |