umap.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:5k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

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