Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0e91398f authored by Jeremy Fitzhardinge's avatar Jeremy Fitzhardinge Committed by Thomas Gleixner
Browse files

xen: implement save/restore



This patch implements Xen save/restore and migration.

Saving is triggered via xenbus, which is polled in
drivers/xen/manage.c.  When a suspend request comes in, the kernel
prepares itself for saving by:

1 - Freeze all processes.  This is primarily to prevent any
    partially-completed pagetable updates from confusing the suspend
    process.  If CONFIG_PREEMPT isn't defined, then this isn't necessary.

2 - Suspend xenbus and other devices

3 - Stop_machine, to make sure all the other vcpus are quiescent.  The
    Xen tools require the domain to run its save off vcpu0.

4 - Within the stop_machine state, it pins any unpinned pgds (under
    construction or destruction), performs canonicalizes various other
    pieces of state (mostly converting mfns to pfns), and finally

5 - Suspend the domain

Restore reverses the steps used to save the domain, ending when all
the frozen processes are thawed.

Signed-off-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
parent 7d88d32a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
obj-y		:= enlighten.o setup.o multicalls.o mmu.o \
			time.o xen-asm.o grant-table.o
			time.o xen-asm.o grant-table.o suspend.o

obj-$(CONFIG_SMP)	+= smp.o
+3 −3
Original line number Diff line number Diff line
@@ -857,7 +857,7 @@ static __init void xen_pagetable_setup_start(pgd_t *base)
			  PFN_DOWN(__pa(xen_start_info->pt_base)));
}

static __init void setup_shared_info(void)
void xen_setup_shared_info(void)
{
	if (!xen_feature(XENFEAT_auto_translated_physmap)) {
		unsigned long addr = fix_to_virt(FIX_PARAVIRT_BOOTMAP);
@@ -894,7 +894,7 @@ static __init void xen_pagetable_setup_done(pgd_t *base)
	pv_mmu_ops.release_pmd = xen_release_pmd;
	pv_mmu_ops.set_pte = xen_set_pte;

	setup_shared_info();
	xen_setup_shared_info();

	/* Actually pin the pagetable down, but we can't set PG_pinned
	   yet because the page structures don't exist yet. */
@@ -902,7 +902,7 @@ static __init void xen_pagetable_setup_done(pgd_t *base)
}

/* This is called once we have the cpu_possible_map */
void __init xen_setup_vcpu_info_placement(void)
void xen_setup_vcpu_info_placement(void)
{
	int cpu;

+46 −0
Original line number Diff line number Diff line
@@ -560,6 +560,29 @@ void xen_pgd_pin(pgd_t *pgd)
	xen_mc_issue(0);
}

/*
 * On save, we need to pin all pagetables to make sure they get their
 * mfns turned into pfns.  Search the list for any unpinned pgds and pin
 * them (unpinned pgds are not currently in use, probably because the
 * process is under construction or destruction).
 */
void xen_mm_pin_all(void)
{
	unsigned long flags;
	struct page *page;

	spin_lock_irqsave(&pgd_lock, flags);

	list_for_each_entry(page, &pgd_list, lru) {
		if (!PagePinned(page)) {
			xen_pgd_pin((pgd_t *)page_address(page));
			SetPageSavePinned(page);
		}
	}

	spin_unlock_irqrestore(&pgd_lock, flags);
}

/* The init_mm pagetable is really pinned as soon as its created, but
   that's before we have page structures to store the bits.  So do all
   the book-keeping now. */
@@ -617,6 +640,29 @@ static void xen_pgd_unpin(pgd_t *pgd)
	xen_mc_issue(0);
}

/*
 * On resume, undo any pinning done at save, so that the rest of the
 * kernel doesn't see any unexpected pinned pagetables.
 */
void xen_mm_unpin_all(void)
{
	unsigned long flags;
	struct page *page;

	spin_lock_irqsave(&pgd_lock, flags);

	list_for_each_entry(page, &pgd_list, lru) {
		if (PageSavePinned(page)) {
			BUG_ON(!PagePinned(page));
			printk("unpinning pinned %p\n", page_address(page));
			xen_pgd_unpin((pgd_t *)page_address(page));
			ClearPageSavePinned(page);
		}
	}

	spin_unlock_irqrestore(&pgd_lock, flags);
}

void xen_activate_mm(struct mm_struct *prev, struct mm_struct *next)
{
	spin_lock(&next->page_table_lock);
+1 −1
Original line number Diff line number Diff line
@@ -35,7 +35,7 @@
#include "xen-ops.h"
#include "mmu.h"

static cpumask_t xen_cpu_initialized_map;
cpumask_t xen_cpu_initialized_map;
static DEFINE_PER_CPU(int, resched_irq) = -1;
static DEFINE_PER_CPU(int, callfunc_irq) = -1;
static DEFINE_PER_CPU(int, debug_irq) = -1;

arch/x86/xen/suspend.c

0 → 100644
+42 −0
Original line number Diff line number Diff line
#include <linux/types.h>

#include <xen/interface/xen.h>
#include <xen/grant_table.h>
#include <xen/events.h>

#include <asm/xen/hypercall.h>
#include <asm/xen/page.h>

#include "xen-ops.h"
#include "mmu.h"

void xen_pre_suspend(void)
{
	xen_start_info->store_mfn = mfn_to_pfn(xen_start_info->store_mfn);
	xen_start_info->console.domU.mfn =
		mfn_to_pfn(xen_start_info->console.domU.mfn);

	BUG_ON(!irqs_disabled());

	HYPERVISOR_shared_info = &xen_dummy_shared_info;
	if (HYPERVISOR_update_va_mapping(fix_to_virt(FIX_PARAVIRT_BOOTMAP),
					 __pte_ma(0), 0))
		BUG();
}

void xen_post_suspend(int suspend_cancelled)
{
	if (suspend_cancelled) {
		xen_start_info->store_mfn =
			pfn_to_mfn(xen_start_info->store_mfn);
		xen_start_info->console.domU.mfn =
			pfn_to_mfn(xen_start_info->console.domU.mfn);
	} else {
#ifdef CONFIG_SMP
		xen_cpu_initialized_map = cpu_online_map;
#endif
	}

	xen_setup_shared_info();
}
Loading