|
@@ -161,7 +161,9 @@
|
|
|
#include <asm/xen/page.h>
|
|
|
#include <asm/xen/hypercall.h>
|
|
|
#include <asm/xen/hypervisor.h>
|
|
|
+#include <xen/grant_table.h>
|
|
|
|
|
|
+#include "multicalls.h"
|
|
|
#include "xen-ops.h"
|
|
|
|
|
|
static void __init m2p_override_init(void);
|
|
@@ -676,7 +678,8 @@ static unsigned long mfn_hash(unsigned long mfn)
|
|
|
}
|
|
|
|
|
|
/* Add an MFN override for a particular page */
|
|
|
-int m2p_add_override(unsigned long mfn, struct page *page, bool clear_pte)
|
|
|
+int m2p_add_override(unsigned long mfn, struct page *page,
|
|
|
+ struct gnttab_map_grant_ref *kmap_op)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
unsigned long pfn;
|
|
@@ -700,9 +703,20 @@ int m2p_add_override(unsigned long mfn, struct page *page, bool clear_pte)
|
|
|
if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn))))
|
|
|
return -ENOMEM;
|
|
|
|
|
|
- if (clear_pte && !PageHighMem(page))
|
|
|
- /* Just zap old mapping for now */
|
|
|
- pte_clear(&init_mm, address, ptep);
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+ /* 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)]);
|
|
|
spin_unlock_irqrestore(&m2p_override_lock, flags);
|
|
@@ -736,14 +750,56 @@ int m2p_remove_override(struct page *page, bool clear_pte)
|
|
|
spin_lock_irqsave(&m2p_override_lock, flags);
|
|
|
list_del(&page->lru);
|
|
|
spin_unlock_irqrestore(&m2p_override_lock, flags);
|
|
|
- set_phys_to_machine(pfn, page->index);
|
|
|
WARN_ON(!PagePrivate(page));
|
|
|
ClearPagePrivate(page);
|
|
|
- if (clear_pte && !PageHighMem(page))
|
|
|
- set_pte_at(&init_mm, address, ptep,
|
|
|
- pfn_pte(pfn, PAGE_KERNEL));
|
|
|
- /* No tlb flush necessary because the caller already
|
|
|
- * left the pte unmapped. */
|
|
|
+
|
|
|
+ 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);
|
|
|
+ if (!PageHighMem(page)) {
|
|
|
+ struct multicall_space mcs;
|
|
|
+ struct gnttab_unmap_grant_ref *unmap_op;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * 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 (map_op->handle == -1)
|
|
|
+ xen_mc_flush();
|
|
|
+ /*
|
|
|
+ * Now if map_op->handle is negative it means that the
|
|
|
+ * hypercall actually returned an error.
|
|
|
+ */
|
|
|
+ if (map_op->handle == GNTST_general_error) {
|
|
|
+ printk(KERN_WARNING "m2p_remove_override: "
|
|
|
+ "pfn %lx mfn %lx, failed to modify kernel mappings",
|
|
|
+ pfn, mfn);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ 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->dev_bus_addr = 0;
|
|
|
+
|
|
|
+ MULTI_grant_table_op(mcs.mc,
|
|
|
+ GNTTABOP_unmap_grant_ref, unmap_op, 1);
|
|
|
+
|
|
|
+ xen_mc_issue(PARAVIRT_LAZY_MMU);
|
|
|
+
|
|
|
+ set_pte_at(&init_mm, address, ptep,
|
|
|
+ pfn_pte(pfn, PAGE_KERNEL));
|
|
|
+ __flush_tlb_single(address);
|
|
|
+ map_op->host_addr = 0;
|
|
|
+ }
|
|
|
+ } else
|
|
|
+ set_phys_to_machine(pfn, page->index);
|
|
|
|
|
|
return 0;
|
|
|
}
|