Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/snapshot.c
Show First 20 Lines • Show All 109 Lines • ▼ Show 20 Lines | |||||||||||||
#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 BHYVE_RUN_DIR "/var/run/bhyve" | /* XXX: could use MAX_SNAPSHOT_VMNAME from vmmapi.h */ | ||||||||||||
#define CHECKPOINT_RUN_DIR BHYVE_RUN_DIR "/checkpoint" | |||||||||||||
#define MAX_VMNAME 100 | #define MAX_VMNAME 100 | ||||||||||||
#define MAX_MSG_SIZE 1024 | |||||||||||||
#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" | ||||||||||||
▲ Show 20 Lines • Show All 1,190 Lines • ▼ Show 20 Lines | vm_vcpu_resume(struct vmctx *ctx) | ||||||||||||
pthread_mutex_lock(&vcpu_lock); | pthread_mutex_lock(&vcpu_lock); | ||||||||||||
checkpoint_active = false; | checkpoint_active = false; | ||||||||||||
pthread_mutex_unlock(&vcpu_lock); | pthread_mutex_unlock(&vcpu_lock); | ||||||||||||
vm_resume_cpu(ctx, -1); | vm_resume_cpu(ctx, -1); | ||||||||||||
pthread_cond_broadcast(&vcpus_can_run); | pthread_cond_broadcast(&vcpus_can_run); | ||||||||||||
} | } | ||||||||||||
static int | int | ||||||||||||
vm_checkpoint(struct vmctx *ctx, char *checkpoint_file, bool stop_vm) | vm_checkpoint(struct vmctx *ctx, 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; | ||||||||||||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Lines | 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); | ||||||||||||
} | } | ||||||||||||
int | int init_snapshot(void) | ||||||||||||
jhbUnsubmitted Not Done Inline Actions
jhb: | |||||||||||||
get_checkpoint_msg(int conn_fd, struct vmctx *ctx) | |||||||||||||
{ | { | ||||||||||||
unsigned char buf[MAX_MSG_SIZE]; | |||||||||||||
struct checkpoint_op *checkpoint_op; | |||||||||||||
int len, recv_len, total_recv = 0; | |||||||||||||
int err = 0; | |||||||||||||
len = sizeof(struct checkpoint_op); /* expected length */ | |||||||||||||
while ((recv_len = recv(conn_fd, buf + total_recv, len - total_recv, 0)) > 0) { | |||||||||||||
total_recv += recv_len; | |||||||||||||
} | |||||||||||||
if (recv_len < 0) { | |||||||||||||
perror("Error while receiving data from bhyvectl"); | |||||||||||||
err = -1; | |||||||||||||
goto done; | |||||||||||||
} | |||||||||||||
checkpoint_op = (struct checkpoint_op *)buf; | |||||||||||||
switch (checkpoint_op->op) { | |||||||||||||
case START_CHECKPOINT: | |||||||||||||
err = vm_checkpoint(ctx, checkpoint_op->snapshot_filename, false); | |||||||||||||
break; | |||||||||||||
case START_SUSPEND: | |||||||||||||
err = vm_checkpoint(ctx, checkpoint_op->snapshot_filename, true); | |||||||||||||
break; | |||||||||||||
default: | |||||||||||||
fprintf(stderr, "Unrecognized checkpoint operation.\n"); | |||||||||||||
err = -1; | |||||||||||||
} | |||||||||||||
done: | |||||||||||||
close(conn_fd); | |||||||||||||
return (err); | |||||||||||||
} | |||||||||||||
/* | |||||||||||||
* Listen for commands from bhyvectl | |||||||||||||
*/ | |||||||||||||
void * | |||||||||||||
checkpoint_thread(void *param) | |||||||||||||
{ | |||||||||||||
struct checkpoint_thread_info *thread_info; | |||||||||||||
int conn_fd, ret; | |||||||||||||
pthread_set_name_np(pthread_self(), "checkpoint thread"); | |||||||||||||
thread_info = (struct checkpoint_thread_info *)param; | |||||||||||||
while ((conn_fd = accept(thread_info->socket_fd, NULL, NULL)) > -1) { | |||||||||||||
ret = get_checkpoint_msg(conn_fd, thread_info->ctx); | |||||||||||||
if (ret != 0) { | |||||||||||||
fprintf(stderr, "Failed to read message on checkpoint " | |||||||||||||
"socket. Retrying.\n"); | |||||||||||||
} | |||||||||||||
} | |||||||||||||
if (conn_fd < -1) { | |||||||||||||
perror("Failed to accept connection"); | |||||||||||||
} | |||||||||||||
return (NULL); | |||||||||||||
} | |||||||||||||
/* | |||||||||||||
* Create directory tree to store runtime specific information: | |||||||||||||
* i.e. UNIX sockets for IPC with bhyvectl. | |||||||||||||
*/ | |||||||||||||
static int | |||||||||||||
make_checkpoint_dir(void) | |||||||||||||
{ | |||||||||||||
int err; | int err; | ||||||||||||
err = mkdir(BHYVE_RUN_DIR, 0755); | |||||||||||||
if (err < 0 && errno != EEXIST) | |||||||||||||
return (err); | |||||||||||||
err = mkdir(CHECKPOINT_RUN_DIR, 0755); | |||||||||||||
if (err < 0 && errno != EEXIST) | |||||||||||||
return (err); | |||||||||||||
return 0; | |||||||||||||
} | |||||||||||||
/* | |||||||||||||
* Create the listening socket for IPC with bhyvectl | |||||||||||||
*/ | |||||||||||||
int | |||||||||||||
init_checkpoint_thread(struct vmctx *ctx) | |||||||||||||
{ | |||||||||||||
struct checkpoint_thread_info *checkpoint_info = NULL; | |||||||||||||
struct sockaddr_un addr; | |||||||||||||
int socket_fd; | |||||||||||||
pthread_t checkpoint_pthread; | |||||||||||||
char vmname_buf[MAX_VMNAME]; | |||||||||||||
int ret, err = 0; | |||||||||||||
memset(&addr, 0, sizeof(addr)); | |||||||||||||
err = pthread_mutex_init(&vcpu_lock, NULL); | err = pthread_mutex_init(&vcpu_lock, NULL); | ||||||||||||
if (err != 0) | if (err != 0) | ||||||||||||
errc(1, err, "checkpoint mutex init"); | errc(1, err, "checkpoint mutex init"); | ||||||||||||
err = pthread_cond_init(&vcpus_idle, NULL); | err = pthread_cond_init(&vcpus_idle, NULL); | ||||||||||||
if (err == 0) | if (err == 0) | ||||||||||||
err = pthread_cond_init(&vcpus_can_run, NULL); | err = pthread_cond_init(&vcpus_can_run, NULL); | ||||||||||||
if (err != 0) | if (err != 0) | ||||||||||||
errc(1, err, "checkpoint cv init"); | errc(1, err, "checkpoint cv init"); | ||||||||||||
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); | |||||||||||||
if (socket_fd < 0) { | |||||||||||||
perror("Socket creation failed (IPC with bhyvectl"); | |||||||||||||
err = -1; | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
err = make_checkpoint_dir(); | |||||||||||||
if (err < 0) { | |||||||||||||
perror("Failed to create checkpoint runtime directory"); | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
addr.sun_family = AF_UNIX; | |||||||||||||
err = vm_get_name(ctx, vmname_buf, MAX_VMNAME - 1); | |||||||||||||
if (err != 0) { | |||||||||||||
perror("Failed to get VM name"); | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", | |||||||||||||
CHECKPOINT_RUN_DIR, vmname_buf); | |||||||||||||
addr.sun_len = SUN_LEN(&addr); | |||||||||||||
unlink(addr.sun_path); | |||||||||||||
if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { | |||||||||||||
perror("Failed to bind socket (IPC with bhyvectl)"); | |||||||||||||
err = -1; | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
if (listen(socket_fd, 10) < 0) { | |||||||||||||
perror("Failed to listen on socket (IPC with bhyvectl)"); | |||||||||||||
err = -1; | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
checkpoint_info = calloc(1, sizeof(*checkpoint_info)); | |||||||||||||
checkpoint_info->ctx = ctx; | |||||||||||||
checkpoint_info->socket_fd = socket_fd; | |||||||||||||
ret = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, | |||||||||||||
checkpoint_info); | |||||||||||||
if (ret < 0) { | |||||||||||||
err = ret; | |||||||||||||
goto fail; | |||||||||||||
} | |||||||||||||
return (0); | return (0); | ||||||||||||
fail: | |||||||||||||
free(checkpoint_info); | |||||||||||||
if (socket_fd > 0) | |||||||||||||
close(socket_fd); | |||||||||||||
unlink(addr.sun_path); | |||||||||||||
return (err); | |||||||||||||
} | } | ||||||||||||
void | 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) | ||||||||||||
▲ Show 20 Lines • Show All 126 Lines • Show Last 20 Lines |