aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/kvm
diff options
context:
space:
mode:
authorMarc Zyngier <marc.zyngier@arm.com>2013-08-06 08:05:48 -0400
committerChristoffer Dall <christoffer.dall@linaro.org>2013-08-07 21:17:39 -0400
commit979acd5e18c3e5cb7e3308c699d79553af5af8c6 (patch)
treecda729a9d652b3d19cb88efa78e846af4032cf67 /arch/arm/kvm
parentd3840b26614d8ce3db53c98061d9fcb1b9ccb0dd (diff)
arm64: KVM: fix 2-level page tables unmapping
When using 64kB pages, we only have two levels of page tables, meaning that PGD, PUD and PMD are fused. In this case, trying to refcount PUDs and PMDs independently is a a complete disaster, as they are the same. We manage to get it right for the allocation (stage2_set_pte uses {pmd,pud}_none), but the unmapping path clears both pud and pmd refcounts, which fails spectacularly with 2-level page tables. The fix is to avoid calling clear_pud_entry when both the pmd and pud pages are empty. For this, and instead of introducing another pud_empty function, consolidate both pte_empty and pmd_empty into page_empty (the code is actually identical) and use that to also test the validity of the pud. Signed-off-by: Marc Zyngier <marc.zyngier@arm.com> Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
Diffstat (limited to 'arch/arm/kvm')
-rw-r--r--arch/arm/kvm/mmu.c22
1 files changed, 8 insertions, 14 deletions
diff --git a/arch/arm/kvm/mmu.c b/arch/arm/kvm/mmu.c
index 80a83ec4a9ae..0988d9e04dd4 100644
--- a/arch/arm/kvm/mmu.c
+++ b/arch/arm/kvm/mmu.c
@@ -85,6 +85,12 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
85 return p; 85 return p;
86} 86}
87 87
88static bool page_empty(void *ptr)
89{
90 struct page *ptr_page = virt_to_page(ptr);
91 return page_count(ptr_page) == 1;
92}
93
88static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr) 94static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
89{ 95{
90 pmd_t *pmd_table = pmd_offset(pud, 0); 96 pmd_t *pmd_table = pmd_offset(pud, 0);
@@ -103,12 +109,6 @@ static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
103 put_page(virt_to_page(pmd)); 109 put_page(virt_to_page(pmd));
104} 110}
105 111
106static bool pmd_empty(pmd_t *pmd)
107{
108 struct page *pmd_page = virt_to_page(pmd);
109 return page_count(pmd_page) == 1;
110}
111
112static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr) 112static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
113{ 113{
114 if (pte_present(*pte)) { 114 if (pte_present(*pte)) {
@@ -118,12 +118,6 @@ static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
118 } 118 }
119} 119}
120 120
121static bool pte_empty(pte_t *pte)
122{
123 struct page *pte_page = virt_to_page(pte);
124 return page_count(pte_page) == 1;
125}
126
127static void unmap_range(struct kvm *kvm, pgd_t *pgdp, 121static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
128 unsigned long long start, u64 size) 122 unsigned long long start, u64 size)
129{ 123{
@@ -153,10 +147,10 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
153 next = addr + PAGE_SIZE; 147 next = addr + PAGE_SIZE;
154 148
155 /* If we emptied the pte, walk back up the ladder */ 149 /* If we emptied the pte, walk back up the ladder */
156 if (pte_empty(pte)) { 150 if (page_empty(pte)) {
157 clear_pmd_entry(kvm, pmd, addr); 151 clear_pmd_entry(kvm, pmd, addr);
158 next = pmd_addr_end(addr, end); 152 next = pmd_addr_end(addr, end);
159 if (pmd_empty(pmd)) { 153 if (page_empty(pmd) && !page_empty(pud)) {
160 clear_pud_entry(kvm, pud, addr); 154 clear_pud_entry(kvm, pud, addr);
161 next = pud_addr_end(addr, end); 155 next = pud_addr_end(addr, end);
162 } 156 }