# HG changeset patch
# User Brendan Cully <brendan@xxxxxxxxx>
# Date 1166167313 28800
# Node ID 660b54dd9d6d7a8a33c583f6cabd4177c866d7b0
# Parent 9182ff9b291d7fef7e05c6899922e88c54d0e419
Ignore safe foreign maps in xc_linux_save.
When called via the checkpoint path, the guest retains references to
the start_info and grant table shared pages owned by Xen. Detect and
zero these in the save path instead of aborting due to an apparent
page table race. Grant table pages are found by walking the guest page
table mapped at suspend time, and only done for two-level page tables
in this patch.
Signed-off-by: Brendan Cully <brendan@xxxxxxxxx>
diff -r 9182ff9b291d -r 660b54dd9d6d tools/libxc/xc_linux_save.c
--- a/tools/libxc/xc_linux_save.c Thu Dec 14 23:05:42 2006 -0800
+++ b/tools/libxc/xc_linux_save.c Thu Dec 14 23:21:53 2006 -0800
@@ -44,6 +44,9 @@ static xen_pfn_t *live_p2m = NULL;
/* Live mapping of system MFN to PFN table. */
static xen_pfn_t *live_m2p = NULL;
+
+/* References to xen pages held by guest - should not count as races */
+static unsigned long foreign_maps[6];
/* grep fodder: machine_to_phys */
@@ -417,7 +420,8 @@ static int canonicalize_pagetable(unsign
const void *spage, void *dpage)
{
- int i, pte_last, xen_start, xen_end, race = 0;
+ int i, pte_last, xen_start, xen_end, race = 0;
+ unsigned long* foreign_map;
uint64_t pte;
/*
@@ -475,12 +479,22 @@ static int canonicalize_pagetable(unsign
mfn = (pte >> PAGE_SHIFT) & 0xfffffff;
if (!MFN_IS_IN_PSEUDOPHYS_MAP(mfn)) {
- /* This will happen if the type info is stale which
- is quite feasible under live migration */
- DPRINTF("PT Race: [%08lx,%d] pte=%llx, mfn=%08lx\n",
- type, i, (unsigned long long)pte, mfn);
+ /* zap foreign mappings which will be recreated on resume */
+ for (foreign_map = foreign_maps; *foreign_map; foreign_map++) {
+ if (mfn == *foreign_map) {
+ DPRINTF("Skipping legitimate mapping %08lx\n", mfn);
+ pte = 0;
+ break;
+ }
+ }
+ if (pte) {
+ /* This will happen if the type info is stale which
+ is quite feasible under live migration */
+ DPRINTF("PT Race: [%08lx,%d] pte=%llx, mfn=%08lx\n",
+ type, i, (unsigned long long)pte, mfn);
+ race = 1; /* inform the caller of race; fatal if !live */
+ }
pfn = 0; /* zap it - we'll retransmit this page later */
- race = 1; /* inform the caller of race; fatal if !live */
} else
pfn = mfn_to_pfn(mfn);
@@ -556,7 +570,99 @@ static xen_pfn_t *xc_map_m2p(int xc_hand
return m2p;
}
-
+static int virt_to_mfn(int xc_handle, int dom, unsigned long pt_mfn,
+ unsigned long va, unsigned long* mfn)
+{
+ unsigned long* pde, *l2;
+ unsigned long pte, l2_mfn;
+ int rc = -1;
+
+ /* TODO: support for other than two-level page tables */
+ if (pt_levels != 2)
+ return -1;
+
+ if (!MFN_IS_IN_PSEUDOPHYS_MAP(pt_mfn)) {
+ DPRINTF("Bad CR3 MFN %08lx\n", pt_mfn);
+ return -1;
+ }
+
+ pde = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ, pt_mfn);
+ pte = pde[l2_table_offset(va)];
+ if (pte & _PAGE_PRESENT) {
+ l2_mfn = (pte >> PAGE_SHIFT) & 0xfffffff;
+ if (!MFN_IS_IN_PSEUDOPHYS_MAP(l2_mfn)) {
+ DPRINTF("Bad L2 MFN %08lx\n", pt_mfn);
+ munmap(pde, PAGE_SIZE);
+ return -1;
+ }
+ l2 = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ,
l2_mfn);
+ pte = l2[l1_table_offset(va)];
+ if (pte & _PAGE_PRESENT) {
+ *mfn = (pte >> PAGE_SHIFT) & 0xfffffff;
+ rc = 0;
+ DPRINTF("VA %08lx maps to MFN %08lx\n", va, *mfn);
+ } else {
+ DPRINTF("MFN for %08lx not present\n", va);
+ }
+ munmap(l2, PAGE_SIZE);
+ } else {
+ DPRINTF("Page table for %08lx not present\n", va);
+ }
+
+ munmap(pde, PAGE_SIZE);
+
+ return rc;
+}
+
+/* record legitimate foreign mappings that shouldn't cause races
+ * when found in canonicalize_pagetables */
+static void find_foreign_maps(int xc_handle, int dom, unsigned long sif,
+ vcpu_guest_context_t* ctxt)
+{
+ unsigned long* page;
+ unsigned long suspend_mfn, gt_va;
+ int gt_len;
+ int i;
+
+ memset(foreign_maps, 0, sizeof(foreign_maps));
+ /* shared info page */
+ foreign_maps[0] = sif;
+
+ suspend_mfn = ctxt->user_regs.edx;
+ if (!MFN_IS_IN_PSEUDOPHYS_MAP(suspend_mfn)) {
+ DPRINTF("Bad suspend record MFN %08lx\n", suspend_mfn);
+ return;
+ }
+
+ /* track down grant table shared pages by walking the current guest
+ * page table to find the mfns of their virtual addresses */
+ /* this is a little silly */
+ page = xc_map_foreign_range(xc_handle, dom, PAGE_SIZE, PROT_READ,
+ suspend_mfn);
+ if (!page) {
+ DPRINTF("Failed to map suspend record\n");
+ return;
+ }
+
+ gt_va = ((start_info_t*)page)->pt_base;
+ gt_len = ((start_info_t*)page)->nr_pt_frames;
+ munmap(page, PAGE_SIZE);
+
+ if (gt_len > 4) {
+ /* probably not the grant table info (plain save) */
+ DPRINTF("No grant table info found in suspend record\n");
+ return;
+ }
+
+ DPRINTF("Grant table shared page base: %08lx, len: %d\n",
+ gt_va, gt_len);
+
+ for (i = 0; i < gt_len; i++) {
+ virt_to_mfn(xc_handle, dom, xen_cr3_to_pfn(ctxt->ctrlreg[3]),
+ gt_va + (i << PAGE_SHIFT),
+ foreign_maps + i + 1);
+ }
+}
int xc_linux_save(int xc_handle, int io_fd, uint32_t dom, uint32_t max_iters,
uint32_t max_factor, uint32_t flags, int (*suspend)(int))
@@ -607,7 +713,6 @@ int xc_linux_save(int xc_handle, int io_
unsigned long needed_to_fix = 0;
unsigned long total_sent = 0;
-
/* If no explicit control parameters given, use defaults */
if(!max_iters)
@@ -813,6 +918,8 @@ int xc_linux_save(int xc_handle, int io_
DPRINTF("Had %d unexplained entries in p2m table\n", err);
}
+ /* record legitimate foreign mappings */
+ find_foreign_maps(xc_handle, dom, shared_info_frame, &ctxt);
/* Start writing out the saved-domain record. */
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel
|