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

Linux/Unix编程

开发平台:

Unix_Linux

  1. /*
  2.  * This file is subject to the terms and conditions of the GNU General Public
  3.  * License.  See the file "COPYING" in the main directory of this archive
  4.  * for more details.
  5.  *
  6.  * Copyright (C) 1994 Linus Torvalds
  7.  * Copyright (C) 1997 Miguel de Icaza
  8.  * Copyright (C) 2001 Ralf Baechle
  9.  */
  10. #include <linux/stat.h>
  11. #include <linux/sched.h>
  12. #include <linux/kernel.h>
  13. #include <linux/mm.h>
  14. #include <linux/smp.h>
  15. #include <linux/smp_lock.h>
  16. #include <linux/shm.h>
  17. #include <linux/errno.h>
  18. #include <linux/mman.h>
  19. #include <linux/module.h>
  20. #include <linux/string.h>
  21. #include <linux/vmalloc.h>
  22. #include <linux/swap.h>
  23. #include <asm/system.h>
  24. #include <asm/pgalloc.h>
  25. #include <asm/page.h>
  26. static inline void remove_mapping_pte_range (pmd_t *pmd, unsigned long address,
  27.      unsigned long size)
  28. {
  29. pte_t *pte;
  30. unsigned long end;
  31. if (pmd_none (*pmd))
  32. return;
  33. if (pmd_bad (*pmd)){
  34. printk ("remove_graphics_pte_range: bad pmd (%08lx)n",
  35. pmd_val (*pmd));
  36. pmd_clear (pmd);
  37. return;
  38. }
  39. pte = pte_offset (pmd, address);
  40. address &= ~PMD_MASK;
  41. end = address + size;
  42. if (end > PMD_SIZE)
  43. end = PMD_SIZE;
  44. do {
  45. pte_t entry = *pte;
  46. if (pte_present (entry))
  47. set_pte (pte, pte_modify (entry, PAGE_NONE));
  48. address += PAGE_SIZE;
  49. pte++;
  50. } while (address < end);
  51. }
  52. static inline void remove_mapping_pmd_range (pgd_t *pgd, unsigned long address,
  53.      unsigned long size)
  54. {
  55. pmd_t *pmd;
  56. unsigned long end;
  57. if (pgd_none (*pgd))
  58. return;
  59. if (pgd_bad (*pgd)){
  60. printk ("remove_graphics_pmd_range: bad pgd (%08lx)n",
  61. pgd_val (*pgd));
  62. pgd_clear (pgd);
  63. return;
  64. }
  65. pmd = pmd_offset (pgd, address);
  66. address &= ~PGDIR_MASK;
  67. end = address + size;
  68. if (end > PGDIR_SIZE)
  69. end = PGDIR_SIZE;
  70. do {
  71. remove_mapping_pte_range (pmd, address, end - address);
  72. address = (address + PMD_SIZE) & PMD_MASK;
  73. pmd++;
  74. } while (address < end);
  75. }
  76. /*
  77.  * This routine is called from the page fault handler to remove a
  78.  * range of active mappings at this point
  79.  */
  80. void remove_mapping (struct task_struct *task, unsigned long start,
  81.      unsigned long end)
  82. {
  83. unsigned long beg = start;
  84. pgd_t *dir;
  85. down_write (&task->mm->mmap_sem);
  86. dir = pgd_offset (task->mm, start);
  87. flush_cache_range (task->mm, beg, end);
  88. while (start < end){
  89. remove_mapping_pmd_range (dir, start, end - start);
  90. start = (start + PGDIR_SIZE) & PGDIR_MASK;
  91. dir++;
  92. }
  93. flush_tlb_range (task->mm, beg, end);
  94. up_write (&task->mm->mmap_sem);
  95. }
  96. EXPORT_SYMBOL(remove_mapping);
  97. void *vmalloc_uncached (unsigned long size)
  98. {
  99. return __vmalloc (size, GFP_KERNEL | __GFP_HIGHMEM,
  100. PAGE_KERNEL_UNCACHED);
  101. }
  102. static inline void free_pte(pte_t page)
  103. {
  104. if (pte_present(page)) {
  105. struct page *ptpage = pte_page(page);
  106. if ((!VALID_PAGE(ptpage)) || PageReserved(ptpage))
  107. return;
  108. __free_page(ptpage);
  109. if (current->mm->rss <= 0)
  110. return;
  111. current->mm->rss--;
  112. return;
  113. }
  114. swap_free(pte_to_swp_entry(page));
  115. }
  116. static inline void forget_pte(pte_t page)
  117. {
  118. if (!pte_none(page)) {
  119. printk("forget_pte: old mapping existed!n");
  120. free_pte(page);
  121. }
  122. }
  123. /*
  124.  * maps a range of vmalloc()ed memory into the requested pages. the old
  125.  * mappings are removed.
  126.  */
  127. static inline void vmap_pte_range (pte_t *pte, unsigned long address,
  128.    unsigned long size, unsigned long vaddr)
  129. {
  130. unsigned long end;
  131. pgd_t *vdir;
  132. pmd_t *vpmd;
  133. pte_t *vpte;
  134. address &= ~PMD_MASK;
  135. end = address + size;
  136. if (end > PMD_SIZE)
  137. end = PMD_SIZE;
  138. do {
  139. pte_t oldpage = *pte;
  140. struct page * page;
  141. pte_clear(pte);
  142. vdir = pgd_offset_k (vaddr);
  143. vpmd = pmd_offset (vdir, vaddr);
  144. vpte = pte_offset (vpmd, vaddr);
  145. page = pte_page (*vpte);
  146. set_pte(pte, mk_pte(page, PAGE_USERIO));
  147. forget_pte(oldpage);
  148. address += PAGE_SIZE;
  149. vaddr += PAGE_SIZE;
  150. pte++;
  151. } while (address < end);
  152. }
  153. static inline int vmap_pmd_range (pmd_t *pmd, unsigned long address,
  154.   unsigned long size, unsigned long vaddr)
  155. {
  156. unsigned long end;
  157. address &= ~PGDIR_MASK;
  158. end = address + size;
  159. if (end > PGDIR_SIZE)
  160. end = PGDIR_SIZE;
  161. vaddr -= address;
  162. do {
  163. pte_t * pte = pte_alloc(current->mm, pmd, address);
  164. if (!pte)
  165. return -ENOMEM;
  166. vmap_pte_range(pte, address, end - address, address + vaddr);
  167. address = (address + PMD_SIZE) & PMD_MASK;
  168. pmd++;
  169. } while (address < end);
  170. return 0;
  171. }
  172. int vmap_page_range (unsigned long from, unsigned long size,
  173.      unsigned long vaddr)
  174. {
  175. int error = 0;
  176. pgd_t * dir;
  177. unsigned long beg = from;
  178. unsigned long end = from + size;
  179. vaddr -= from;
  180. dir = pgd_offset(current->mm, from);
  181. flush_cache_range(current->mm, beg, end);
  182. while (from < end) {
  183. pmd_t *pmd = pmd_alloc(current->mm, dir, from);
  184. error = -ENOMEM;
  185. if (!pmd)
  186. break;
  187. error = vmap_pmd_range(pmd, from, end - from, vaddr + from);
  188. if (error)
  189. break;
  190. from = (from + PGDIR_SIZE) & PGDIR_MASK;
  191. dir++;
  192. }
  193. flush_tlb_range(current->mm, beg, end);
  194. return error;
  195. }