Allow starting a VM again after it was terminated
authorstefan <stefan@openbsd.org>
Fri, 29 Jul 2016 16:36:51 +0000 (16:36 +0000)
committerstefan <stefan@openbsd.org>
Fri, 29 Jul 2016 16:36:51 +0000 (16:36 +0000)
If a VM exits, terminate it and remove it from the list of
available VMs. That allows a VM with name `foo' to be restarted
after it has exited.

This changes structures shared between vmd and vmctl. You need to
rebuild vmctl also.

ok mlarkin@

usr.sbin/vmd/config.c
usr.sbin/vmd/vmd.c
usr.sbin/vmd/vmd.h
usr.sbin/vmd/vmm.c

index 06f2399..42d94bd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.9 2015/12/07 15:57:53 reyk Exp $ */
+/*     $OpenBSD: config.c,v 1.10 2016/07/29 16:36:51 stefan Exp $      */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -135,6 +135,7 @@ config_getvm(struct privsep *ps, struct vm_create_params *vcp,
                goto fail;
 
        memcpy(&vm->vm_params, vcp, sizeof(vm->vm_params));
+       vm->vm_pid = -1;
 
        for (i = 0; i < vcp->vcp_ndisks; i++)
                vm->vm_disks[i] = -1;
index 03ec2c1..06e3096 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.c,v 1.27 2016/02/05 11:40:15 reyk Exp $   */
+/*     $OpenBSD: vmd.c,v 1.28 2016/07/29 16:36:51 stefan Exp $ */
 
 /*
  * Copyright (c) 2015 Reyk Floeter <reyk@openbsd.org>
@@ -148,6 +148,7 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
                memcpy(&vmr, imsg->data, sizeof(vmr));
                if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL)
                        fatalx("%s: invalid vm response", __func__);
+               vm->vm_pid = vmr.vmr_pid;
                vcp = &vm->vm_params;
                vcp->vcp_id = vmr.vmr_id;
 
@@ -181,9 +182,11 @@ vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
                }
                break;
        case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
+       case IMSG_VMDOP_TERMINATE_VM_EVENT:
                IMSG_SIZE_CHECK(imsg, &vmr);
                memcpy(&vmr, imsg->data, sizeof(vmr));
-               proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
+               if (imsg->hdr.type == IMSG_VMDOP_TERMINATE_VM_RESPONSE)
+                       proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
                if (vmr.vmr_result == 0) {
                        /* Remove local reference */
                        vm = vm_getbyid(vmr.vmr_id);
@@ -508,6 +511,19 @@ vm_getbyname(const char *name)
        return (NULL);
 }
 
+struct vmd_vm *
+vm_getbypid(pid_t pid)
+{
+       struct vmd_vm   *vm;
+
+       TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
+               if (vm->vm_pid == pid)
+                       return (vm);
+       }
+
+       return (NULL);
+}
+
 void
 vm_remove(struct vmd_vm *vm)
 {
index 386ba12..38ebc7f 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmd.h,v 1.21 2016/07/09 09:06:22 stefan Exp $ */
+/*     $OpenBSD: vmd.h,v 1.22 2016/07/29 16:36:51 stefan Exp $ */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -57,12 +57,14 @@ enum imsg_type {
        IMSG_VMDOP_GET_INFO_VM_DATA,
        IMSG_VMDOP_GET_INFO_VM_END_DATA,
        IMSG_VMDOP_LOAD,
-       IMSG_VMDOP_RELOAD
+       IMSG_VMDOP_RELOAD,
+       IMSG_VMDOP_TERMINATE_VM_EVENT
 };
 
 struct vmop_result {
        int                      vmr_result;
        uint32_t                 vmr_id;
+       pid_t                    vmr_pid;
        char                     vmr_ttyname[VM_TTYNAME_MAX];
 };
 
@@ -78,6 +80,7 @@ struct vmop_id {
 
 struct vmd_vm {
        struct vm_create_params  vm_params;
+       pid_t                    vm_pid;
        uint32_t                 vm_vmid;
        int                      vm_kernel;
        int                      vm_disks[VMM_MAX_DISKS_PER_VM];
@@ -109,6 +112,7 @@ void         vmd_reload(int, const char *);
 struct vmd_vm *vm_getbyvmid(uint32_t);
 struct vmd_vm *vm_getbyid(uint32_t);
 struct vmd_vm *vm_getbyname(const char *);
+struct vmd_vm *vm_getbypid(pid_t);
 void    vm_remove(struct vmd_vm *);
 char   *get_string(uint8_t *, size_t);
 
index 826b4cc..59403d5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: vmm.c,v 1.33 2016/07/19 09:52:34 natano Exp $ */
+/*     $OpenBSD: vmm.c,v 1.34 2016/07/29 16:36:51 stefan Exp $ */
 
 /*
  * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org>
@@ -19,6 +19,7 @@
 #include <sys/param.h>
 #include <sys/ioctl.h>
 #include <sys/queue.h>
+#include <sys/wait.h>
 #include <sys/uio.h>
 #include <sys/socket.h>
 #include <sys/time.h>
@@ -103,6 +104,7 @@ struct i8253_counter i8253_counter[3];
 struct ns8250_regs com1_regs;
 io_fn_t ioports_map[MAX_PORTS];
 
+void vmm_sighdlr(int, short, void *);
 int start_client_vmd(void);
 int opentap(void);
 int start_vm(struct imsg *, uint32_t *);
@@ -191,6 +193,10 @@ vmm_run(struct privsep *ps, struct privsep_proc *p, void *arg)
        if (config_init(ps->ps_env) == -1)
                fatal("failed to initialize configuration");
 
+       signal_del(&ps->ps_evsigchld);
+       signal_set(&ps->ps_evsigchld, SIGCHLD, vmm_sighdlr, ps);
+       signal_add(&ps->ps_evsigchld, NULL);
+
 #if 0
        /*
         * pledge in the vmm process:
@@ -296,6 +302,60 @@ vmm_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
        return (0);
 }
 
+void
+vmm_sighdlr(int sig, short event, void *arg)
+{
+       struct privsep *ps = arg;
+       int status;
+       uint32_t vmid;
+       pid_t pid;
+       struct vmop_result vmr;
+       struct vmd_vm *vm;
+       struct vm_terminate_params vtp;
+
+       switch (sig) {
+       case SIGCHLD:
+               do {
+                       pid = waitpid(-1, &status, WNOHANG);
+                       if (pid <= 0)
+                               continue;
+
+                       if (WIFEXITED(status) || WIFSIGNALED(status)) {
+                               vm = vm_getbypid(pid);
+                               if (vm == NULL) {
+                                       /*
+                                        * If the VM is gone already, it
+                                        * got terminated via a
+                                        * IMSG_VMDOP_TERMINATE_VM_REQUEST.
+                                        */
+                                       continue;
+                               }
+
+                               vmid = vm->vm_params.vcp_id;
+                               vtp.vtp_vm_id = vmid;
+                               if (terminate_vm(&vtp) == 0) {
+                                       memset(&vmr, 0, sizeof(vmr));
+                                       vmr.vmr_result = 0;
+                                       vmr.vmr_id = vmid;
+                                       vm_remove(vm);
+                                       if (proc_compose_imsg(ps, PROC_PARENT,
+                                           -1, IMSG_VMDOP_TERMINATE_VM_EVENT,
+                                           0, -1, &vmr, sizeof(vmr)) == -1)
+                                               log_warnx("could not signal "
+                                                   "termination of VM %u to "
+                                                   "parent", vmid);
+                               } else
+                                       log_warnx("could not terminate VM %u",
+                                           vmid);
+                       } else
+                               fatalx("unexpected cause of SIGCHLD");
+               } while (pid > 0 || (pid == -1 && errno == EINTR));
+               break;
+       default:
+               fatalx("unexpected signal");
+       }
+}
+
 /*
  * vcpu_reset
  *
@@ -436,6 +496,8 @@ start_vm(struct imsg *imsg, uint32_t *id)
 
        if (ret > 0) {
                /* Parent */
+               vm->vm_pid = ret;
+
                for (i = 0 ; i < vcp->vcp_ndisks; i++) {
                        close(vm->vm_disks[i]);
                        vm->vm_disks[i] = -1;
@@ -864,7 +926,6 @@ run_vm(int *child_disks, int *child_taps, struct vm_create_params *vcp,
        pthread_t *tid;
        void *exit_status;
        struct vm_run_params **vrp;
-       struct vm_terminate_params vtp;
 
        if (vcp == NULL)
                return (EINVAL);
@@ -949,13 +1010,6 @@ run_vm(int *child_disks, int *child_taps, struct vm_create_params *vcp,
                if (exit_status != NULL) {
                        log_warnx("%s: vm %d vcpu run thread %zd exited "
                            "abnormally", __progname, vcp->vcp_id, i);
-                       /* Terminate the VM if we can */
-                       memset(&vtp, 0, sizeof(vtp));
-                       vtp.vtp_vm_id = vcp->vcp_id;
-                       if (terminate_vm(&vtp)) {
-                               log_warnx("%s: could not terminate vm %d",
-                                   __progname, vcp->vcp_id);
-                       }
                        ret = EIO;
                }
        }