Changeset View
Changeset View
Standalone View
Standalone View
usr.sbin/bhyve/snapshot.c
Show First 20 Lines • Show All 164 Lines • ▼ Show 20 Lines | const struct vm_snapshot_kern_info snapshot_kern_structs[] = { | ||||
{ "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; | ||||
static int snapdir_fd = -1; | |||||
/* | /* | ||||
* 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 1,101 Lines • ▼ Show 20 Lines | checkpoint_cpu_resume(int vcpu) | ||||
pthread_mutex_lock(&vcpu_lock); | pthread_mutex_lock(&vcpu_lock); | ||||
while (checkpoint_active) | while (checkpoint_active) | ||||
pthread_cond_wait(&vcpus_can_run, &vcpu_lock); | pthread_cond_wait(&vcpus_can_run, &vcpu_lock); | ||||
CPU_CLR(vcpu, &vcpus_suspended); | CPU_CLR(vcpu, &vcpus_suspended); | ||||
pthread_mutex_unlock(&vcpu_lock); | pthread_mutex_unlock(&vcpu_lock); | ||||
} | } | ||||
static void | static int | ||||
vm_vcpu_pause(struct vmctx *ctx) | vm_vcpu_pause(struct vmctx *ctx) | ||||
{ | { | ||||
pthread_mutex_lock(&vcpu_lock); | pthread_mutex_lock(&vcpu_lock); | ||||
checkpoint_active = true; | checkpoint_active = true; | ||||
vm_suspend_cpu(ctx, -1); | if (vm_suspend_cpu(ctx, -1) != 0) { | ||||
pthread_mutex_unlock(&vcpu_lock); | |||||
return (errno); | |||||
} | |||||
while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) | while (CPU_CMP(&vcpus_active, &vcpus_suspended) != 0) | ||||
pthread_cond_wait(&vcpus_idle, &vcpu_lock); | pthread_cond_wait(&vcpus_idle, &vcpu_lock); | ||||
pthread_mutex_unlock(&vcpu_lock); | pthread_mutex_unlock(&vcpu_lock); | ||||
return (0); | |||||
} | } | ||||
static void | static void | ||||
vm_vcpu_resume(struct vmctx *ctx) | 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 | static int | ||||
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, meta_fd; | ||||
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; | ||||
char *kdata_filename = NULL; | char *kdata_filename = NULL; | ||||
FILE *meta_file = NULL; | FILE *meta_file = NULL; | ||||
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 = openat(snapdir_fd, kdata_filename, O_WRONLY | O_CREAT | O_TRUNC, 0600); | ||||
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; | ||||
} | } | ||||
fd_checkpoint = open(checkpoint_file, O_RDWR | O_CREAT | O_TRUNC, 0700); | fd_checkpoint = openat(snapdir_fd, checkpoint_file, | ||||
O_RDWR | O_CREAT | O_TRUNC, 0600); | |||||
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; | ||||
} | } | ||||
meta_filename = strcat_extension(checkpoint_file, ".meta"); | meta_filename = strcat_extension(checkpoint_file, ".meta"); | ||||
if (meta_filename == NULL) { | if (meta_filename == NULL) { | ||||
fprintf(stderr, "Failed to construct vm metadata filename.\n"); | fprintf(stderr, "Failed to construct vm metadata filename.\n"); | ||||
error = -1; | |||||
goto done; | goto done; | ||||
} | } | ||||
meta_file = fopen(meta_filename, "w"); | meta_fd = openat(snapdir_fd, meta_filename, | ||||
O_WRONLY | O_CREAT | O_TRUNC, 0600); | |||||
if (meta_fd < 0) | |||||
perror("Failed to open vm metadata snapshot file descriptor."); | |||||
meta_file = fdopen(meta_fd, "w"); | |||||
if (meta_file == NULL) { | if (meta_file == NULL) { | ||||
perror("Failed to open vm metadata snapshot file."); | perror("Failed to open vm metadata snapshot file."); | ||||
close(meta_fd); | |||||
error = -1; | |||||
goto done; | goto done; | ||||
} | } | ||||
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."); | ||||
error = -1; | |||||
goto done; | goto done; | ||||
} | } | ||||
vm_vcpu_pause(ctx); | ret = vm_vcpu_pause(ctx); | ||||
if (ret != 0) { | |||||
fprintf(stderr, "Could not pause vcpu\n"); | |||||
error = ret; | |||||
goto done; | |||||
} | |||||
ret = vm_pause_user_devs(ctx); | ret = vm_pause_user_devs(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; | ||||
} | } | ||||
▲ Show 20 Lines • Show All 135 Lines • ▼ Show 20 Lines | |||||
*/ | */ | ||||
int | int | ||||
init_checkpoint_thread(struct vmctx *ctx) | init_checkpoint_thread(struct vmctx *ctx) | ||||
{ | { | ||||
struct checkpoint_thread_info *checkpoint_info = NULL; | struct checkpoint_thread_info *checkpoint_info = NULL; | ||||
struct sockaddr_un addr; | struct sockaddr_un addr; | ||||
int socket_fd; | int socket_fd; | ||||
pthread_t checkpoint_pthread; | pthread_t checkpoint_pthread; | ||||
const char *snapdir = getenv("BHYVE_SNAPDIR") ? : "."; | |||||
int err; | int err; | ||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_t rights; | |||||
#endif | |||||
memset(&addr, 0, sizeof(addr)); | memset(&addr, 0, sizeof(addr)); | ||||
err = mkdir(snapdir, 0755); | |||||
if (err < 0 && errno != EEXIST) { | |||||
warn("Cannot create directory '%s'", snapdir); | |||||
goto fail; | |||||
} | |||||
snapdir_fd = open(snapdir, O_RDONLY | O_DIRECTORY); | |||||
if (snapdir_fd < 0) { | |||||
warn("Cannot open snapshot directory '%s'", snapdir); | |||||
err = -1; | |||||
goto fail; | |||||
} | |||||
socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); | socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); | ||||
if (socket_fd < 0) { | if (socket_fd < 0) { | ||||
EPRINTLN("Socket creation failed: %s", strerror(errno)); | EPRINTLN("Socket creation failed: %s", strerror(errno)); | ||||
err = -1; | err = -1; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
addr.sun_family = AF_UNIX; | addr.sun_family = AF_UNIX; | ||||
snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", | snprintf(addr.sun_path, sizeof(addr.sun_path), "%s%s", | ||||
BHYVE_RUN_DIR, vm_get_name(ctx)); | BHYVE_RUN_DIR, vm_get_name(ctx)); | ||||
corvink: Is this part unrelated to your fd part? If yes, you could split this as well. | |||||
Done Inline Actionsgusev.vitaliy_gmail.com: Done. https://reviews.freebsd.org/D38856 | |||||
addr.sun_len = SUN_LEN(&addr); | addr.sun_len = SUN_LEN(&addr); | ||||
unlink(addr.sun_path); | unlink(addr.sun_path); | ||||
if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { | if (bind(socket_fd, (struct sockaddr *)&addr, addr.sun_len) != 0) { | ||||
EPRINTLN("Failed to bind socket \"%s\": %s\n", | EPRINTLN("Failed to bind socket \"%s\": %s\n", | ||||
addr.sun_path, strerror(errno)); | addr.sun_path, strerror(errno)); | ||||
err = -1; | err = -1; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
if (listen(socket_fd, 10) < 0) { | if (listen(socket_fd, 10) < 0) { | ||||
EPRINTLN("ipc socket listen: %s\n", strerror(errno)); | EPRINTLN("ipc socket listen: %s\n", strerror(errno)); | ||||
err = errno; | err = errno; | ||||
goto fail; | goto fail; | ||||
} | } | ||||
#ifndef WITHOUT_CAPSICUM | |||||
cap_rights_init(&rights, CAP_LOOKUP, CAP_FTRUNCATE, CAP_PWRITE, CAP_PREAD, | |||||
CAP_FCNTL, CAP_CREATE); | |||||
if (caph_rights_limit(snapdir_fd, &rights) == -1) { | |||||
warn("Unable to apply rights for sandbox for snapdir"); | |||||
err = -1; | |||||
goto fail; | |||||
} | |||||
cap_rights_init(&rights, CAP_READ | CAP_WRITE | CAP_ACCEPT); | |||||
if (caph_rights_limit(socket_fd, &rights) == -1) { | |||||
warn("Unable to apply rights for sandbox for checkpoint socket"); | |||||
err = -1; | |||||
goto fail; | |||||
} | |||||
#endif | |||||
checkpoint_info = calloc(1, sizeof(*checkpoint_info)); | checkpoint_info = calloc(1, sizeof(*checkpoint_info)); | ||||
checkpoint_info->ctx = ctx; | checkpoint_info->ctx = ctx; | ||||
checkpoint_info->socket_fd = socket_fd; | checkpoint_info->socket_fd = socket_fd; | ||||
err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, | err = pthread_create(&checkpoint_pthread, NULL, checkpoint_thread, | ||||
checkpoint_info); | checkpoint_info); | ||||
if (err != 0) | if (err != 0) | ||||
goto fail; | goto fail; | ||||
return (0); | return (0); | ||||
fail: | fail: | ||||
free(checkpoint_info); | free(checkpoint_info); | ||||
if (socket_fd > 0) | if (socket_fd > 0) | ||||
close(socket_fd); | close(socket_fd); | ||||
close(snapdir_fd); | |||||
unlink(addr.sun_path); | unlink(addr.sun_path); | ||||
return (err); | 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) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 129 Lines • Show Last 20 Lines |
Is this part unrelated to your fd part? If yes, you could split this as well.