diff options
author | 2017-03-15 18:06:18 +0000 | |
---|---|---|
committer | 2017-03-15 18:06:18 +0000 | |
commit | 3320a88dc894d1f5b353f5dae162d252874540c0 (patch) | |
tree | d8f8eb3bea74fbb54693e821fb502fed4bfd0f10 | |
parent | Print "-" if the tty name is empty. (diff) | |
download | wireguard-openbsd-3320a88dc894d1f5b353f5dae162d252874540c0.tar.xz wireguard-openbsd-3320a88dc894d1f5b353f5dae162d252874540c0.zip |
Improve vmmci(4) shutdown and reboot.
This change handles various cases to power off the VM, even if it is
unresponsive, stuck in ddb, or when the shutdown was initiated from
the VM guest side. Usage of timeout and VM ACKs make sure that the VM
is really turned off at some point.
OK mlarkin@
-rw-r--r-- | sys/dev/pv/vmmci.c | 49 | ||||
-rw-r--r-- | usr.sbin/vmd/virtio.c | 70 | ||||
-rw-r--r-- | usr.sbin/vmd/virtio.h | 10 | ||||
-rw-r--r-- | usr.sbin/vmd/vm.c | 30 | ||||
-rw-r--r-- | usr.sbin/vmd/vmd.h | 3 | ||||
-rw-r--r-- | usr.sbin/vmd/vmm.c | 8 |
6 files changed, 156 insertions, 14 deletions
diff --git a/sys/dev/pv/vmmci.c b/sys/dev/pv/vmmci.c index 1dfd5e7eaf2..ee9c7c195b0 100644 --- a/sys/dev/pv/vmmci.c +++ b/sys/dev/pv/vmmci.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmmci.c,v 1.1 2017/01/21 11:23:14 reyk Exp $ */ +/* $OpenBSD: vmmci.c,v 1.2 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2017 Reyk Floeter <reyk@openbsd.org> @@ -51,6 +51,7 @@ struct vmmci_softc { int vmmci_match(struct device *, void *, void *); void vmmci_attach(struct device *, struct device *, void *); +int vmmci_activate(struct device *, int); int vmmci_config_change(struct virtio_softc *); void vmmci_tick(void *); @@ -60,7 +61,8 @@ struct cfattach vmmci_ca = { sizeof(struct vmmci_softc), vmmci_match, vmmci_attach, - NULL + NULL, + vmmci_activate }; /* Configuration registers */ @@ -70,6 +72,7 @@ struct cfattach vmmci_ca = { /* Feature bits */ #define VMMCI_F_TIMESYNC (1<<0) +#define VMMCI_F_ACK (1<<1) struct cfdriver vmmci_cd = { NULL, "vmmci", DV_DULL @@ -100,7 +103,7 @@ vmmci_attach(struct device *parent, struct device *self, void *aux) vsc->sc_ipl = IPL_NET; sc->sc_virtio = vsc; - features = VMMCI_F_TIMESYNC; + features = VMMCI_F_TIMESYNC|VMMCI_F_ACK; features = virtio_negotiate_features(vsc, features, NULL); if (features & VMMCI_F_TIMESYNC) { @@ -118,13 +121,42 @@ vmmci_attach(struct device *parent, struct device *self, void *aux) } int +vmmci_activate(struct device *self, int act) +{ + struct vmmci_softc *sc = (struct vmmci_softc *)self; + struct virtio_softc *vsc = sc->sc_virtio; + + if ((vsc->sc_features & VMMCI_F_ACK) == 0) + return (0); + + switch (act) { + case DVACT_POWERDOWN: + printf("%s: powerdown\n", sc->sc_dev.dv_xname); + + /* + * Tell the host that we are shutting down. The host will + * start a timer and kill our VM if we didn't reboot before + * expiration. This avoids being stuck in the + * "Please press any key to reboot" handler on RB_HALT; + * without hooking into the MD code directly. + */ + virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND, + VMMCI_SHUTDOWN); + break; + default: + break; + } + return (0); +} + +int vmmci_config_change(struct virtio_softc *vsc) { - struct vmmci_softc *sc = (struct vmmci_softc *)vsc->sc_child; - int cmd; + struct vmmci_softc *sc = (struct vmmci_softc *)vsc->sc_child; + uint32_t cmd; /* Check for command */ - cmd = virtio_read_device_config_1(vsc, VMMCI_CONFIG_COMMAND); + cmd = virtio_read_device_config_4(vsc, VMMCI_CONFIG_COMMAND); if (cmd == sc->sc_cmd) return (0); sc->sc_cmd = cmd; @@ -141,9 +173,14 @@ vmmci_config_change(struct virtio_softc *vsc) break; default: printf("%s: invalid command %d\n", sc->sc_dev.dv_xname, cmd); + cmd = VMMCI_NONE; break; } + if ((cmd != VMMCI_NONE) && + (vsc->sc_features & VMMCI_F_ACK)) + virtio_write_device_config_4(vsc, VMMCI_CONFIG_COMMAND, cmd); + return (1); } diff --git a/usr.sbin/vmd/virtio.c b/usr.sbin/vmd/virtio.c index 2d4f46fefb8..fdf1f44618a 100644 --- a/usr.sbin/vmd/virtio.c +++ b/usr.sbin/vmd/virtio.c @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.c,v 1.33 2017/03/02 07:33:37 reyk Exp $ */ +/* $OpenBSD: virtio.c,v 1.34 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -57,6 +57,7 @@ int nr_vionet; #define VIRTIO_NET_F_MAC (1<<5) #define VMMCI_F_TIMESYNC (1<<0) +#define VMMCI_F_ACK (1<<1) const char * vioblk_cmd_name(uint32_t type) @@ -1202,6 +1203,8 @@ out: int vmmci_ctl(unsigned int cmd) { + struct timeval tv = { 0, 0 }; + if ((vmmci.cfg.device_status & VIRTIO_CONFIG_DEVICE_STATUS_DRIVER_OK) == 0) return (-1); @@ -1211,6 +1214,7 @@ vmmci_ctl(unsigned int cmd) switch (cmd) { case VMMCI_NONE: + break; case VMMCI_SHUTDOWN: case VMMCI_REBOOT: /* Update command */ @@ -1226,6 +1230,10 @@ vmmci_ctl(unsigned int cmd) /* Trigger interrupt */ vmmci.cfg.isr_status = VIRTIO_CONFIG_ISR_CONFIG_CHANGE; vcpu_assert_pic_irq(vmmci.vm_id, 0, vmmci.irq); + + /* Add ACK timeout */ + tv.tv_sec = VMMCI_TIMEOUT; + evtimer_add(&vmmci.timeout, &tv); break; default: fatalx("invalid vmmci command: %d", cmd); @@ -1234,6 +1242,58 @@ vmmci_ctl(unsigned int cmd) return (0); } +void +vmmci_ack(unsigned int cmd) +{ + struct timeval tv = { 0, 0 }; + + switch (cmd) { + case VMMCI_NONE: + break; + case VMMCI_SHUTDOWN: + /* + * The shutdown was requested by the VM if we don't have + * a pending shutdown request. In this case add a short + * timeout to give the VM a chance to reboot before the + * timer is expired. + */ + if (vmmci.cmd == 0) { + log_debug("%s: vm %u requested shutdown", __func__, + vmmci.vm_id); + tv.tv_sec = VMMCI_TIMEOUT; + evtimer_add(&vmmci.timeout, &tv); + return; + } + /* FALLTHROUGH */ + case VMMCI_REBOOT: + /* + * If the VM acknowleged our shutdown request, give it + * enough time to shutdown or reboot gracefully. This + * might take a considerable amount of time (running + * rc.shutdown on the VM), so increase the timeout before + * killing it forcefully. + */ + if (cmd == vmmci.cmd && + evtimer_pending(&vmmci.timeout, NULL)) { + log_debug("%s: vm %u acknowledged shutdown request", + __func__, vmmci.vm_id); + tv.tv_sec = VMMCI_SHUTDOWN_TIMEOUT; + evtimer_add(&vmmci.timeout, &tv); + } + break; + default: + log_warnx("%s: illegal request %u", __func__, cmd); + break; + } +} + +void +vmmci_timeout(int fd, short type, void *arg) +{ + log_debug("%s: vm %u shutdown", __progname, vmmci.vm_id); + vm_shutdown(vmmci.cmd == VMMCI_REBOOT ? VMMCI_REBOOT : VMMCI_SHUTDOWN); +} + int vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr, void *unused) @@ -1263,6 +1323,9 @@ vmmci_io(int dir, uint16_t reg, uint32_t *data, uint8_t *intr, case VIRTIO_CONFIG_DEVICE_STATUS: vmmci.cfg.device_status = *data; break; + case VIRTIO_CONFIG_DEVICE_CONFIG_NOMSI: + vmmci_ack(*data); + break; } } else { switch (reg) { @@ -1502,7 +1565,10 @@ virtio_init(struct vmop_create_params *vmc, int *child_disks, int *child_taps) return; } - vmmci.cfg.device_feature = VMMCI_F_TIMESYNC; + memset(&vmmci, 0, sizeof(vmmci)); + vmmci.cfg.device_feature = VMMCI_F_TIMESYNC|VMMCI_F_ACK; vmmci.vm_id = vcp->vcp_id; vmmci.irq = pci_get_dev_irq(id); + + evtimer_set(&vmmci.timeout, vmmci_timeout, NULL); } diff --git a/usr.sbin/vmd/virtio.h b/usr.sbin/vmd/virtio.h index 291d5deeb69..d92a0f3081f 100644 --- a/usr.sbin/vmd/virtio.h +++ b/usr.sbin/vmd/virtio.h @@ -1,4 +1,4 @@ -/* $OpenBSD: virtio.h,v 1.10 2017/03/02 07:33:37 reyk Exp $ */ +/* $OpenBSD: virtio.h,v 1.11 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -31,8 +31,9 @@ #define VIONET_QUEUE_SIZE 64 #define VIONET_QUEUE_MASK (VIONET_QUEUE_SIZE - 1) -/* Version of the VMM Control Interface */ -#define VMMCI_VERSION 1 +/* VMM Control Interface shutdown timeout (in seconds) */ +#define VMMCI_TIMEOUT 3 +#define VMMCI_SHUTDOWN_TIMEOUT 30 /* All the devices we support have either 1 or 2 queues */ #define VIRTIO_MAX_QUEUES 2 @@ -145,6 +146,7 @@ enum vmmci_cmd { struct vmmci_dev { struct virtio_io_cfg cfg; + struct event timeout; struct timeval time; enum vmmci_cmd cmd; uint32_t vm_id; @@ -174,5 +176,7 @@ int vionet_enq_rx(struct vionet_dev *, char *, ssize_t, int *); int vmmci_io(int, uint16_t, uint32_t *, uint8_t *, void *); int vmmci_ctl(unsigned int); +void vmmci_ack(unsigned int); +void vmmci_timeout(int, short, void *); const char *vioblk_cmd_name(uint32_t); diff --git a/usr.sbin/vmd/vm.c b/usr.sbin/vmd/vm.c index f6fed52466b..de6fef1496f 100644 --- a/usr.sbin/vmd/vm.c +++ b/usr.sbin/vmd/vm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vm.c,v 1.2 2017/03/02 07:33:37 reyk Exp $ */ +/* $OpenBSD: vm.c,v 1.3 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -307,6 +307,34 @@ vm_dispatch_vmm(int fd, short event, void *arg) } /* + * vm_ctl + * + * Tell the vmm parent process to shutdown or reboot the VM and exit. + */ +__dead void +vm_shutdown(unsigned int cmd) +{ + struct privsep *ps = &env->vmd_ps; + + switch (cmd) { + case VMMCI_NONE: + case VMMCI_SHUTDOWN: + (void)proc_compose(ps, PROC_VMM, + IMSG_VMDOP_VM_SHUTDOWN, NULL, 0); + break; + case VMMCI_REBOOT: + (void)proc_compose(ps, PROC_VMM, + IMSG_VMDOP_VM_REBOOT, NULL, 0); + break; + default: + fatalx("invalid vm ctl command: %d", cmd); + } + proc_flush_imsg(ps, PROC_VMM, -1); + + _exit(0); +} + +/* * vcpu_reset * * Requests vmm(4) to reset the VCPUs in the indicated VM to diff --git a/usr.sbin/vmd/vmd.h b/usr.sbin/vmd/vmd.h index 2afc04f997b..1c59fc484f3 100644 --- a/usr.sbin/vmd/vmd.h +++ b/usr.sbin/vmd/vmd.h @@ -1,4 +1,4 @@ -/* $OpenBSD: vmd.h,v 1.47 2017/03/02 07:33:37 reyk Exp $ */ +/* $OpenBSD: vmd.h,v 1.48 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -236,6 +236,7 @@ int vmm_pipe(struct vmd_vm *, int, void (*)(int, short, void *)); /* vm.c */ int start_vm(struct vmd_vm *, int); +__dead void vm_shutdown(unsigned int); /* control.c */ int config_init(struct vmd *); diff --git a/usr.sbin/vmd/vmm.c b/usr.sbin/vmd/vmm.c index ef4bc810876..9802ab2ca91 100644 --- a/usr.sbin/vmd/vmm.c +++ b/usr.sbin/vmd/vmm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: vmm.c,v 1.66 2017/03/01 18:00:50 reyk Exp $ */ +/* $OpenBSD: vmm.c,v 1.67 2017/03/15 18:06:18 reyk Exp $ */ /* * Copyright (c) 2015 Mike Larkin <mlarkin@openbsd.org> @@ -377,6 +377,12 @@ vmm_dispatch_vm(int fd, short event, void *arg) #endif switch (imsg.hdr.type) { + case IMSG_VMDOP_VM_SHUTDOWN: + vm->vm_shutdown = 1; + break; + case IMSG_VMDOP_VM_REBOOT: + vm->vm_shutdown = 0; + break; default: fatalx("%s: got invalid imsg %d from %s", __func__, imsg.hdr.type, |