]> Pileus Git - ~andy/linux/blobdiff - arch/x86/xen/p2m.c
Merge tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb...
[~andy/linux] / arch / x86 / xen / p2m.c
index 76ba0e97e530131199e40aec0730554ae7b7825c..95fb2aa5927efc4678eccd8a51d91044b4dd9adb 100644 (file)
@@ -22,7 +22,7 @@
  *
  * P2M_PER_PAGE depends on the architecture, as a mfn is always
  * unsigned long (8 bytes on 64-bit, 4 bytes on 32), leading to
- * 512 and 1024 entries respectively. 
+ * 512 and 1024 entries respectively.
  *
  * In short, these structures contain the Machine Frame Number (MFN) of the PFN.
  *
  *      /    | ~0, ~0, ....  |
  *     |     \---------------/
  *     |
- *     p2m_missing             p2m_missing
- * /------------------\     /------------\
- * | [p2m_mid_missing]+---->| ~0, ~0, ~0 |
- * | [p2m_mid_missing]+---->| ..., ~0    |
- * \------------------/     \------------/
+ *   p2m_mid_missing           p2m_missing
+ * /-----------------\     /------------\
+ * | [p2m_missing]   +---->| ~0, ~0, ~0 |
+ * | [p2m_missing]   +---->| ..., ~0    |
+ * \-----------------/     \------------/
  *
  * where ~0 is INVALID_P2M_ENTRY. IDENTITY is (PFN | IDENTITY_BIT)
  */
@@ -396,7 +396,85 @@ void __init xen_build_dynamic_phys_to_machine(void)
 
        m2p_override_init();
 }
+#ifdef CONFIG_X86_64
+#include <linux/bootmem.h>
+unsigned long __init xen_revector_p2m_tree(void)
+{
+       unsigned long va_start;
+       unsigned long va_end;
+       unsigned long pfn;
+       unsigned long pfn_free = 0;
+       unsigned long *mfn_list = NULL;
+       unsigned long size;
+
+       va_start = xen_start_info->mfn_list;
+       /*We copy in increments of P2M_PER_PAGE * sizeof(unsigned long),
+        * so make sure it is rounded up to that */
+       size = PAGE_ALIGN(xen_start_info->nr_pages * sizeof(unsigned long));
+       va_end = va_start + size;
+
+       /* If we were revectored already, don't do it again. */
+       if (va_start <= __START_KERNEL_map && va_start >= __PAGE_OFFSET)
+               return 0;
+
+       mfn_list = alloc_bootmem_align(size, PAGE_SIZE);
+       if (!mfn_list) {
+               pr_warn("Could not allocate space for a new P2M tree!\n");
+               return xen_start_info->mfn_list;
+       }
+       /* Fill it out with INVALID_P2M_ENTRY value */
+       memset(mfn_list, 0xFF, size);
+
+       for (pfn = 0; pfn < ALIGN(MAX_DOMAIN_PAGES, P2M_PER_PAGE); pfn += P2M_PER_PAGE) {
+               unsigned topidx = p2m_top_index(pfn);
+               unsigned mididx;
+               unsigned long *mid_p;
+
+               if (!p2m_top[topidx])
+                       continue;
 
+               if (p2m_top[topidx] == p2m_mid_missing)
+                       continue;
+
+               mididx = p2m_mid_index(pfn);
+               mid_p = p2m_top[topidx][mididx];
+               if (!mid_p)
+                       continue;
+               if ((mid_p == p2m_missing) || (mid_p == p2m_identity))
+                       continue;
+
+               if ((unsigned long)mid_p == INVALID_P2M_ENTRY)
+                       continue;
+
+               /* The old va. Rebase it on mfn_list */
+               if (mid_p >= (unsigned long *)va_start && mid_p <= (unsigned long *)va_end) {
+                       unsigned long *new;
+
+                       if (pfn_free  > (size / sizeof(unsigned long))) {
+                               WARN(1, "Only allocated for %ld pages, but we want %ld!\n",
+                                    size / sizeof(unsigned long), pfn_free);
+                               return 0;
+                       }
+                       new = &mfn_list[pfn_free];
+
+                       copy_page(new, mid_p);
+                       p2m_top[topidx][mididx] = &mfn_list[pfn_free];
+                       p2m_top_mfn_p[topidx][mididx] = virt_to_mfn(&mfn_list[pfn_free]);
+
+                       pfn_free += P2M_PER_PAGE;
+
+               }
+               /* This should be the leafs allocated for identity from _brk. */
+       }
+       return (unsigned long)mfn_list;
+
+}
+#else
+unsigned long __init xen_revector_p2m_tree(void)
+{
+       return 0;
+}
+#endif
 unsigned long get_phys_to_machine(unsigned long pfn)
 {
        unsigned topidx, mididx, idx;
@@ -430,7 +508,7 @@ static void free_p2m_page(void *p)
        free_page((unsigned long)p);
 }
 
-/* 
+/*
  * Fully allocate the p2m structure for a given pfn.  We need to check
  * that both the top and mid levels are allocated, and make sure the
  * parallel mfn tree is kept in sync.  We may race with other cpus, so
@@ -828,9 +906,6 @@ int m2p_add_override(unsigned long mfn, struct page *page,
 
                        xen_mc_issue(PARAVIRT_LAZY_MMU);
                }
-               /* let's use dev_bus_addr to record the old mfn instead */
-               kmap_op->dev_bus_addr = page->index;
-               page->index = (unsigned long) kmap_op;
        }
        spin_lock_irqsave(&m2p_override_lock, flags);
        list_add(&page->lru,  &m2p_overrides[mfn_hash(mfn)]);
@@ -857,7 +932,8 @@ int m2p_add_override(unsigned long mfn, struct page *page,
        return 0;
 }
 EXPORT_SYMBOL_GPL(m2p_add_override);
-int m2p_remove_override(struct page *page, bool clear_pte)
+int m2p_remove_override(struct page *page,
+               struct gnttab_map_grant_ref *kmap_op)
 {
        unsigned long flags;
        unsigned long mfn;
@@ -887,10 +963,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
        WARN_ON(!PagePrivate(page));
        ClearPagePrivate(page);
 
-       if (clear_pte) {
-               struct gnttab_map_grant_ref *map_op =
-                       (struct gnttab_map_grant_ref *) page->index;
-               set_phys_to_machine(pfn, map_op->dev_bus_addr);
+       set_phys_to_machine(pfn, page->index);
+       if (kmap_op != NULL) {
                if (!PageHighMem(page)) {
                        struct multicall_space mcs;
                        struct gnttab_unmap_grant_ref *unmap_op;
@@ -902,13 +976,13 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                         * issued. In this case handle is going to -1 because
                         * it hasn't been modified yet.
                         */
-                       if (map_op->handle == -1)
+                       if (kmap_op->handle == -1)
                                xen_mc_flush();
                        /*
-                        * Now if map_op->handle is negative it means that the
+                        * Now if kmap_op->handle is negative it means that the
                         * hypercall actually returned an error.
                         */
-                       if (map_op->handle == GNTST_general_error) {
+                       if (kmap_op->handle == GNTST_general_error) {
                                printk(KERN_WARNING "m2p_remove_override: "
                                                "pfn %lx mfn %lx, failed to modify kernel mappings",
                                                pfn, mfn);
@@ -918,8 +992,8 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                        mcs = xen_mc_entry(
                                        sizeof(struct gnttab_unmap_grant_ref));
                        unmap_op = mcs.args;
-                       unmap_op->host_addr = map_op->host_addr;
-                       unmap_op->handle = map_op->handle;
+                       unmap_op->host_addr = kmap_op->host_addr;
+                       unmap_op->handle = kmap_op->handle;
                        unmap_op->dev_bus_addr = 0;
 
                        MULTI_grant_table_op(mcs.mc,
@@ -930,10 +1004,9 @@ int m2p_remove_override(struct page *page, bool clear_pte)
                        set_pte_at(&init_mm, address, ptep,
                                        pfn_pte(pfn, PAGE_KERNEL));
                        __flush_tlb_single(address);
-                       map_op->host_addr = 0;
+                       kmap_op->host_addr = 0;
                }
-       } else
-               set_phys_to_machine(pfn, page->index);
+       }
 
        /* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
         * somewhere in this domain, even before being added to the