]> xenbits.xensource.com Git - people/aperard/linux.git/commitdiff
xen: remove scratch frames for ballooned pages and m2p override
authorDavid Vrabel <david.vrabel@citrix.com>
Mon, 5 Jan 2015 17:06:01 +0000 (17:06 +0000)
committerAnthony PERARD <anthony.perard@citrix.com>
Thu, 7 May 2015 16:55:58 +0000 (17:55 +0100)
The scratch frame mappings for ballooned pages and the m2p override
are broken.  Remove them in preparation for replacing them with
simpler mechanisms that works.

The scratch pages did not ensure that the page was not in use.  In
particular, the foreign page could still be in use by hardware.  If
the guest reused the frame the hardware could read or write that
frame.

The m2p override did not handle the same frame being granted by two
different grant references.  Trying an M2P override lookup in this
case is impossible.

With the m2p override removed, the grant map/unmap for the kernel
mappings (for x86 PV) can be easily batched in
set_foreign_p2m_mapping() and clear_foreign_p2m_mapping().

Signed-off-by: David Vrabel <david.vrabel@citrix.com>
Reviewed-by: Stefano Stabellini <stefano.stabellini@eu.citrix.com>
(cherry picked from commit 0bb599fd30108883b00c7d4a226eeb49111e6932)

 Conflicts:
arch/x86/include/asm/xen/page.h
arch/x86/xen/p2m.c

arch/x86/include/asm/xen/page.h
arch/x86/xen/p2m.c
drivers/xen/balloon.c

index 0e87f0d0ebcc24b4250932e06a83d9536b68b21d..60e26193a6d9438cdd17c3deca1fe6a28722a6d6 100644 (file)
@@ -61,7 +61,6 @@ extern int m2p_remove_override(struct page *page,
                               struct gnttab_map_grant_ref *kmap_op,
                               unsigned long mfn);
 extern struct page *m2p_find_override(unsigned long mfn);
-extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
 
 static inline unsigned long pfn_to_mfn(unsigned long pfn)
 {
@@ -117,21 +116,12 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn)
                return mfn;
 
        pfn = mfn_to_pfn_no_overrides(mfn);
-       if (get_phys_to_machine(pfn) != mfn) {
-               /*
-                * If this appears to be a foreign mfn (because the pfn
-                * doesn't map back to the mfn), then check the local override
-                * table to see if there's a better pfn to use.
-                *
-                * m2p_find_override_pfn returns ~0 if it doesn't find anything.
-                */
-               pfn = m2p_find_override_pfn(mfn, ~0);
-       }
+       if (get_phys_to_machine(pfn) != mfn)
+               pfn = ~0;
 
        /*
-        * pfn is ~0 if there are no entries in the m2p for mfn or if the
-        * entry doesn't map back to the mfn and m2p_override doesn't have a
-        * valid entry for it.
+        * pfn is ~0 if there are no entries in the m2p for mfn or the
+        * entry doesn't map back to the mfn.
         */
        if (pfn == ~0 &&
                        get_phys_to_machine(mfn) == IDENTITY_FRAME(mfn))
index b0564a455c030558df03ffa0198eacf69986894c..0a94db78278bfdd7341c37176f886a193e634ea0 100644 (file)
 #include "multicalls.h"
 #include "xen-ops.h"
 
-static void __init m2p_override_init(void);
-
 unsigned long xen_max_p2m_pfn __read_mostly;
 
 #define P2M_PER_PAGE           (PAGE_SIZE / sizeof(unsigned long))
@@ -420,8 +418,6 @@ void __init xen_build_dynamic_phys_to_machine(void)
                }
                p2m_top[topidx][mididx] = &mfn_list[pfn];
        }
-
-       m2p_override_init();
 }
 #ifdef CONFIG_X86_64
 #include <linux/bootmem.h>
@@ -917,45 +913,22 @@ bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
        return true;
 }
 
-#define M2P_OVERRIDE_HASH_SHIFT        10
-#define M2P_OVERRIDE_HASH      (1 << M2P_OVERRIDE_HASH_SHIFT)
-
-static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH);
-static DEFINE_SPINLOCK(m2p_override_lock);
-
-static void __init m2p_override_init(void)
-{
-       unsigned i;
-
-       m2p_overrides = extend_brk(sizeof(*m2p_overrides) * M2P_OVERRIDE_HASH,
-                                  sizeof(unsigned long));
-
-       for (i = 0; i < M2P_OVERRIDE_HASH; i++)
-               INIT_LIST_HEAD(&m2p_overrides[i]);
-}
-
-static unsigned long mfn_hash(unsigned long mfn)
-{
-       return hash_long(mfn, M2P_OVERRIDE_HASH_SHIFT);
-}
-
 int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
                            struct gnttab_map_grant_ref *kmap_ops,
                            struct page **pages, unsigned int count)
 {
        int i, ret = 0;
-       bool lazy = false;
        pte_t *pte;
 
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return 0;
 
-       if (kmap_ops &&
-           !in_interrupt() &&
-           paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
-               arch_enter_lazy_mmu_mode();
-               lazy = true;
-       }
+       if (kmap_ops) {
+               ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
+                                               kmap_ops, count);
+               if (ret)
+                       goto out;
+        }
 
        for (i = 0; i < count; i++) {
                unsigned long mfn, pfn;
@@ -982,95 +955,22 @@ int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
                        ret = -ENOMEM;
                        goto out;
                }
-
-               if (kmap_ops) {
-                       ret = m2p_add_override(mfn, pages[i], &kmap_ops[i]);
-                       if (ret)
-                               goto out;
-               }
        }
 
 out:
-       if (lazy)
-               arch_leave_lazy_mmu_mode();
-
        return ret;
 }
 EXPORT_SYMBOL_GPL(set_foreign_p2m_mapping);
 
-/* Add an MFN override for a particular page */
-int m2p_add_override(unsigned long mfn, struct page *page,
-               struct gnttab_map_grant_ref *kmap_op)
-{
-       unsigned long flags;
-       unsigned long pfn;
-       unsigned long uninitialized_var(address);
-       unsigned level;
-       pte_t *ptep = NULL;
-
-       pfn = page_to_pfn(page);
-       if (!PageHighMem(page)) {
-               address = (unsigned long)__va(pfn << PAGE_SHIFT);
-               ptep = lookup_address(address, &level);
-               if (WARN(ptep == NULL || level != PG_LEVEL_4K,
-                                       "m2p_add_override: pfn %lx not mapped", pfn))
-                       return -EINVAL;
-       }
-
-       if (kmap_op != NULL) {
-               if (!PageHighMem(page)) {
-                       struct multicall_space mcs =
-                               xen_mc_entry(sizeof(*kmap_op));
-
-                       MULTI_grant_table_op(mcs.mc,
-                                       GNTTABOP_map_grant_ref, kmap_op, 1);
-
-                       xen_mc_issue(PARAVIRT_LAZY_MMU);
-               }
-       }
-       spin_lock_irqsave(&m2p_override_lock, flags);
-       list_add(&page->lru,  &m2p_overrides[mfn_hash(mfn)]);
-       spin_unlock_irqrestore(&m2p_override_lock, flags);
-
-       /* p2m(m2p(mfn)) == mfn: the mfn is already present somewhere in
-        * this domain. Set the FOREIGN_FRAME_BIT in the p2m for the other
-        * pfn so that the following mfn_to_pfn(mfn) calls will return the
-        * pfn from the m2p_override (the backend pfn) instead.
-        * We need to do this because the pages shared by the frontend
-        * (xen-blkfront) can be already locked (lock_page, called by
-        * do_read_cache_page); when the userspace backend tries to use them
-        * with direct_IO, mfn_to_pfn returns the pfn of the frontend, so
-        * do_blockdev_direct_IO is going to try to lock the same pages
-        * again resulting in a deadlock.
-        * As a side effect get_user_pages_fast might not be safe on the
-        * frontend pages while they are being shared with the backend,
-        * because mfn_to_pfn (that ends up being called by GUPF) will
-        * return the backend pfn rather than the frontend pfn. */
-       pfn = mfn_to_pfn_no_overrides(mfn);
-       if (get_phys_to_machine(pfn) == mfn)
-               set_phys_to_machine(pfn, FOREIGN_FRAME(mfn));
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(m2p_add_override);
-
 int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
                              struct gnttab_unmap_grant_ref *kunmap_ops,
                              struct page **pages, unsigned int count)
 {
        int i, ret = 0;
-       bool lazy = false;
 
        if (xen_feature(XENFEAT_auto_translated_physmap))
                return 0;
 
-       if (kunmap_ops &&
-           !in_interrupt() &&
-           paravirt_get_lazy_mode() == PARAVIRT_LAZY_NONE) {
-               arch_enter_lazy_mmu_mode();
-               lazy = true;
-       }
-
        for (i = 0; i < count; i++) {
                unsigned long mfn = get_phys_to_machine(page_to_pfn(pages[i]));
                unsigned long pfn = page_to_pfn(pages[i]);
@@ -1085,150 +985,17 @@ int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
                ClearPagePrivate(pages[i]);
                set_phys_to_machine(pfn, pages[i]->index);
 
-               if (kunmap_ops)
-                       ret = m2p_remove_override(pages[i], &kunmap_ops[i], mfn);
-               if (ret)
-                       goto out;
        }
+        
+        if (kunmap_ops)
+                ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+                                                kunmap_ops, count);
 
 out:
-       if (lazy)
-               arch_leave_lazy_mmu_mode();
        return ret;
 }
 EXPORT_SYMBOL_GPL(clear_foreign_p2m_mapping);
 
-int m2p_remove_override(struct page *page,
-                       struct gnttab_unmap_grant_ref *kunmap_op,
-                       unsigned long mfn)
-{
-       unsigned long flags;
-       unsigned long pfn;
-       unsigned long uninitialized_var(address);
-       unsigned level;
-       pte_t *ptep = NULL;
-
-       pfn = page_to_pfn(page);
-
-       if (!PageHighMem(page)) {
-               address = (unsigned long)__va(pfn << PAGE_SHIFT);
-               ptep = lookup_address(address, &level);
-
-               if (WARN(ptep == NULL || level != PG_LEVEL_4K,
-                                       "m2p_remove_override: pfn %lx not mapped", pfn))
-                       return -EINVAL;
-       }
-
-       spin_lock_irqsave(&m2p_override_lock, flags);
-       list_del(&page->lru);
-       spin_unlock_irqrestore(&m2p_override_lock, flags);
-
-       if (kunmap_op != NULL) {
-               if (!PageHighMem(page)) {
-                       struct multicall_space mcs;
-                       struct gnttab_unmap_and_replace *unmap_op;
-                       struct page *scratch_page = get_balloon_scratch_page();
-                       unsigned long scratch_page_address = (unsigned long)
-                               __va(page_to_pfn(scratch_page) << PAGE_SHIFT);
-
-                       /*
-                        * It might be that we queued all the m2p grant table
-                        * hypercalls in a multicall, then m2p_remove_override
-                        * get called before the multicall has actually been
-                        * issued. In this case handle is going to -1 because
-                        * it hasn't been modified yet.
-                        */
-                       if (kunmap_op->handle == -1)
-                               xen_mc_flush();
-                       /*
-                        * Now if kmap_op->handle is negative it means that the
-                        * hypercall actually returned an error.
-                        */
-                       if (kunmap_op->handle == GNTST_general_error) {
-                               printk(KERN_WARNING "m2p_remove_override: "
-                                               "pfn %lx mfn %lx, failed to modify kernel mappings",
-                                               pfn, mfn);
-                               put_balloon_scratch_page();
-                               return -1;
-                       }
-
-                       xen_mc_batch();
-
-                       mcs = __xen_mc_entry(
-                                       sizeof(struct gnttab_unmap_and_replace));
-                       unmap_op = mcs.args;
-                       unmap_op->host_addr = kunmap_op->host_addr;
-                       unmap_op->new_addr = scratch_page_address;
-                       unmap_op->handle = kunmap_op->handle;
-
-                       MULTI_grant_table_op(mcs.mc,
-                                       GNTTABOP_unmap_and_replace, unmap_op, 1);
-
-                       mcs = __xen_mc_entry(0);
-                       MULTI_update_va_mapping(mcs.mc, scratch_page_address,
-                                       pfn_pte(page_to_pfn(scratch_page),
-                                       PAGE_KERNEL_RO), 0);
-
-                       xen_mc_issue(PARAVIRT_LAZY_MMU);
-
-                       put_balloon_scratch_page();
-               }
-       }
-
-       /* p2m(m2p(mfn)) == FOREIGN_FRAME(mfn): the mfn is already present
-        * somewhere in this domain, even before being added to the
-        * m2p_override (see comment above in m2p_add_override).
-        * If there are no other entries in the m2p_override corresponding
-        * to this mfn, then remove the FOREIGN_FRAME_BIT from the p2m for
-        * the original pfn (the one shared by the frontend): the backend
-        * cannot do any IO on this page anymore because it has been
-        * unshared. Removing the FOREIGN_FRAME_BIT from the p2m entry of
-        * the original pfn causes mfn_to_pfn(mfn) to return the frontend
-        * pfn again. */
-       mfn &= ~FOREIGN_FRAME_BIT;
-       pfn = mfn_to_pfn_no_overrides(mfn);
-       if (get_phys_to_machine(pfn) == FOREIGN_FRAME(mfn) &&
-                       m2p_find_override(mfn) == NULL)
-               set_phys_to_machine(pfn, mfn);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(m2p_remove_override);
-
-struct page *m2p_find_override(unsigned long mfn)
-{
-       unsigned long flags;
-       struct list_head *bucket = &m2p_overrides[mfn_hash(mfn)];
-       struct page *p, *ret;
-
-       ret = NULL;
-
-       spin_lock_irqsave(&m2p_override_lock, flags);
-
-       list_for_each_entry(p, bucket, lru) {
-               if (page_private(p) == mfn) {
-                       ret = p;
-                       break;
-               }
-       }
-
-       spin_unlock_irqrestore(&m2p_override_lock, flags);
-
-       return ret;
-}
-
-unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn)
-{
-       struct page *p = m2p_find_override(mfn);
-       unsigned long ret = pfn;
-
-       if (p)
-               ret = page_to_pfn(p);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(m2p_find_override_pfn);
-
 #ifdef CONFIG_XEN_DEBUG_FS
 #include <linux/debugfs.h>
 #include "debugfs.h"
index 5c660c77f03b58a32c24749b7053df6608a23230..b18667bb9eda853ab93652ef3440031d086b9821 100644 (file)
@@ -92,7 +92,6 @@ EXPORT_SYMBOL_GPL(balloon_stats);
 
 /* We increase/decrease in batches which fit in a page */
 static xen_pfn_t frame_list[PAGE_SIZE / sizeof(unsigned long)];
-static DEFINE_PER_CPU(struct page *, balloon_scratch_page);
 
 
 /* List of ballooned pages, threaded through the mem_map array. */
@@ -420,22 +419,12 @@ static enum bp_state decrease_reservation(unsigned long nr_pages, gfp_t gfp)
                page = pfn_to_page(pfn);
 
 #ifdef CONFIG_XEN_HAVE_PVMMU
-               /*
-                * Ballooned out frames are effectively replaced with
-                * a scratch frame.  Ensure direct mappings and the
-                * p2m are consistent.
-                */
                if (!xen_feature(XENFEAT_auto_translated_physmap)) {
                        if (!PageHighMem(page)) {
-                               struct page *scratch_page = get_balloon_scratch_page();
-
                                ret = HYPERVISOR_update_va_mapping(
                                                (unsigned long)__va(pfn << PAGE_SHIFT),
-                                               pfn_pte(page_to_pfn(scratch_page),
-                                                       PAGE_KERNEL_RO), 0);
+                                               __pte_ma(0), 0);
                                BUG_ON(ret);
-
-                               put_balloon_scratch_page();
                        }
                        __set_phys_to_machine(pfn, INVALID_P2M_ENTRY);
                }
@@ -497,18 +486,6 @@ static void balloon_process(struct work_struct *work)
        mutex_unlock(&balloon_mutex);
 }
 
-struct page *get_balloon_scratch_page(void)
-{
-       struct page *ret = get_cpu_var(balloon_scratch_page);
-       BUG_ON(ret == NULL);
-       return ret;
-}
-
-void put_balloon_scratch_page(void)
-{
-       put_cpu_var(balloon_scratch_page);
-}
-
 /* Resets the Xen limit, sets new target, and kicks off processing. */
 void balloon_set_new_target(unsigned long target)
 {
@@ -602,61 +579,13 @@ static void __init balloon_add_region(unsigned long start_pfn,
        }
 }
 
-static int alloc_balloon_scratch_page(int cpu)
-{
-       if (per_cpu(balloon_scratch_page, cpu) != NULL)
-               return 0;
-
-       per_cpu(balloon_scratch_page, cpu) = alloc_page(GFP_KERNEL);
-       if (per_cpu(balloon_scratch_page, cpu) == NULL) {
-               pr_warn("Failed to allocate balloon_scratch_page for cpu %d\n", cpu);
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-
-static int balloon_cpu_notify(struct notifier_block *self,
-                                   unsigned long action, void *hcpu)
-{
-       int cpu = (long)hcpu;
-       switch (action) {
-       case CPU_UP_PREPARE:
-               if (alloc_balloon_scratch_page(cpu))
-                       return NOTIFY_BAD;
-               break;
-       default:
-               break;
-       }
-       return NOTIFY_OK;
-}
-
-static struct notifier_block balloon_cpu_notifier = {
-       .notifier_call  = balloon_cpu_notify,
-};
-
 static int __init balloon_init(void)
 {
-       int i, cpu;
+       int i;
 
        if (!xen_domain())
                return -ENODEV;
 
-       if (!xen_feature(XENFEAT_auto_translated_physmap)) {
-               register_cpu_notifier(&balloon_cpu_notifier);
-
-               get_online_cpus();
-               for_each_online_cpu(cpu) {
-                       if (alloc_balloon_scratch_page(cpu)) {
-                               put_online_cpus();
-                               unregister_cpu_notifier(&balloon_cpu_notifier);
-                               return -ENOMEM;
-                       }
-               }
-               put_online_cpus();
-       }
-
        pr_info("Initialising balloon driver\n");
 
        balloon_stats.current_pages = xen_pv_domain()
@@ -693,15 +622,4 @@ static int __init balloon_init(void)
 
 subsys_initcall(balloon_init);
 
-static int __init balloon_clear(void)
-{
-       int cpu;
-
-       for_each_possible_cpu(cpu)
-               per_cpu(balloon_scratch_page, cpu) = NULL;
-
-       return 0;
-}
-early_initcall(balloon_clear);
-
 MODULE_LICENSE("GPL");