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

嵌入式Linux

开发平台:

Unix_Linux

  1. /*
  2.  * linux/mm/remap.c
  3.  *
  4.  * (C) Copyright 1996 Linus Torvalds
  5.  */
  6. #include <linux/slab.h>
  7. #include <linux/smp_lock.h>
  8. #include <linux/shm.h>
  9. #include <linux/mman.h>
  10. #include <linux/swap.h>
  11. #include <asm/uaccess.h>
  12. #include <asm/pgalloc.h>
  13. extern int vm_enough_memory(long pages);
  14. static inline pte_t *get_one_pte(struct mm_struct *mm, unsigned long addr)
  15. {
  16. pgd_t * pgd;
  17. pmd_t * pmd;
  18. pte_t * pte = NULL;
  19. pgd = pgd_offset(mm, addr);
  20. if (pgd_none(*pgd))
  21. goto end;
  22. if (pgd_bad(*pgd)) {
  23. pgd_ERROR(*pgd);
  24. pgd_clear(pgd);
  25. goto end;
  26. }
  27. pmd = pmd_offset(pgd, addr);
  28. if (pmd_none(*pmd))
  29. goto end;
  30. if (pmd_bad(*pmd)) {
  31. pmd_ERROR(*pmd);
  32. pmd_clear(pmd);
  33. goto end;
  34. }
  35. pte = pte_offset(pmd, addr);
  36. if (pte_none(*pte))
  37. pte = NULL;
  38. end:
  39. return pte;
  40. }
  41. static inline pte_t *alloc_one_pte(struct mm_struct *mm, unsigned long addr)
  42. {
  43. pmd_t * pmd;
  44. pte_t * pte = NULL;
  45. pmd = pmd_alloc(mm, pgd_offset(mm, addr), addr);
  46. if (pmd)
  47. pte = pte_alloc(mm, pmd, addr);
  48. return pte;
  49. }
  50. static inline int copy_one_pte(struct mm_struct *mm, pte_t * src, pte_t * dst)
  51. {
  52. int error = 0;
  53. pte_t pte;
  54. if (!pte_none(*src)) {
  55. pte = ptep_get_and_clear(src);
  56. if (!dst) {
  57. /* No dest?  We must put it back. */
  58. dst = src;
  59. error++;
  60. }
  61. set_pte(dst, pte);
  62. }
  63. return error;
  64. }
  65. static int move_one_page(struct mm_struct *mm, unsigned long old_addr, unsigned long new_addr)
  66. {
  67. int error = 0;
  68. pte_t * src;
  69. spin_lock(&mm->page_table_lock);
  70. src = get_one_pte(mm, old_addr);
  71. if (src)
  72. error = copy_one_pte(mm, src, alloc_one_pte(mm, new_addr));
  73. spin_unlock(&mm->page_table_lock);
  74. return error;
  75. }
  76. static int move_page_tables(struct mm_struct * mm,
  77. unsigned long new_addr, unsigned long old_addr, unsigned long len)
  78. {
  79. unsigned long offset = len;
  80. flush_cache_range(mm, old_addr, old_addr + len);
  81. /*
  82.  * This is not the clever way to do this, but we're taking the
  83.  * easy way out on the assumption that most remappings will be
  84.  * only a few pages.. This also makes error recovery easier.
  85.  */
  86. while (offset) {
  87. offset -= PAGE_SIZE;
  88. if (move_one_page(mm, old_addr + offset, new_addr + offset))
  89. goto oops_we_failed;
  90. }
  91. flush_tlb_range(mm, old_addr, old_addr + len);
  92. return 0;
  93. /*
  94.  * Ok, the move failed because we didn't have enough pages for
  95.  * the new page table tree. This is unlikely, but we have to
  96.  * take the possibility into account. In that case we just move
  97.  * all the pages back (this will work, because we still have
  98.  * the old page tables)
  99.  */
  100. oops_we_failed:
  101. flush_cache_range(mm, new_addr, new_addr + len);
  102. while ((offset += PAGE_SIZE) < len)
  103. move_one_page(mm, new_addr + offset, old_addr + offset);
  104. zap_page_range(mm, new_addr, len);
  105. return -1;
  106. }
  107. static inline unsigned long move_vma(struct vm_area_struct * vma,
  108. unsigned long addr, unsigned long old_len, unsigned long new_len,
  109. unsigned long new_addr)
  110. {
  111. struct mm_struct * mm = vma->vm_mm;
  112. struct vm_area_struct * new_vma, * next, * prev;
  113. int allocated_vma;
  114. new_vma = NULL;
  115. next = find_vma_prev(mm, new_addr, &prev);
  116. if (next) {
  117. if (prev && prev->vm_end == new_addr &&
  118.     can_vma_merge(prev, vma->vm_flags) && !vma->vm_file && !(vma->vm_flags & VM_SHARED)) {
  119. spin_lock(&mm->page_table_lock);
  120. prev->vm_end = new_addr + new_len;
  121. spin_unlock(&mm->page_table_lock);
  122. new_vma = prev;
  123. if (next != prev->vm_next)
  124. BUG();
  125. if (prev->vm_end == next->vm_start && can_vma_merge(next, prev->vm_flags)) {
  126. spin_lock(&mm->page_table_lock);
  127. prev->vm_end = next->vm_end;
  128. __vma_unlink(mm, next, prev);
  129. spin_unlock(&mm->page_table_lock);
  130. mm->map_count--;
  131. kmem_cache_free(vm_area_cachep, next);
  132. }
  133. } else if (next->vm_start == new_addr + new_len &&
  134.    can_vma_merge(next, vma->vm_flags) && !vma->vm_file && !(vma->vm_flags & VM_SHARED)) {
  135. spin_lock(&mm->page_table_lock);
  136. next->vm_start = new_addr;
  137. spin_unlock(&mm->page_table_lock);
  138. new_vma = next;
  139. }
  140. } else {
  141. prev = find_vma(mm, new_addr-1);
  142. if (prev && prev->vm_end == new_addr &&
  143.     can_vma_merge(prev, vma->vm_flags) && !vma->vm_file && !(vma->vm_flags & VM_SHARED)) {
  144. spin_lock(&mm->page_table_lock);
  145. prev->vm_end = new_addr + new_len;
  146. spin_unlock(&mm->page_table_lock);
  147. new_vma = prev;
  148. }
  149. }
  150. allocated_vma = 0;
  151. if (!new_vma) {
  152. new_vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL);
  153. if (!new_vma)
  154. goto out;
  155. allocated_vma = 1;
  156. }
  157. if (!move_page_tables(current->mm, new_addr, addr, old_len)) {
  158. if (allocated_vma) {
  159. *new_vma = *vma;
  160. new_vma->vm_start = new_addr;
  161. new_vma->vm_end = new_addr+new_len;
  162. new_vma->vm_pgoff += (addr - vma->vm_start) >> PAGE_SHIFT;
  163. new_vma->vm_raend = 0;
  164. if (new_vma->vm_file)
  165. get_file(new_vma->vm_file);
  166. if (new_vma->vm_ops && new_vma->vm_ops->open)
  167. new_vma->vm_ops->open(new_vma);
  168. insert_vm_struct(current->mm, new_vma);
  169. }
  170. do_munmap(current->mm, addr, old_len);
  171. current->mm->total_vm += new_len >> PAGE_SHIFT;
  172. if (new_vma->vm_flags & VM_LOCKED) {
  173. current->mm->locked_vm += new_len >> PAGE_SHIFT;
  174. make_pages_present(new_vma->vm_start,
  175.    new_vma->vm_end);
  176. }
  177. return new_addr;
  178. }
  179. if (allocated_vma)
  180. kmem_cache_free(vm_area_cachep, new_vma);
  181.  out:
  182. return -ENOMEM;
  183. }
  184. /*
  185.  * Expand (or shrink) an existing mapping, potentially moving it at the
  186.  * same time (controlled by the MREMAP_MAYMOVE flag and available VM space)
  187.  *
  188.  * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise
  189.  * This option implies MREMAP_MAYMOVE.
  190.  */
  191. unsigned long do_mremap(unsigned long addr,
  192. unsigned long old_len, unsigned long new_len,
  193. unsigned long flags, unsigned long new_addr)
  194. {
  195. struct vm_area_struct *vma;
  196. unsigned long ret = -EINVAL;
  197. if (flags & ~(MREMAP_FIXED | MREMAP_MAYMOVE))
  198. goto out;
  199. if (addr & ~PAGE_MASK)
  200. goto out;
  201. old_len = PAGE_ALIGN(old_len);
  202. new_len = PAGE_ALIGN(new_len);
  203. /* new_addr is only valid if MREMAP_FIXED is specified */
  204. if (flags & MREMAP_FIXED) {
  205. if (new_addr & ~PAGE_MASK)
  206. goto out;
  207. if (!(flags & MREMAP_MAYMOVE))
  208. goto out;
  209. if (new_len > TASK_SIZE || new_addr > TASK_SIZE - new_len)
  210. goto out;
  211. /* Check if the location we're moving into overlaps the
  212.  * old location at all, and fail if it does.
  213.  */
  214. if ((new_addr <= addr) && (new_addr+new_len) > addr)
  215. goto out;
  216. if ((addr <= new_addr) && (addr+old_len) > new_addr)
  217. goto out;
  218. do_munmap(current->mm, new_addr, new_len);
  219. }
  220. /*
  221.  * Always allow a shrinking remap: that just unmaps
  222.  * the unnecessary pages..
  223.  */
  224. ret = addr;
  225. if (old_len >= new_len) {
  226. do_munmap(current->mm, addr+new_len, old_len - new_len);
  227. if (!(flags & MREMAP_FIXED) || (new_addr == addr))
  228. goto out;
  229. }
  230. /*
  231.  * Ok, we need to grow..  or relocate.
  232.  */
  233. ret = -EFAULT;
  234. vma = find_vma(current->mm, addr);
  235. if (!vma || vma->vm_start > addr)
  236. goto out;
  237. /* We can't remap across vm area boundaries */
  238. if (old_len > vma->vm_end - addr)
  239. goto out;
  240. if (vma->vm_flags & VM_DONTEXPAND) {
  241. if (new_len > old_len)
  242. goto out;
  243. }
  244. if (vma->vm_flags & VM_LOCKED) {
  245. unsigned long locked = current->mm->locked_vm << PAGE_SHIFT;
  246. locked += new_len - old_len;
  247. ret = -EAGAIN;
  248. if (locked > current->rlim[RLIMIT_MEMLOCK].rlim_cur)
  249. goto out;
  250. }
  251. ret = -ENOMEM;
  252. if ((current->mm->total_vm << PAGE_SHIFT) + (new_len - old_len)
  253.     > current->rlim[RLIMIT_AS].rlim_cur)
  254. goto out;
  255. /* Private writable mapping? Check memory availability.. */
  256. if ((vma->vm_flags & (VM_SHARED | VM_WRITE)) == VM_WRITE &&
  257.     !(flags & MAP_NORESERVE)  &&
  258.     !vm_enough_memory((new_len - old_len) >> PAGE_SHIFT))
  259. goto out;
  260. /* old_len exactly to the end of the area..
  261.  * And we're not relocating the area.
  262.  */
  263. if (old_len == vma->vm_end - addr &&
  264.     !((flags & MREMAP_FIXED) && (addr != new_addr)) &&
  265.     (old_len != new_len || !(flags & MREMAP_MAYMOVE))) {
  266. unsigned long max_addr = TASK_SIZE;
  267. if (vma->vm_next)
  268. max_addr = vma->vm_next->vm_start;
  269. /* can we just expand the current mapping? */
  270. if (max_addr - addr >= new_len) {
  271. int pages = (new_len - old_len) >> PAGE_SHIFT;
  272. spin_lock(&vma->vm_mm->page_table_lock);
  273. vma->vm_end = addr + new_len;
  274. spin_unlock(&vma->vm_mm->page_table_lock);
  275. current->mm->total_vm += pages;
  276. if (vma->vm_flags & VM_LOCKED) {
  277. current->mm->locked_vm += pages;
  278. make_pages_present(addr + old_len,
  279.    addr + new_len);
  280. }
  281. ret = addr;
  282. goto out;
  283. }
  284. }
  285. /*
  286.  * We weren't able to just expand or shrink the area,
  287.  * we need to create a new one and move it..
  288.  */
  289. ret = -ENOMEM;
  290. if (flags & MREMAP_MAYMOVE) {
  291. if (!(flags & MREMAP_FIXED)) {
  292. unsigned long map_flags = 0;
  293. if (vma->vm_flags & VM_SHARED)
  294. map_flags |= MAP_SHARED;
  295. new_addr = get_unmapped_area(vma->vm_file, 0, new_len, vma->vm_pgoff, map_flags);
  296. ret = new_addr;
  297. if (new_addr & ~PAGE_MASK)
  298. goto out;
  299. }
  300. ret = move_vma(vma, addr, old_len, new_len, new_addr);
  301. }
  302. out:
  303. return ret;
  304. }
  305. asmlinkage unsigned long sys_mremap(unsigned long addr,
  306. unsigned long old_len, unsigned long new_len,
  307. unsigned long flags, unsigned long new_addr)
  308. {
  309. unsigned long ret;
  310. down_write(&current->mm->mmap_sem);
  311. ret = do_mremap(addr, old_len, new_len, flags, new_addr);
  312. up_write(&current->mm->mmap_sem);
  313. return ret;
  314. }