From 8dee0e9bd64849deca80da9052c806e764ef026c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Roger=20Pau=20Monn=C3=A9?= Date: Tue, 7 Mar 2017 09:16:51 +0000 Subject: [PATCH] xen: add support for canceled suspend When running on Xen, it's possible that a suspend request to the hypervisor fails (return from HYPERVISOR_suspend different than 0). This means that the suspend hasn't succeed, and the resume procedure needs to properly handle this case. First of all, when such situation happens there's no need to reset the vector callback, hypercall page, shared info, event channels or grant table, because it's state is preserved. Also, the PV drivers don't need to be reset to the initial state, since the connection with the backed has not been interrupted. Submitted by: Liuyingdong Reviewed by: royger MFC after: 2 weeks Differential revision: https://reviews.freebsd.org/D9635 --- sys/dev/xen/blkfront/blkfront.c | 5 +++++ sys/dev/xen/control/control.c | 14 +++++++++----- sys/dev/xen/netfront/netfront.c | 14 ++++++++++++++ sys/xen/xen-os.h | 2 ++ sys/xen/xenbus/xenbusb.c | 5 +++++ 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/sys/dev/xen/blkfront/blkfront.c b/sys/dev/xen/blkfront/blkfront.c index 9eca2201f89..d76fc01a4bd 100644 --- a/sys/dev/xen/blkfront/blkfront.c +++ b/sys/dev/xen/blkfront/blkfront.c @@ -1537,6 +1537,11 @@ xbd_resume(device_t dev) { struct xbd_softc *sc = device_get_softc(dev); + if (xen_suspend_cancelled) { + sc->xbd_state = XBD_STATE_CONNECTED; + return (0); + } + DPRINTK("xbd_resume: %s\n", xenbus_get_node(dev)); xbd_free(sc); diff --git a/sys/dev/xen/control/control.c b/sys/dev/xen/control/control.c index ae13c6c1007..d4249886332 100644 --- a/sys/dev/xen/control/control.c +++ b/sys/dev/xen/control/control.c @@ -148,6 +148,7 @@ __FBSDID("$FreeBSD$"); #include +bool xen_suspend_cancelled; /*--------------------------- Forward Declarations --------------------------*/ /** Function signature for shutdown event handlers. */ typedef void (xctrl_shutdown_handler_t)(void); @@ -196,7 +197,6 @@ xctrl_suspend() #ifdef SMP cpuset_t cpu_suspend_map; #endif - int suspend_cancelled; EVENTHANDLER_INVOKE(power_suspend_early); stop_all_proc(); @@ -267,16 +267,20 @@ xctrl_suspend() intr_suspend(); xen_hvm_suspend(); - suspend_cancelled = HYPERVISOR_suspend(0); + xen_suspend_cancelled = !!HYPERVISOR_suspend(0); - xen_hvm_resume(suspend_cancelled != 0); - intr_resume(suspend_cancelled != 0); + if (!xen_suspend_cancelled) { + xen_hvm_resume(false); + } + intr_resume(xen_suspend_cancelled != 0); enable_intr(); /* * Reset grant table info. */ - gnttab_resume(NULL); + if (!xen_suspend_cancelled) { + gnttab_resume(NULL); + } #ifdef SMP if (!CPU_EMPTY(&cpu_suspend_map)) { diff --git a/sys/dev/xen/netfront/netfront.c b/sys/dev/xen/netfront/netfront.c index 459712ab3fd..dea722a35a4 100644 --- a/sys/dev/xen/netfront/netfront.c +++ b/sys/dev/xen/netfront/netfront.c @@ -439,6 +439,20 @@ static int netfront_resume(device_t dev) { struct netfront_info *info = device_get_softc(dev); + u_int i; + + if (xen_suspend_cancelled) { + for (i = 0; i < info->num_queues; i++) { + XN_RX_LOCK(&info->rxq[i]); + XN_TX_LOCK(&info->txq[i]); + } + netfront_carrier_on(info); + for (i = 0; i < info->num_queues; i++) { + XN_RX_UNLOCK(&info->rxq[i]); + XN_TX_UNLOCK(&info->txq[i]); + } + return (0); + } netif_disconnect_backend(info); return (0); diff --git a/sys/xen/xen-os.h b/sys/xen/xen-os.h index 96e084fe601..044433ae3d3 100644 --- a/sys/xen/xen-os.h +++ b/sys/xen/xen-os.h @@ -56,6 +56,8 @@ extern char *console_page; extern int xen_disable_pv_disks; extern int xen_disable_pv_nics; +extern bool xen_suspend_cancelled; + enum xen_domain_type { XEN_NATIVE, /* running on bare hardware */ XEN_PV_DOMAIN, /* running in a PV domain */ diff --git a/sys/xen/xenbus/xenbusb.c b/sys/xen/xenbus/xenbusb.c index 3a0e54353cb..8b755e2a62c 100644 --- a/sys/xen/xenbus/xenbusb.c +++ b/sys/xen/xenbus/xenbusb.c @@ -791,6 +791,11 @@ xenbusb_resume(device_t dev) if (device_get_state(kids[i]) == DS_NOTPRESENT) continue; + if (xen_suspend_cancelled) { + DEVICE_RESUME(kids[i]); + continue; + } + ivars = device_get_ivars(kids[i]); xs_unregister_watch(&ivars->xd_otherend_watch);