aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorIngo Molnar <mingo@elte.hu>2008-01-30 07:34:03 -0500
committerIngo Molnar <mingo@elte.hu>2008-01-30 07:34:03 -0500
commit6faa4c53b2f06fd271060761ce27f4f53289175c (patch)
treea10304d5ed232f9ad862dee6350cdfea5f4c9076 /arch
parent44136717e06b888c2d3129a80abf9ac79233f1eb (diff)
x86: 64-bit, add the new split_large_page() function
Signed-off-by: Ingo Molnar <mingo@elte.hu> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/mm/pageattr_64.c95
1 files changed, 71 insertions, 24 deletions
diff --git a/arch/x86/mm/pageattr_64.c b/arch/x86/mm/pageattr_64.c
index 190d4d37bc2f..139795029fb6 100644
--- a/arch/x86/mm/pageattr_64.c
+++ b/arch/x86/mm/pageattr_64.c
@@ -45,34 +45,91 @@ pte_t *lookup_address(unsigned long address, int *level)
45 return pte_offset_kernel(pmd, address); 45 return pte_offset_kernel(pmd, address);
46} 46}
47 47
48static struct page * 48static void __set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
49split_large_page(unsigned long address, pgprot_t ref_prot)
50{ 49{
50 /* change init_mm */
51 set_pte_atomic(kpte, pte);
52#ifdef CONFIG_X86_32
53 if (SHARED_KERNEL_PMD)
54 return;
55 {
56 struct page *page;
57
58 for (page = pgd_list; page; page = (struct page *)page->index) {
59 pgd_t *pgd;
60 pud_t *pud;
61 pmd_t *pmd;
62
63 pgd = (pgd_t *)page_address(page) + pgd_index(address);
64 pud = pud_offset(pgd, address);
65 pmd = pmd_offset(pud, address);
66 set_pte_atomic((pte_t *)pmd, pte);
67 }
68 }
69#endif
70}
71
72static int split_large_page(pte_t *kpte, unsigned long address)
73{
74 pgprot_t ref_prot = pte_pgprot(pte_clrhuge(*kpte));
75 gfp_t gfp_flags = GFP_KERNEL;
76 unsigned long flags;
51 unsigned long addr; 77 unsigned long addr;
78 pte_t *pbase, *tmp;
52 struct page *base; 79 struct page *base;
53 pte_t *pbase; 80 int i, level;
54 int i; 81
55 82
56 base = alloc_pages(GFP_KERNEL, 0); 83#ifdef CONFIG_DEBUG_PAGEALLOC
84 gfp_flags = GFP_ATOMIC;
85#endif
86 base = alloc_pages(gfp_flags, 0);
57 if (!base) 87 if (!base)
58 return NULL; 88 return -ENOMEM;
89
90 spin_lock_irqsave(&pgd_lock, flags);
91 /*
92 * Check for races, another CPU might have split this page
93 * up for us already:
94 */
95 tmp = lookup_address(address, &level);
96 if (tmp != kpte) {
97 WARN_ON_ONCE(1);
98 goto out_unlock;
99 }
59 100
60 address = __pa(address); 101 address = __pa(address);
61 addr = address & LARGE_PAGE_MASK; 102 addr = address & LARGE_PAGE_MASK;
62 pbase = (pte_t *)page_address(base); 103 pbase = (pte_t *)page_address(base);
104#ifdef CONFIG_X86_32
105 paravirt_alloc_pt(&init_mm, page_to_pfn(base));
106#endif
107
63 for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) 108 for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE)
64 pbase[i] = pfn_pte(addr >> PAGE_SHIFT, ref_prot); 109 set_pte(&pbase[i], pfn_pte(addr >> PAGE_SHIFT, ref_prot));
110
111 /*
112 * Install the new, split up pagetable:
113 */
114 __set_pmd_pte(kpte, address, mk_pte(base, ref_prot));
115 base = NULL;
116
117out_unlock:
118 spin_unlock_irqrestore(&pgd_lock, flags);
119
120 if (base)
121 __free_pages(base, 0);
65 122
66 return base; 123 return 0;
67} 124}
68 125
69static int 126static int
70__change_page_attr(unsigned long address, struct page *page, pgprot_t prot) 127__change_page_attr(unsigned long address, struct page *page, pgprot_t prot)
71{ 128{
129 pgprot_t ref_prot2, oldprot;
72 struct page *kpte_page; 130 struct page *kpte_page;
131 int level, err = 0;
73 pte_t *kpte; 132 pte_t *kpte;
74 pgprot_t ref_prot2, oldprot;
75 int level;
76 133
77repeat: 134repeat:
78 kpte = lookup_address(address, &level); 135 kpte = lookup_address(address, &level);
@@ -88,22 +145,12 @@ repeat:
88 if (level == 4) { 145 if (level == 4) {
89 set_pte_atomic(kpte, mk_pte(page, prot)); 146 set_pte_atomic(kpte, mk_pte(page, prot));
90 } else { 147 } else {
91 /* 148 err = split_large_page(kpte, address);
92 * split_large_page will take the reference for this 149 if (!err)
93 * change_page_attr on the split page. 150 goto repeat;
94 */
95 struct page *split;
96
97 ref_prot2 = pte_pgprot(pte_clrhuge(*kpte));
98 split = split_large_page(address, ref_prot2);
99 if (!split)
100 return -ENOMEM;
101 pgprot_val(ref_prot2) &= ~_PAGE_NX;
102 set_pte_atomic(kpte, mk_pte(split, ref_prot2));
103 goto repeat;
104 } 151 }
105 152
106 return 0; 153 return err;
107} 154}
108 155
109/** 156/**