]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
SPARC64: LDoms suspend domain service.
authorBijan Mottahedeh <bijan.mottahedeh@oracle.com>
Wed, 8 Jul 2015 23:35:03 +0000 (16:35 -0700)
committerSantosh Shilimkar <santosh.shilimkar@oracle.com>
Mon, 19 Oct 2015 20:41:00 +0000 (13:41 -0700)
Signed-off-by: Bijan Mottahedeh <bijan.mottahedeh@oracle.com>
Reviewd-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Reviewd-by: Aaron Young <aaron.young@oracle.com>
Orabug: 21970743
(cherry picked from commit 21d81b0e030cd18f85f7059ec304b7b8f3833162)

arch/sparc/Kconfig
arch/sparc/include/asm/hypervisor.h
arch/sparc/kernel/ds.c
arch/sparc/kernel/hvapi.c
arch/sparc/kernel/hvcalls.S

index c26416ff64c9ef4dba0d0af9398ad871e3f077e5..28b3e9b76755294fd57821e867ab9a15b649a863 100644 (file)
@@ -109,6 +109,9 @@ config HAVE_LATENCYTOP_SUPPORT
 config ARCH_HIBERNATION_POSSIBLE
        def_bool y if SPARC64
 
+config ARCH_SUSPEND_POSSIBLE
+       def_bool y if SPARC64
+
 config AUDIT_ARCH
        bool
        default y
index f5b6537306f0b3259e2313f3ec03b3ed32215c49..05d3a120e8654e8504c00616232e65c6db8f339c 100644 (file)
@@ -2937,6 +2937,12 @@ unsigned long sun4v_reboot_data_set(unsigned long ra,
                                    unsigned long len);
 #endif
 
+#define HV_FAST_GUEST_SUSPEND          0x181
+
+#ifndef __ASSEMBLY__
+unsigned long sun4v_guest_suspend(void);
+#endif
+
 #define HV_FAST_VT_GET_PERFREG         0x184
 #define HV_FAST_VT_SET_PERFREG         0x185
 
index 944620d1e07e3d0e84f3c03412ad27f2e52ceedd..062787045d7719ff7e182396c5ad2f37a1f65ebf 100644 (file)
 #include <linux/random.h>
 #include <linux/init.h>
 #include <linux/smp.h>
+#include <linux/pm.h>
+#include <linux/console.h>
+#include <linux/suspend.h>
+#include <linux/syscore_ops.h>
+#include <linux/stop_machine.h>
 
 #include <asm/hypervisor.h>
 #include <asm/ldc.h>
@@ -483,6 +488,28 @@ struct ds_panic_res {
        char                            reason[1];
 };
 
+struct ds_suspend_req {
+       __u64                           req_num;
+       __u64                           req_type;
+};
+
+struct ds_suspend_res {
+       __u64                           req_num;
+       __u32                           result;
+       __u32                           rec_result;
+       char                            reason[1];
+};
+
+#define        SUSPEND_PRE_SUCCESS             0x0
+#define        SUSPEND_PRE_FAILURE             0x1
+#define        SUSPEND_INVALID_MSG             0x2
+#define        SUSPEND_INPROGRESS              0x3
+#define        SUSPEND_FAILURE                 0x4
+#define        SUSPEND_POST_SUCCESS            0x5
+#define        SUSPEND_POST_FAILURE            0x6
+
+#define        SUSPEND_REC_SUCCESS             0x0
+
 struct ds_pri_msg {
        u64                             req_num;
        u64                             type;
@@ -594,6 +621,8 @@ static void ds_dom_shutdown_data_cb(ds_cb_arg_t arg,
        ds_svc_hdl_t hdl, void *buf, size_t len);
 static void ds_dom_panic_data_cb(ds_cb_arg_t arg,
        ds_svc_hdl_t hdl, void *buf, size_t len);
+static void ds_dom_suspend_data_cb(ds_cb_arg_t arg,
+       ds_svc_hdl_t hdl, void *buf, size_t len);
 #ifdef CONFIG_HOTPLUG_CPU
 static void ds_dr_cpu_data_cb(ds_cb_arg_t arg,
        ds_svc_hdl_t hdl, void *buf, size_t len);
@@ -637,7 +666,13 @@ static struct ds_builtin_service ds_primary_builtin_template[] = {
                                   NULL,
                                   ds_dom_panic_data_cb},
        },
-
+       {
+               .id             = "domain-suspend",
+               .vers           = {DS_CAP_MAJOR, DS_CAP_MINOR},
+               .ops            = {NULL,
+                                  NULL,
+                                  ds_dom_suspend_data_cb},
+       },
 #ifdef CONFIG_HOTPLUG_CPU
        {
                .id             = "dr-cpu",
@@ -1552,6 +1587,103 @@ static void ds_dom_panic_data_cb(ds_cb_arg_t arg,
        panic("PANIC requested.\n");
 }
 
+static int suspend_guest(void *data)
+{
+       int err;
+
+       err = syscore_suspend();
+       if (err)
+               return err;
+
+       pr_alert("Suspending the guest...\n");
+       err = sun4v_guest_suspend();
+
+       syscore_resume();
+
+       return err;
+}
+
+/*
+ * Copied from kernel_kexec().
+ * Added freeze_kernel_threads().
+ */
+static int suspend(void)
+{
+       int error = 0;
+
+       lock_system_sleep();
+       pm_prepare_console();
+       error = freeze_processes();
+       if (error)
+               goto restore_console;
+       error = freeze_kernel_threads();
+       if (error)
+               goto thaw_processes;
+       suspend_console();
+       error = dpm_suspend_start(PMSG_FREEZE);
+       if (error)
+               goto resume_console;
+
+       /* At this point, dpm_suspend_start() has been called,
+        * but *not* dpm_suspend_end(). We *must* call
+        * dpm_suspend_end() now.  Otherwise, drivers for
+        * some devices (e.g. interrupt controllers) become
+        * desynchronized with the actual state of the
+        * hardware at resume time, and evil weirdness ensues.
+        */
+       error = dpm_suspend_end(PMSG_FREEZE);
+       if (error)
+               goto resume_devices;
+
+       error = stop_machine(suspend_guest, NULL, NULL);
+
+resume_devices:
+       dpm_resume_start(PMSG_RESTORE);
+       dpm_resume_end(PMSG_RESTORE);
+resume_console:
+       resume_console();
+       thaw_kernel_threads();
+thaw_processes:
+       thaw_processes();
+restore_console:
+       pm_restore_console();
+       unlock_system_sleep();
+
+       return error;
+}
+
+static void ds_dom_suspend_data_cb(ds_cb_arg_t arg,
+               ds_svc_hdl_t handle, void *buf, size_t len)
+{
+       int rv;
+       struct ds_dev *ds = (struct ds_dev *)arg;
+       struct ds_suspend_req *rp;
+       struct ds_suspend_res res;
+
+       dprintk("entered.\n");
+
+       rp = (struct ds_suspend_req *)buf;
+
+       pr_alert("ds-%llu: Suspend request received.\n", ds->id);
+
+       res.req_num = rp->req_num;
+       res.result = SUSPEND_PRE_SUCCESS;
+       res.rec_result = SUSPEND_REC_SUCCESS;
+       res.reason[0] = 0;
+       rv = ds_cap_send(handle, &res, sizeof(struct ds_suspend_res));
+
+       if (rv)
+               pr_err("ds-%llu: ds_cap_send failed err=%d\n", ds->id, rv);
+       else {
+               rv = suspend();
+               dprintk("ds-%llu: rv=%d.\n", ds->id, rv);
+       }
+
+       res.result = rv ? SUSPEND_FAILURE : SUSPEND_POST_SUCCESS;
+       res.rec_result = SUSPEND_REC_SUCCESS;
+       ds_cap_send(handle, &res, sizeof(struct ds_suspend_res));
+}
+
 #ifdef CONFIG_HOTPLUG_CPU
 
 static void __dr_cpu_send_error(struct ds_dev *ds,
index 662500fa555f74160f6449143e6d0785af2643e9..e2e1e527cac4fc3222e8c92afae926238fdee9e6 100644 (file)
@@ -188,7 +188,7 @@ void __init sun4v_hvapi_init(void)
 
        group = HV_GRP_CORE;
        major = 1;
-       minor = 1;
+       minor = 2;
        if (sun4v_hvapi_register(group, major, &minor))
                goto bad;
 
index afbaba52d2f16cb30daf092f5cd3986e7ca9c299..12a5e66768852429861cd9dca85901bceb354569 100644 (file)
@@ -806,6 +806,13 @@ ENTRY(sun4v_reboot_data_set)
         nop
 ENDPROC(sun4v_reboot_data_set)
 
+ENTRY(sun4v_guest_suspend)
+       mov     HV_FAST_GUEST_SUSPEND, %o5
+       ta      HV_FAST_TRAP
+       retl
+        nop
+ENDPROC(sun4v_guest_suspend)
+
 ENTRY(sun4v_vt_get_perfreg)
        mov     %o1, %o4
        mov     HV_FAST_VT_GET_PERFREG, %o5