diff options
Diffstat (limited to 'mm/memory.c')
-rw-r--r-- | mm/memory.c | 69 |
1 files changed, 19 insertions, 50 deletions
diff --git a/mm/memory.c b/mm/memory.c index aa8af0e20269..d22f78c8a381 100644 --- a/mm/memory.c +++ b/mm/memory.c | |||
@@ -349,6 +349,11 @@ void print_bad_pte(struct vm_area_struct *vma, pte_t pte, unsigned long vaddr) | |||
349 | dump_stack(); | 349 | dump_stack(); |
350 | } | 350 | } |
351 | 351 | ||
352 | static inline int is_cow_mapping(unsigned int flags) | ||
353 | { | ||
354 | return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; | ||
355 | } | ||
356 | |||
352 | /* | 357 | /* |
353 | * This function gets the "struct page" associated with a pte. | 358 | * This function gets the "struct page" associated with a pte. |
354 | * | 359 | * |
@@ -377,6 +382,8 @@ struct page *vm_normal_page(struct vm_area_struct *vma, unsigned long addr, pte_ | |||
377 | unsigned long off = (addr - vma->vm_start) >> PAGE_SHIFT; | 382 | unsigned long off = (addr - vma->vm_start) >> PAGE_SHIFT; |
378 | if (pfn == vma->vm_pgoff + off) | 383 | if (pfn == vma->vm_pgoff + off) |
379 | return NULL; | 384 | return NULL; |
385 | if (!is_cow_mapping(vma->vm_flags)) | ||
386 | return NULL; | ||
380 | } | 387 | } |
381 | 388 | ||
382 | /* | 389 | /* |
@@ -437,7 +444,7 @@ copy_one_pte(struct mm_struct *dst_mm, struct mm_struct *src_mm, | |||
437 | * If it's a COW mapping, write protect it both | 444 | * If it's a COW mapping, write protect it both |
438 | * in the parent and the child | 445 | * in the parent and the child |
439 | */ | 446 | */ |
440 | if ((vm_flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE) { | 447 | if (is_cow_mapping(vm_flags)) { |
441 | ptep_set_wrprotect(src_mm, addr, src_pte); | 448 | ptep_set_wrprotect(src_mm, addr, src_pte); |
442 | pte = *src_pte; | 449 | pte = *src_pte; |
443 | } | 450 | } |
@@ -1002,7 +1009,7 @@ int get_user_pages(struct task_struct *tsk, struct mm_struct *mm, | |||
1002 | continue; | 1009 | continue; |
1003 | } | 1010 | } |
1004 | 1011 | ||
1005 | if (!vma || (vma->vm_flags & VM_IO) | 1012 | if (!vma || (vma->vm_flags & (VM_IO | VM_PFNMAP)) |
1006 | || !(vm_flags & vma->vm_flags)) | 1013 | || !(vm_flags & vma->vm_flags)) |
1007 | return i ? : -EFAULT; | 1014 | return i ? : -EFAULT; |
1008 | 1015 | ||
@@ -1226,50 +1233,6 @@ int vm_insert_page(struct vm_area_struct *vma, unsigned long addr, struct page * | |||
1226 | EXPORT_SYMBOL(vm_insert_page); | 1233 | EXPORT_SYMBOL(vm_insert_page); |
1227 | 1234 | ||
1228 | /* | 1235 | /* |
1229 | * Somebody does a pfn remapping that doesn't actually work as a vma. | ||
1230 | * | ||
1231 | * Do it as individual pages instead, and warn about it. It's bad form, | ||
1232 | * and very inefficient. | ||
1233 | */ | ||
1234 | static int incomplete_pfn_remap(struct vm_area_struct *vma, | ||
1235 | unsigned long start, unsigned long end, | ||
1236 | unsigned long pfn, pgprot_t prot) | ||
1237 | { | ||
1238 | static int warn = 10; | ||
1239 | struct page *page; | ||
1240 | int retval; | ||
1241 | |||
1242 | if (!(vma->vm_flags & VM_INCOMPLETE)) { | ||
1243 | if (warn) { | ||
1244 | warn--; | ||
1245 | printk("%s does an incomplete pfn remapping", current->comm); | ||
1246 | dump_stack(); | ||
1247 | } | ||
1248 | } | ||
1249 | vma->vm_flags |= VM_INCOMPLETE | VM_IO | VM_RESERVED; | ||
1250 | |||
1251 | if (start < vma->vm_start || end > vma->vm_end) | ||
1252 | return -EINVAL; | ||
1253 | |||
1254 | if (!pfn_valid(pfn)) | ||
1255 | return -EINVAL; | ||
1256 | |||
1257 | page = pfn_to_page(pfn); | ||
1258 | if (!PageReserved(page)) | ||
1259 | return -EINVAL; | ||
1260 | |||
1261 | retval = 0; | ||
1262 | while (start < end) { | ||
1263 | retval = insert_page(vma->vm_mm, start, page, prot); | ||
1264 | if (retval < 0) | ||
1265 | break; | ||
1266 | start += PAGE_SIZE; | ||
1267 | page++; | ||
1268 | } | ||
1269 | return retval; | ||
1270 | } | ||
1271 | |||
1272 | /* | ||
1273 | * maps a range of physical memory into the requested pages. the old | 1236 | * maps a range of physical memory into the requested pages. the old |
1274 | * mappings are removed. any references to nonexistent pages results | 1237 | * mappings are removed. any references to nonexistent pages results |
1275 | * in null mappings (currently treated as "copy-on-access") | 1238 | * in null mappings (currently treated as "copy-on-access") |
@@ -1343,9 +1306,6 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
1343 | struct mm_struct *mm = vma->vm_mm; | 1306 | struct mm_struct *mm = vma->vm_mm; |
1344 | int err; | 1307 | int err; |
1345 | 1308 | ||
1346 | if (addr != vma->vm_start || end != vma->vm_end) | ||
1347 | return incomplete_pfn_remap(vma, addr, end, pfn, prot); | ||
1348 | |||
1349 | /* | 1309 | /* |
1350 | * Physically remapped pages are special. Tell the | 1310 | * Physically remapped pages are special. Tell the |
1351 | * rest of the world about it: | 1311 | * rest of the world about it: |
@@ -1359,9 +1319,18 @@ int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr, | |||
1359 | * VM_PFNMAP tells the core MM that the base pages are just | 1319 | * VM_PFNMAP tells the core MM that the base pages are just |
1360 | * raw PFN mappings, and do not have a "struct page" associated | 1320 | * raw PFN mappings, and do not have a "struct page" associated |
1361 | * with them. | 1321 | * with them. |
1322 | * | ||
1323 | * There's a horrible special case to handle copy-on-write | ||
1324 | * behaviour that some programs depend on. We mark the "original" | ||
1325 | * un-COW'ed pages by matching them up with "vma->vm_pgoff". | ||
1362 | */ | 1326 | */ |
1327 | if (is_cow_mapping(vma->vm_flags)) { | ||
1328 | if (addr != vma->vm_start || end != vma->vm_end) | ||
1329 | return -EINVAL; | ||
1330 | vma->vm_pgoff = pfn; | ||
1331 | } | ||
1332 | |||
1363 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; | 1333 | vma->vm_flags |= VM_IO | VM_RESERVED | VM_PFNMAP; |
1364 | vma->vm_pgoff = pfn; | ||
1365 | 1334 | ||
1366 | BUG_ON(addr >= end); | 1335 | BUG_ON(addr >= end); |
1367 | pfn -= addr >> PAGE_SHIFT; | 1336 | pfn -= addr >> PAGE_SHIFT; |