/arch/i386/mm/pageattr.c

https://bitbucket.org/evzijst/gittest · C · 221 lines · 159 code · 25 blank · 37 comment · 27 complexity · cf3ffafbccb859af7a0a8cb3a885d891 MD5 · raw file

  1. /*
  2. * Copyright 2002 Andi Kleen, SuSE Labs.
  3. * Thanks to Ben LaHaise for precious feedback.
  4. */
  5. #include <linux/config.h>
  6. #include <linux/mm.h>
  7. #include <linux/sched.h>
  8. #include <linux/highmem.h>
  9. #include <linux/module.h>
  10. #include <linux/slab.h>
  11. #include <asm/uaccess.h>
  12. #include <asm/processor.h>
  13. #include <asm/tlbflush.h>
  14. static DEFINE_SPINLOCK(cpa_lock);
  15. static struct list_head df_list = LIST_HEAD_INIT(df_list);
  16. pte_t *lookup_address(unsigned long address)
  17. {
  18. pgd_t *pgd = pgd_offset_k(address);
  19. pud_t *pud;
  20. pmd_t *pmd;
  21. if (pgd_none(*pgd))
  22. return NULL;
  23. pud = pud_offset(pgd, address);
  24. if (pud_none(*pud))
  25. return NULL;
  26. pmd = pmd_offset(pud, address);
  27. if (pmd_none(*pmd))
  28. return NULL;
  29. if (pmd_large(*pmd))
  30. return (pte_t *)pmd;
  31. return pte_offset_kernel(pmd, address);
  32. }
  33. static struct page *split_large_page(unsigned long address, pgprot_t prot)
  34. {
  35. int i;
  36. unsigned long addr;
  37. struct page *base;
  38. pte_t *pbase;
  39. spin_unlock_irq(&cpa_lock);
  40. base = alloc_pages(GFP_KERNEL, 0);
  41. spin_lock_irq(&cpa_lock);
  42. if (!base)
  43. return NULL;
  44. address = __pa(address);
  45. addr = address & LARGE_PAGE_MASK;
  46. pbase = (pte_t *)page_address(base);
  47. for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {
  48. pbase[i] = pfn_pte(addr >> PAGE_SHIFT,
  49. addr == address ? prot : PAGE_KERNEL);
  50. }
  51. return base;
  52. }
  53. static void flush_kernel_map(void *dummy)
  54. {
  55. /* Could use CLFLUSH here if the CPU supports it (Hammer,P4) */
  56. if (boot_cpu_data.x86_model >= 4)
  57. asm volatile("wbinvd":::"memory");
  58. /* Flush all to work around Errata in early athlons regarding
  59. * large page flushing.
  60. */
  61. __flush_tlb_all();
  62. }
  63. static void set_pmd_pte(pte_t *kpte, unsigned long address, pte_t pte)
  64. {
  65. struct page *page;
  66. unsigned long flags;
  67. set_pte_atomic(kpte, pte); /* change init_mm */
  68. if (PTRS_PER_PMD > 1)
  69. return;
  70. spin_lock_irqsave(&pgd_lock, flags);
  71. for (page = pgd_list; page; page = (struct page *)page->index) {
  72. pgd_t *pgd;
  73. pud_t *pud;
  74. pmd_t *pmd;
  75. pgd = (pgd_t *)page_address(page) + pgd_index(address);
  76. pud = pud_offset(pgd, address);
  77. pmd = pmd_offset(pud, address);
  78. set_pte_atomic((pte_t *)pmd, pte);
  79. }
  80. spin_unlock_irqrestore(&pgd_lock, flags);
  81. }
  82. /*
  83. * No more special protections in this 2/4MB area - revert to a
  84. * large page again.
  85. */
  86. static inline void revert_page(struct page *kpte_page, unsigned long address)
  87. {
  88. pte_t *linear = (pte_t *)
  89. pmd_offset(pud_offset(pgd_offset_k(address), address), address);
  90. set_pmd_pte(linear, address,
  91. pfn_pte((__pa(address) & LARGE_PAGE_MASK) >> PAGE_SHIFT,
  92. PAGE_KERNEL_LARGE));
  93. }
  94. static int
  95. __change_page_attr(struct page *page, pgprot_t prot)
  96. {
  97. pte_t *kpte;
  98. unsigned long address;
  99. struct page *kpte_page;
  100. BUG_ON(PageHighMem(page));
  101. address = (unsigned long)page_address(page);
  102. kpte = lookup_address(address);
  103. if (!kpte)
  104. return -EINVAL;
  105. kpte_page = virt_to_page(kpte);
  106. if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) {
  107. if ((pte_val(*kpte) & _PAGE_PSE) == 0) {
  108. set_pte_atomic(kpte, mk_pte(page, prot));
  109. } else {
  110. struct page *split = split_large_page(address, prot);
  111. if (!split)
  112. return -ENOMEM;
  113. set_pmd_pte(kpte,address,mk_pte(split, PAGE_KERNEL));
  114. kpte_page = split;
  115. }
  116. get_page(kpte_page);
  117. } else if ((pte_val(*kpte) & _PAGE_PSE) == 0) {
  118. set_pte_atomic(kpte, mk_pte(page, PAGE_KERNEL));
  119. __put_page(kpte_page);
  120. } else
  121. BUG();
  122. /*
  123. * If the pte was reserved, it means it was created at boot
  124. * time (not via split_large_page) and in turn we must not
  125. * replace it with a largepage.
  126. */
  127. if (!PageReserved(kpte_page)) {
  128. /* memleak and potential failed 2M page regeneration */
  129. BUG_ON(!page_count(kpte_page));
  130. if (cpu_has_pse && (page_count(kpte_page) == 1)) {
  131. list_add(&kpte_page->lru, &df_list);
  132. revert_page(kpte_page, address);
  133. }
  134. }
  135. return 0;
  136. }
  137. static inline void flush_map(void)
  138. {
  139. on_each_cpu(flush_kernel_map, NULL, 1, 1);
  140. }
  141. /*
  142. * Change the page attributes of an page in the linear mapping.
  143. *
  144. * This should be used when a page is mapped with a different caching policy
  145. * than write-back somewhere - some CPUs do not like it when mappings with
  146. * different caching policies exist. This changes the page attributes of the
  147. * in kernel linear mapping too.
  148. *
  149. * The caller needs to ensure that there are no conflicting mappings elsewhere.
  150. * This function only deals with the kernel linear map.
  151. *
  152. * Caller must call global_flush_tlb() after this.
  153. */
  154. int change_page_attr(struct page *page, int numpages, pgprot_t prot)
  155. {
  156. int err = 0;
  157. int i;
  158. unsigned long flags;
  159. spin_lock_irqsave(&cpa_lock, flags);
  160. for (i = 0; i < numpages; i++, page++) {
  161. err = __change_page_attr(page, prot);
  162. if (err)
  163. break;
  164. }
  165. spin_unlock_irqrestore(&cpa_lock, flags);
  166. return err;
  167. }
  168. void global_flush_tlb(void)
  169. {
  170. LIST_HEAD(l);
  171. struct page *pg, *next;
  172. BUG_ON(irqs_disabled());
  173. spin_lock_irq(&cpa_lock);
  174. list_splice_init(&df_list, &l);
  175. spin_unlock_irq(&cpa_lock);
  176. flush_map();
  177. list_for_each_entry_safe(pg, next, &l, lru)
  178. __free_page(pg);
  179. }
  180. #ifdef CONFIG_DEBUG_PAGEALLOC
  181. void kernel_map_pages(struct page *page, int numpages, int enable)
  182. {
  183. if (PageHighMem(page))
  184. return;
  185. /* the return value is ignored - the calls cannot fail,
  186. * large pages are disabled at boot time.
  187. */
  188. change_page_attr(page, numpages, enable ? PAGE_KERNEL : __pgprot(0));
  189. /* we should perform an IPI and flush all tlbs,
  190. * but that can deadlock->flush only current cpu.
  191. */
  192. __flush_tlb_all();
  193. }
  194. #endif
  195. EXPORT_SYMBOL(change_page_attr);
  196. EXPORT_SYMBOL(global_flush_tlb);