diff options
Diffstat (limited to 'arch/x86/xen/p2m.c')
-rw-r--r-- | arch/x86/xen/p2m.c | 128 |
1 files changed, 109 insertions, 19 deletions
diff --git a/arch/x86/xen/p2m.c b/arch/x86/xen/p2m.c index 58efeb9d5440..1b267e75158d 100644 --- a/arch/x86/xen/p2m.c +++ b/arch/x86/xen/p2m.c | |||
@@ -161,7 +161,9 @@ | |||
161 | #include <asm/xen/page.h> | 161 | #include <asm/xen/page.h> |
162 | #include <asm/xen/hypercall.h> | 162 | #include <asm/xen/hypercall.h> |
163 | #include <asm/xen/hypervisor.h> | 163 | #include <asm/xen/hypervisor.h> |
164 | #include <xen/grant_table.h> | ||
164 | 165 | ||
166 | #include "multicalls.h" | ||
165 | #include "xen-ops.h" | 167 | #include "xen-ops.h" |
166 | 168 | ||
167 | static void __init m2p_override_init(void); | 169 | static void __init m2p_override_init(void); |
@@ -676,7 +678,8 @@ static unsigned long mfn_hash(unsigned long mfn) | |||
676 | } | 678 | } |
677 | 679 | ||
678 | /* Add an MFN override for a particular page */ | 680 | /* Add an MFN override for a particular page */ |
679 | int m2p_add_override(unsigned long mfn, struct page *page, bool clear_pte) | 681 | int m2p_add_override(unsigned long mfn, struct page *page, |
682 | struct gnttab_map_grant_ref *kmap_op) | ||
680 | { | 683 | { |
681 | unsigned long flags; | 684 | unsigned long flags; |
682 | unsigned long pfn; | 685 | unsigned long pfn; |
@@ -692,16 +695,28 @@ int m2p_add_override(unsigned long mfn, struct page *page, bool clear_pte) | |||
692 | "m2p_add_override: pfn %lx not mapped", pfn)) | 695 | "m2p_add_override: pfn %lx not mapped", pfn)) |
693 | return -EINVAL; | 696 | return -EINVAL; |
694 | } | 697 | } |
695 | 698 | WARN_ON(PagePrivate(page)); | |
696 | page->private = mfn; | 699 | SetPagePrivate(page); |
700 | set_page_private(page, mfn); | ||
697 | page->index = pfn_to_mfn(pfn); | 701 | page->index = pfn_to_mfn(pfn); |
698 | 702 | ||
699 | if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)))) | 703 | if (unlikely(!set_phys_to_machine(pfn, FOREIGN_FRAME(mfn)))) |
700 | return -ENOMEM; | 704 | return -ENOMEM; |
701 | 705 | ||
702 | if (clear_pte && !PageHighMem(page)) | 706 | if (kmap_op != NULL) { |
703 | /* Just zap old mapping for now */ | 707 | if (!PageHighMem(page)) { |
704 | pte_clear(&init_mm, address, ptep); | 708 | struct multicall_space mcs = |
709 | xen_mc_entry(sizeof(*kmap_op)); | ||
710 | |||
711 | MULTI_grant_table_op(mcs.mc, | ||
712 | GNTTABOP_map_grant_ref, kmap_op, 1); | ||
713 | |||
714 | xen_mc_issue(PARAVIRT_LAZY_MMU); | ||
715 | } | ||
716 | /* let's use dev_bus_addr to record the old mfn instead */ | ||
717 | kmap_op->dev_bus_addr = page->index; | ||
718 | page->index = (unsigned long) kmap_op; | ||
719 | } | ||
705 | spin_lock_irqsave(&m2p_override_lock, flags); | 720 | spin_lock_irqsave(&m2p_override_lock, flags); |
706 | list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]); | 721 | list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]); |
707 | spin_unlock_irqrestore(&m2p_override_lock, flags); | 722 | spin_unlock_irqrestore(&m2p_override_lock, flags); |
@@ -735,13 +750,56 @@ int m2p_remove_override(struct page *page, bool clear_pte) | |||
735 | spin_lock_irqsave(&m2p_override_lock, flags); | 750 | spin_lock_irqsave(&m2p_override_lock, flags); |
736 | list_del(&page->lru); | 751 | list_del(&page->lru); |
737 | spin_unlock_irqrestore(&m2p_override_lock, flags); | 752 | spin_unlock_irqrestore(&m2p_override_lock, flags); |
738 | set_phys_to_machine(pfn, page->index); | 753 | WARN_ON(!PagePrivate(page)); |
754 | ClearPagePrivate(page); | ||
739 | 755 | ||
740 | if (clear_pte && !PageHighMem(page)) | 756 | if (clear_pte) { |
741 | set_pte_at(&init_mm, address, ptep, | 757 | struct gnttab_map_grant_ref *map_op = |
742 | pfn_pte(pfn, PAGE_KERNEL)); | 758 | (struct gnttab_map_grant_ref *) page->index; |
743 | /* No tlb flush necessary because the caller already | 759 | set_phys_to_machine(pfn, map_op->dev_bus_addr); |
744 | * left the pte unmapped. */ | 760 | if (!PageHighMem(page)) { |
761 | struct multicall_space mcs; | ||
762 | struct gnttab_unmap_grant_ref *unmap_op; | ||
763 | |||
764 | /* | ||
765 | * It might be that we queued all the m2p grant table | ||
766 | * hypercalls in a multicall, then m2p_remove_override | ||
767 | * get called before the multicall has actually been | ||
768 | * issued. In this case handle is going to -1 because | ||
769 | * it hasn't been modified yet. | ||
770 | */ | ||
771 | if (map_op->handle == -1) | ||
772 | xen_mc_flush(); | ||
773 | /* | ||
774 | * Now if map_op->handle is negative it means that the | ||
775 | * hypercall actually returned an error. | ||
776 | */ | ||
777 | if (map_op->handle == GNTST_general_error) { | ||
778 | printk(KERN_WARNING "m2p_remove_override: " | ||
779 | "pfn %lx mfn %lx, failed to modify kernel mappings", | ||
780 | pfn, mfn); | ||
781 | return -1; | ||
782 | } | ||
783 | |||
784 | mcs = xen_mc_entry( | ||
785 | sizeof(struct gnttab_unmap_grant_ref)); | ||
786 | unmap_op = mcs.args; | ||
787 | unmap_op->host_addr = map_op->host_addr; | ||
788 | unmap_op->handle = map_op->handle; | ||
789 | unmap_op->dev_bus_addr = 0; | ||
790 | |||
791 | MULTI_grant_table_op(mcs.mc, | ||
792 | GNTTABOP_unmap_grant_ref, unmap_op, 1); | ||
793 | |||
794 | xen_mc_issue(PARAVIRT_LAZY_MMU); | ||
795 | |||
796 | set_pte_at(&init_mm, address, ptep, | ||
797 | pfn_pte(pfn, PAGE_KERNEL)); | ||
798 | __flush_tlb_single(address); | ||
799 | map_op->host_addr = 0; | ||
800 | } | ||
801 | } else | ||
802 | set_phys_to_machine(pfn, page->index); | ||
745 | 803 | ||
746 | return 0; | 804 | return 0; |
747 | } | 805 | } |
@@ -758,7 +816,7 @@ struct page *m2p_find_override(unsigned long mfn) | |||
758 | spin_lock_irqsave(&m2p_override_lock, flags); | 816 | spin_lock_irqsave(&m2p_override_lock, flags); |
759 | 817 | ||
760 | list_for_each_entry(p, bucket, lru) { | 818 | list_for_each_entry(p, bucket, lru) { |
761 | if (p->private == mfn) { | 819 | if (page_private(p) == mfn) { |
762 | ret = p; | 820 | ret = p; |
763 | break; | 821 | break; |
764 | } | 822 | } |
@@ -782,17 +840,21 @@ unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn) | |||
782 | EXPORT_SYMBOL_GPL(m2p_find_override_pfn); | 840 | EXPORT_SYMBOL_GPL(m2p_find_override_pfn); |
783 | 841 | ||
784 | #ifdef CONFIG_XEN_DEBUG_FS | 842 | #ifdef CONFIG_XEN_DEBUG_FS |
785 | 843 | #include <linux/debugfs.h> | |
786 | int p2m_dump_show(struct seq_file *m, void *v) | 844 | #include "debugfs.h" |
845 | static int p2m_dump_show(struct seq_file *m, void *v) | ||
787 | { | 846 | { |
788 | static const char * const level_name[] = { "top", "middle", | 847 | static const char * const level_name[] = { "top", "middle", |
789 | "entry", "abnormal" }; | 848 | "entry", "abnormal", "error"}; |
790 | static const char * const type_name[] = { "identity", "missing", | ||
791 | "pfn", "abnormal"}; | ||
792 | #define TYPE_IDENTITY 0 | 849 | #define TYPE_IDENTITY 0 |
793 | #define TYPE_MISSING 1 | 850 | #define TYPE_MISSING 1 |
794 | #define TYPE_PFN 2 | 851 | #define TYPE_PFN 2 |
795 | #define TYPE_UNKNOWN 3 | 852 | #define TYPE_UNKNOWN 3 |
853 | static const char * const type_name[] = { | ||
854 | [TYPE_IDENTITY] = "identity", | ||
855 | [TYPE_MISSING] = "missing", | ||
856 | [TYPE_PFN] = "pfn", | ||
857 | [TYPE_UNKNOWN] = "abnormal"}; | ||
796 | unsigned long pfn, prev_pfn_type = 0, prev_pfn_level = 0; | 858 | unsigned long pfn, prev_pfn_type = 0, prev_pfn_level = 0; |
797 | unsigned int uninitialized_var(prev_level); | 859 | unsigned int uninitialized_var(prev_level); |
798 | unsigned int uninitialized_var(prev_type); | 860 | unsigned int uninitialized_var(prev_type); |
@@ -856,4 +918,32 @@ int p2m_dump_show(struct seq_file *m, void *v) | |||
856 | #undef TYPE_PFN | 918 | #undef TYPE_PFN |
857 | #undef TYPE_UNKNOWN | 919 | #undef TYPE_UNKNOWN |
858 | } | 920 | } |
859 | #endif | 921 | |
922 | static int p2m_dump_open(struct inode *inode, struct file *filp) | ||
923 | { | ||
924 | return single_open(filp, p2m_dump_show, NULL); | ||
925 | } | ||
926 | |||
927 | static const struct file_operations p2m_dump_fops = { | ||
928 | .open = p2m_dump_open, | ||
929 | .read = seq_read, | ||
930 | .llseek = seq_lseek, | ||
931 | .release = single_release, | ||
932 | }; | ||
933 | |||
934 | static struct dentry *d_mmu_debug; | ||
935 | |||
936 | static int __init xen_p2m_debugfs(void) | ||
937 | { | ||
938 | struct dentry *d_xen = xen_init_debugfs(); | ||
939 | |||
940 | if (d_xen == NULL) | ||
941 | return -ENOMEM; | ||
942 | |||
943 | d_mmu_debug = debugfs_create_dir("mmu", d_xen); | ||
944 | |||
945 | debugfs_create_file("p2m", 0600, d_mmu_debug, NULL, &p2m_dump_fops); | ||
946 | return 0; | ||
947 | } | ||
948 | fs_initcall(xen_p2m_debugfs); | ||
949 | #endif /* CONFIG_XEN_DEBUG_FS */ | ||