From: jbeulich@novell.com
Subject: use base kernel suspend/resume infrastructure
Patch-mainline: n/a
... rather than calling just a few functions explicitly.
--- head.orig/arch/x86/kernel/time-xen.c 2012-04-11 13:26:23.000000000 +0200
+++ head/arch/x86/kernel/time-xen.c 2012-04-11 16:59:35.000000000 +0200
@@ -68,6 +68,10 @@ DEFINE_PER_CPU(struct vcpu_runstate_info
/* Must be signed, as it's compared with s64 quantities which can be -ve. */
#define NS_PER_TICK (1000000000LL/HZ)
+static struct vcpu_set_periodic_timer xen_set_periodic_tick = {
+ .period_ns = NS_PER_TICK
+};
+
/*
* GCC 4.3 can turn loops over an induction variable into division. We do
* not support arbitrary 64-bit division, and so must break the induction.
@@ -565,6 +569,17 @@ void mark_tsc_unstable(char *reason)
}
EXPORT_SYMBOL_GPL(mark_tsc_unstable);
+static void init_missing_ticks_accounting(unsigned int cpu)
+{
+ struct vcpu_runstate_info *runstate = setup_runstate_area(cpu);
+
+ per_cpu(processed_blocked_time, cpu) =
+ runstate->time[RUNSTATE_blocked];
+ per_cpu(processed_stolen_time, cpu) =
+ runstate->time[RUNSTATE_runnable] +
+ runstate->time[RUNSTATE_offline];
+}
+
static cycle_t cs_last;
static cycle_t xen_clocksource_read(struct clocksource *cs)
@@ -601,11 +616,32 @@ static cycle_t xen_clocksource_read(stru
#endif
}
+/* No locking required. Interrupts are disabled on all CPUs. */
static void xen_clocksource_resume(struct clocksource *cs)
{
- extern void time_resume(void);
+ unsigned int cpu;
+
+ init_cpu_khz();
+
+ for_each_online_cpu(cpu) {
+ switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu,
+ &xen_set_periodic_tick)) {
+ case 0:
+#if CONFIG_XEN_COMPAT <= 0x030004
+ case -ENOSYS:
+#endif
+ break;
+ default:
+ BUG();
+ }
+ get_time_values_from_xen(cpu);
+ per_cpu(processed_system_time, cpu) =
+ per_cpu(shadow_time, 0).system_timestamp;
+ init_missing_ticks_accounting(cpu);
+ }
+
+ processed_system_time = per_cpu(shadow_time, 0).system_timestamp;
- time_resume();
cs_last = local_clock();
}
@@ -637,17 +673,6 @@ struct vcpu_runstate_info *setup_runstat
return rs;
}
-static void init_missing_ticks_accounting(unsigned int cpu)
-{
- struct vcpu_runstate_info *runstate = setup_runstate_area(cpu);
-
- per_cpu(processed_blocked_time, cpu) =
- runstate->time[RUNSTATE_blocked];
- per_cpu(processed_stolen_time, cpu) =
- runstate->time[RUNSTATE_runnable] +
- runstate->time[RUNSTATE_offline];
-}
-
/* Dynamically-mapped IRQ. */
DEFINE_PER_CPU(int, timer_irq);
@@ -664,10 +689,6 @@ static void setup_cpu0_timer_irq(void)
BUG_ON(per_cpu(timer_irq, 0) < 0);
}
-static struct vcpu_set_periodic_timer xen_set_periodic_tick = {
- .period_ns = NS_PER_TICK
-};
-
static void __init _late_time_init(void)
{
update_wallclock(false);
@@ -810,35 +831,6 @@ void xen_halt(void)
VOID(HYPERVISOR_vcpu_op(VCPUOP_down, smp_processor_id(), NULL));
}
-/* No locking required. Interrupts are disabled on all CPUs. */
-void time_resume(void)
-{
- unsigned int cpu;
-
- init_cpu_khz();
-
- for_each_online_cpu(cpu) {
- switch (HYPERVISOR_vcpu_op(VCPUOP_set_periodic_timer, cpu,
- &xen_set_periodic_tick)) {
- case 0:
-#if CONFIG_XEN_COMPAT <= 0x030004
- case -ENOSYS:
-#endif
- break;
- default:
- BUG();
- }
- get_time_values_from_xen(cpu);
- per_cpu(processed_system_time, cpu) =
- per_cpu(shadow_time, 0).system_timestamp;
- init_missing_ticks_accounting(cpu);
- }
-
- processed_system_time = per_cpu(shadow_time, 0).system_timestamp;
-
- update_wallclock(false);
-}
-
#ifdef CONFIG_SMP
static char timer_name[NR_CPUS][15];
--- head.orig/drivers/xen/core/evtchn.c 2012-04-12 09:56:01.000000000 +0200
+++ head/drivers/xen/core/evtchn.c 2012-06-08 10:39:05.000000000 +0200
@@ -1090,6 +1090,8 @@ int xen_test_irq_pending(int irq)
}
#ifdef CONFIG_PM_SLEEP
+#include <linux/syscore_ops.h>
+
static void restore_cpu_virqs(unsigned int cpu)
{
struct evtchn_bind_virq bind_virq;
@@ -1151,9 +1153,20 @@ static void restore_cpu_ipis(unsigned in
}
}
-void irq_resume(void)
+static void evtchn_resume(void)
{
unsigned int cpu, irq, evtchn;
+ struct evtchn_status status;
+
+ /* Avoid doing anything in the 'suspend cancelled' case. */
+ status.dom = DOMID_SELF;
+ status.port = evtchn_from_irq(__this_cpu_read(virq_to_irq[VIRQ_TIMER]));
+ if (HYPERVISOR_event_channel_op(EVTCHNOP_status, &status))
+ BUG();
+ if (status.status == EVTCHNSTAT_virq
+ && status.vcpu == smp_processor_id()
+ && status.u.virq == VIRQ_TIMER)
+ return;
init_evtchn_cpu_bindings();
@@ -1194,8 +1207,19 @@ void irq_resume(void)
restore_cpu_virqs(cpu);
restore_cpu_ipis(cpu);
}
+}
+static struct syscore_ops evtchn_syscore_ops = {
+ .resume = evtchn_resume,
+};
+
+static int __init evtchn_register(void)
+{
+ if (!is_initial_xendomain())
+ register_syscore_ops(&evtchn_syscore_ops);
+ return 0;
}
+core_initcall(evtchn_register);
#endif
int __init arch_early_irq_init(void)
--- head.orig/drivers/xen/core/gnttab.c 2012-03-12 16:18:44.000000000 +0100
+++ head/drivers/xen/core/gnttab.c 2012-05-23 13:46:56.000000000 +0200
@@ -795,20 +795,35 @@ EXPORT_SYMBOL(gnttab_post_map_adjust);
int gnttab_resume(void)
{
if (max_nr_grant_frames() < nr_grant_frames)
- return -ENOSYS;
+ return 0;
return gnttab_map(0, nr_grant_frames - 1);
}
#ifdef CONFIG_PM_SLEEP
-int gnttab_suspend(void)
-{
+#include <linux/syscore_ops.h>
+
#ifdef CONFIG_X86
+static int gnttab_suspend(void)
+{
apply_to_page_range(&init_mm, (unsigned long)shared,
PAGE_SIZE * nr_grant_frames,
unmap_pte_fn, NULL);
-#endif
return 0;
}
+#else
+#define gnttab_suspend NULL
+#endif
+
+static void _gnttab_resume(void)
+{
+ if (gnttab_resume())
+ BUG();
+}
+
+static struct syscore_ops gnttab_syscore_ops = {
+ .resume = _gnttab_resume,
+ .suspend = gnttab_suspend,
+};
#endif
#else /* !CONFIG_XEN */
@@ -936,6 +951,11 @@ int __devinit gnttab_init(void)
}
#endif
+#if defined(CONFIG_XEN) && defined(CONFIG_PM_SLEEP)
+ if (!is_initial_xendomain())
+ register_syscore_ops(&gnttab_syscore_ops);
+#endif
+
return 0;
ini_nomem:
--- head.orig/drivers/xen/core/machine_reboot.c 2011-11-18 15:43:23.000000000 +0100
+++ head/drivers/xen/core/machine_reboot.c 2012-04-11 16:09:22.000000000 +0200
@@ -6,6 +6,7 @@
#include <linux/sysrq.h>
#include <linux/stringify.h>
#include <linux/stop_machine.h>
+#include <linux/syscore_ops.h>
#include <asm/irq.h>
#include <asm/mmu_context.h>
#include <xen/evtchn.h>
@@ -16,6 +17,7 @@
#include <xen/xencons.h>
#include <xen/cpu_hotplug.h>
#include <xen/interface/vcpu.h>
+#include "../../base/base.h"
#if defined(__i386__) || defined(__x86_64__)
#include <asm/pci_x86.h>
@@ -139,50 +141,28 @@ struct suspend {
static int take_machine_down(void *_suspend)
{
struct suspend *suspend = _suspend;
- int suspend_cancelled, err;
- extern void time_resume(void);
+ int suspend_cancelled;
- if (suspend->fast_suspend) {
- BUG_ON(!irqs_disabled());
- } else {
- BUG_ON(irqs_disabled());
-
- for (;;) {
- err = smp_suspend();
- if (err)
- return err;
-
- xenbus_suspend();
- preempt_disable();
-
- if (num_online_cpus() == 1)
- break;
-
- preempt_enable();
- xenbus_suspend_cancel();
- }
-
- local_irq_disable();
- }
+ BUG_ON(!irqs_disabled());
mm_pin_all();
- gnttab_suspend();
- pre_suspend();
-
- /*
- * This hypercall returns 1 if suspend was cancelled or the domain was
- * merely checkpointed, and 0 if it is resuming in a new domain.
- */
- suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
+ suspend_cancelled = syscore_suspend();
+ if (!suspend_cancelled) {
+ pre_suspend();
+ /*
+ * This hypercall returns 1 if suspend was cancelled or the domain was
+ * merely checkpointed, and 0 if it is resuming in a new domain.
+ */
+ suspend_cancelled = HYPERVISOR_suspend(virt_to_mfn(xen_start_info));
+ } else
+ BUG_ON(suspend_cancelled > 0);
suspend->resume_notifier(suspend_cancelled);
- post_suspend(suspend_cancelled);
- gnttab_resume();
+ if (suspend_cancelled >= 0) {
+ post_suspend(suspend_cancelled);
+ syscore_resume();
+ }
if (!suspend_cancelled) {
- extern void spinlock_resume(void);
-
- spinlock_resume();
- irq_resume();
#ifdef __x86_64__
/*
* Older versions of Xen do not save/restore the user %cr3.
@@ -194,10 +174,6 @@ static int take_machine_down(void *_susp
current->active_mm->pgd)));
#endif
}
- time_resume();
-
- if (!suspend->fast_suspend)
- local_irq_enable();
return suspend_cancelled;
}
@@ -205,8 +181,14 @@ static int take_machine_down(void *_susp
int __xen_suspend(int fast_suspend, void (*resume_notifier)(int))
{
int err, suspend_cancelled;
+ const char *what;
struct suspend suspend;
+#define _check(fn, args...) ({ \
+ what = #fn; \
+ err = (fn)(args); \
+})
+
BUG_ON(smp_processor_id() != 0);
BUG_ON(in_interrupt());
@@ -224,30 +206,77 @@ int __xen_suspend(int fast_suspend, void
suspend.fast_suspend = fast_suspend;
suspend.resume_notifier = resume_notifier;
+ if (_check(dpm_suspend_start, PMSG_SUSPEND)) {
+ dpm_resume_end(PMSG_RESUME);
+ pr_err("%s() failed: %d\n", what, err);
+ return err;
+ }
+
if (fast_suspend) {
xenbus_suspend();
+
+ if (_check(dpm_suspend_end, PMSG_SUSPEND)) {
+ xenbus_suspend_cancel();
+ dpm_resume_end(PMSG_RESUME);
+ pr_err("%s() failed: %d\n", what, err);
+ return err;
+ }
+
err = stop_machine(take_machine_down, &suspend,
&cpumask_of_cpu(0));
if (err < 0)
xenbus_suspend_cancel();
} else {
+ BUG_ON(irqs_disabled());
+
+ for (;;) {
+ xenbus_suspend();
+
+ if (!_check(dpm_suspend_end, PMSG_SUSPEND)
+ && _check(smp_suspend))
+ dpm_resume_start(PMSG_RESUME);
+ if (err) {
+ xenbus_suspend_cancel();
+ dpm_resume_end(PMSG_RESUME);
+ pr_err("%s() failed: %d\n", what, err);
+ return err;
+ }
+
+ preempt_disable();
+
+ if (num_online_cpus() == 1)
+ break;
+
+ preempt_enable();
+
+ dpm_resume_start(PMSG_RESUME);
+
+ xenbus_suspend_cancel();
+ }
+
+ local_irq_disable();
err = take_machine_down(&suspend);
+ local_irq_enable();
}
- if (err < 0)
- return err;
+ dpm_resume_start(PMSG_RESUME);
- suspend_cancelled = err;
- if (!suspend_cancelled) {
- xencons_resume();
- xenbus_resume();
- } else {
- xenbus_suspend_cancel();
+ if (err >= 0) {
+ suspend_cancelled = err;
+ if (!suspend_cancelled) {
+ xencons_resume();
+ xenbus_resume();
+ } else {
+ xenbus_suspend_cancel();
+ err = 0;
+ }
+
+ if (!fast_suspend)
+ smp_resume();
}
- if (!fast_suspend)
- smp_resume();
+ dpm_resume_end(PMSG_RESUME);
- return 0;
+ return err;
}
#endif
--- head.orig/drivers/xen/core/spinlock.c 2012-02-09 12:50:33.000000000 +0100
+++ head/drivers/xen/core/spinlock.c 2012-02-01 09:18:11.000000000 +0100
@@ -64,7 +64,9 @@ void __cpuinit xen_spinlock_cleanup(unsi
}
#ifdef CONFIG_PM_SLEEP
-void __cpuinit spinlock_resume(void)
+#include <linux/syscore_ops.h>
+
+static void __cpuinit spinlock_resume(void)
{
unsigned int cpu;
@@ -73,6 +75,18 @@ void __cpuinit spinlock_resume(void)
xen_spinlock_init(cpu);
}
}
+
+static struct syscore_ops __cpuinitdata spinlock_syscore_ops = {
+ .resume = spinlock_resume
+};
+
+static int __init spinlock_register(void)
+{
+ if (!is_initial_xendomain())
+ register_syscore_ops(&spinlock_syscore_ops);
+ return 0;
+}
+core_initcall(spinlock_register);
#endif
static inline void sequence(unsigned int bias)
--- head.orig/include/xen/evtchn.h 2012-02-10 09:14:30.000000000 +0100
+++ head/include/xen/evtchn.h 2012-02-10 11:30:51.000000000 +0100
@@ -110,7 +110,9 @@ int bind_ipi_to_irqhandler(
*/
void unbind_from_irqhandler(unsigned int irq, void *dev_id);
+#ifndef CONFIG_XEN
void irq_resume(void);
+#endif
/* Entry point for notifications into Linux subsystems. */
asmlinkage void evtchn_do_upcall(struct pt_regs *regs);
--- head.orig/include/xen/gnttab.h 2012-03-12 13:39:32.000000000 +0100
+++ head/include/xen/gnttab.h 2012-03-12 14:00:59.000000000 +0100
@@ -113,8 +113,9 @@ static inline void __gnttab_dma_unmap_pa
void gnttab_reset_grant_page(struct page *page);
-int gnttab_suspend(void);
+#ifndef CONFIG_XEN
int gnttab_resume(void);
+#endif
void *arch_gnttab_alloc_shared(unsigned long *frames);