pageattr.c
上传用户:jlfgdled
上传日期:2013-04-10
资源大小:33168k
文件大小:5k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  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 <asm/uaccess.h>
  11. #include <asm/processor.h>
  12. #include <asm/io.h>
  13. static inline pte_t *lookup_address(unsigned long address) 
  14. pgd_t *pgd = pgd_offset_k(address);
  15. pmd_t *pmd;
  16. if (!pgd) return NULL; 
  17. pmd = pmd_offset(pgd, address);
  18. if (!pmd) return NULL; 
  19. if ((pmd_val(*pmd) & PAGE_LARGE) == PAGE_LARGE)
  20. return (pte_t *)pmd; 
  21.         return pte_offset(pmd, address);
  22. static struct page *split_large_page(unsigned long address, pgprot_t prot)
  23. int i; 
  24. unsigned long addr;
  25. struct page *base = alloc_pages(GFP_KERNEL, 0);
  26. pte_t *pbase;
  27. if (!base) 
  28. return NULL;
  29. address = __pa(address);
  30. addr = address & LARGE_PAGE_MASK; 
  31. pbase = (pte_t *)page_address(base);
  32. for (i = 0; i < PTRS_PER_PTE; i++, addr += PAGE_SIZE) {
  33. pbase[i] = mk_pte_phys(addr, 
  34.       addr == address ? prot : PAGE_KERNEL);
  35. }
  36. return base;
  37. static void flush_kernel_map(void * address) 
  38. {
  39. struct cpuinfo_x86 *cpu = &cpu_data[smp_processor_id()]; 
  40. wmb(); 
  41. /* Disabled for now because there seem to be some problems with CLFLUSH */
  42. if (0 && test_bit(X86_FEATURE_CLFLSH, &cpu->x86_capability)) { 
  43. /* is this worth it? */ 
  44. int i;
  45. for (i = 0; i < PAGE_SIZE; i += cpu->x86_clflush_size) 
  46. asm volatile("clflush %0" :: "m" (__pa(address) + i)); 
  47. } else
  48. asm volatile("wbinvd":::"memory"); 
  49. __flush_tlb_one(address);
  50. }
  51. /* no more special protections in this 2/4MB area - revert to a
  52.    large page again. */
  53. static inline void revert_page(struct page *kpte_page, unsigned long address)
  54. {
  55. pgd_t *pgd;
  56. pmd_t *pmd; 
  57. pte_t large_pte; 
  58. pgd = pgd_offset_k(address); 
  59. if (!pgd) BUG(); 
  60. pmd = pmd_offset(pgd, address);
  61. if (!pmd) BUG(); 
  62. if ((pmd_val(*pmd) & _PAGE_GLOBAL) == 0) BUG(); 
  63. large_pte = mk_pte_phys(__pa(address) & LARGE_PAGE_MASK, PAGE_KERNEL_LARGE); 
  64. set_pte((pte_t *)pmd, large_pte);
  65. }
  66.  
  67. /*
  68.  * Change the page attributes of an page in the linear mapping.
  69.  *
  70.  * This should be used when a page is mapped with a different caching policy
  71.  * than write-back somewhere - some CPUs do not like it when mappings with
  72.  * different caching policies exist. This changes the page attributes of the
  73.  * in kernel linear mapping too.
  74.  * 
  75.  * The caller needs to ensure that there are no conflicting mappings elsewhere.
  76.  * This function only deals with the kernel linear map.
  77.  * When page is in highmem it must never be kmap'ed.
  78.  */
  79. static int 
  80. __change_page_attr(unsigned long address, struct page *page, pgprot_t prot, 
  81.    struct page **oldpage) 
  82. pte_t *kpte; 
  83. struct page *kpte_page;
  84. kpte = lookup_address(address);
  85. if (!kpte) 
  86. return 0; /* not mapped in kernel */
  87. kpte_page = virt_to_page(((unsigned long)kpte) & PAGE_MASK);
  88. if (pgprot_val(prot) != pgprot_val(PAGE_KERNEL)) { 
  89. if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
  90. pte_t old = *kpte;
  91. pte_t standard = mk_pte(page, PAGE_KERNEL); 
  92. set_pte(kpte, mk_pte(page, prot)); 
  93. if (pte_same(old,standard))
  94. atomic_inc(&kpte_page->count);
  95. } else {
  96. struct page *split = split_large_page(address, prot); 
  97. if (!split)
  98. return -ENOMEM;
  99. set_pte(kpte,mk_pte(split, PAGE_KERNEL));
  100. }
  101. } else if ((pte_val(*kpte) & _PAGE_PSE) == 0) { 
  102. set_pte(kpte, mk_pte(page, PAGE_KERNEL));
  103. atomic_dec(&kpte_page->count); 
  104. }
  105. if (atomic_read(&kpte_page->count) == 1) { 
  106. *oldpage = kpte_page;
  107. revert_page(kpte_page, address);
  108. return 0;
  109. static inline void flush_and_free(void *address, struct page *fpage)
  110. {
  111. #ifdef CONFIG_SMP
  112. smp_call_function(flush_kernel_map, address, 1, 1);
  113. #endif
  114. flush_kernel_map(address); 
  115. if (fpage)
  116. __free_page(fpage); 
  117. }
  118. int change_page_attr(struct page *page, int numpages, pgprot_t prot)
  119. {
  120. int err = 0; 
  121. struct page *fpage, *fpage2; 
  122. int i; 
  123. down_write(&init_mm.mmap_sem);
  124. for (i = 0; i < numpages; i++, page++) { 
  125. fpage = fpage2 = NULL;
  126. err = __change_page_attr((unsigned long)page_address(page), 
  127.  page, prot, &fpage); 
  128. /* Handle kernel mapping too which aliases part of the lowmem */
  129. if (!err && page_to_phys(page) < KERNEL_TEXT_SIZE) { 
  130. err = __change_page_attr((unsigned long) __START_KERNEL_map + 
  131.  page_to_phys(page),
  132.  page, prot, &fpage2); 
  133. if (err) 
  134. break; 
  135. if (fpage || fpage2 || i == numpages-1) { 
  136. flush_and_free(page_address(page), fpage); 
  137. if (unlikely(fpage2 != NULL))
  138. flush_and_free((char *)__START_KERNEL_map + 
  139.        page_to_phys(page), fpage2);
  140. up_write(&init_mm.mmap_sem); 
  141. return err;
  142. }
  143. EXPORT_SYMBOL(change_page_attr);