Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[PATCH] NOMMU: Make mremap() partially work for NOMMU kernels

Make mremap() partially work for NOMMU kernels. It may resize a VMA provided
that it doesn't exceed the size of the slab object in which the storage is
allocated that the VMA refers to. Shareable VMAs may not be resized.

Moving VMAs (as permitted by MREMAP_MAYMOVE) is not currently supported.

This patch also makes use of the fact that the VMA list is now ordered to cut
it short when possible.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>

authored by

David Howells and committed by
Linus Torvalds
6fa5f80b 3034097a

+70 -17
+24
Documentation/nommu-mmap.txt
··· 128 128 error will result if they don't. This is most likely to be encountered 129 129 with character device files, pipes, fifos and sockets. 130 130 131 + 132 + ============= 133 + NO-MMU MREMAP 134 + ============= 135 + 136 + The mremap() function is partially supported. It may change the size of a 137 + mapping, and may move it[*] if MREMAP_MAYMOVE is specified and if the new size 138 + of the mapping exceeds the size of the slab object currently occupied by the 139 + memory to which the mapping refers, or if a smaller slab object could be used. 140 + 141 + MREMAP_FIXED is not supported, though it is ignored if there's no change of 142 + address and the object does not need to be moved. 143 + 144 + Shared mappings may not be moved. Shareable mappings may not be moved either, 145 + even if they are not currently shared. 146 + 147 + The mremap() function must be given an exact match for base address and size of 148 + a previously mapped object. It may not be used to create holes in existing 149 + mappings, move parts of existing mappings or resize parts of mappings. It must 150 + act on a complete mapping. 151 + 152 + [*] Not currently supported. 153 + 154 + 131 155 ============================================ 132 156 PROVIDING SHAREABLE CHARACTER DEVICE SUPPORT 133 157 ============================================
+46 -17
mm/nommu.c
··· 350 350 EXPORT_SYMBOL(find_vma); 351 351 352 352 /* 353 + * look up the first VMA exactly that exactly matches addr 354 + * - should be called with mm->mmap_sem at least held readlocked 355 + */ 356 + static inline struct vm_area_struct *find_vma_exact(struct mm_struct *mm, 357 + unsigned long addr) 358 + { 359 + struct vm_list_struct *vml; 360 + 361 + /* search the vm_start ordered list */ 362 + for (vml = mm->context.vmlist; vml; vml = vml->next) { 363 + if (vml->vma->vm_start == addr) 364 + return vml->vma; 365 + if (vml->vma->vm_start > addr) 366 + break; 367 + } 368 + 369 + return NULL; 370 + } 371 + 372 + /* 353 373 * find a VMA in the global tree 354 374 */ 355 375 static inline struct vm_area_struct *find_nommu_vma(unsigned long start) ··· 1091 1071 } 1092 1072 1093 1073 /* 1094 - * Expand (or shrink) an existing mapping, potentially moving it at the 1095 - * same time (controlled by the MREMAP_MAYMOVE flag and available VM space) 1074 + * expand (or shrink) an existing mapping, potentially moving it at the same 1075 + * time (controlled by the MREMAP_MAYMOVE flag and available VM space) 1096 1076 * 1097 - * MREMAP_FIXED option added 5-Dec-1999 by Benjamin LaHaise 1098 - * This option implies MREMAP_MAYMOVE. 1077 + * under NOMMU conditions, we only permit changing a mapping's size, and only 1078 + * as long as it stays within the hole allocated by the kmalloc() call in 1079 + * do_mmap_pgoff() and the block is not shareable 1099 1080 * 1100 - * on uClinux, we only permit changing a mapping's size, and only as long as it stays within the 1101 - * hole allocated by the kmalloc() call in do_mmap_pgoff() and the block is not shareable 1081 + * MREMAP_FIXED is not supported under NOMMU conditions 1102 1082 */ 1103 1083 unsigned long do_mremap(unsigned long addr, 1104 1084 unsigned long old_len, unsigned long new_len, 1105 1085 unsigned long flags, unsigned long new_addr) 1106 1086 { 1107 - struct vm_list_struct *vml = NULL; 1087 + struct vm_area_struct *vma; 1108 1088 1109 1089 /* insanity checks first */ 1110 1090 if (new_len == 0) ··· 1113 1093 if (flags & MREMAP_FIXED && new_addr != addr) 1114 1094 return (unsigned long) -EINVAL; 1115 1095 1116 - for (vml = current->mm->context.vmlist; vml; vml = vml->next) 1117 - if (vml->vma->vm_start == addr) 1118 - goto found; 1096 + vma = find_vma_exact(current->mm, addr); 1097 + if (!vma) 1098 + return (unsigned long) -EINVAL; 1119 1099 1120 - return (unsigned long) -EINVAL; 1121 - 1122 - found: 1123 - if (vml->vma->vm_end != vml->vma->vm_start + old_len) 1100 + if (vma->vm_end != vma->vm_start + old_len) 1124 1101 return (unsigned long) -EFAULT; 1125 1102 1126 - if (vml->vma->vm_flags & VM_MAYSHARE) 1103 + if (vma->vm_flags & VM_MAYSHARE) 1127 1104 return (unsigned long) -EPERM; 1128 1105 1129 1106 if (new_len > kobjsize((void *) addr)) 1130 1107 return (unsigned long) -ENOMEM; 1131 1108 1132 1109 /* all checks complete - do it */ 1133 - vml->vma->vm_end = vml->vma->vm_start + new_len; 1110 + vma->vm_end = vma->vm_start + new_len; 1134 1111 1135 1112 askedalloc -= old_len; 1136 1113 askedalloc += new_len; 1137 1114 1138 - return vml->vma->vm_start; 1115 + return vma->vm_start; 1116 + } 1117 + 1118 + asmlinkage unsigned long sys_mremap(unsigned long addr, 1119 + unsigned long old_len, unsigned long new_len, 1120 + unsigned long flags, unsigned long new_addr) 1121 + { 1122 + unsigned long ret; 1123 + 1124 + down_write(&current->mm->mmap_sem); 1125 + ret = do_mremap(addr, old_len, new_len, flags, new_addr); 1126 + up_write(&current->mm->mmap_sem); 1127 + return ret; 1139 1128 } 1140 1129 1141 1130 struct page *follow_page(struct vm_area_struct *vma, unsigned long address,