diff options
-rw-r--r-- | mm/mmap.c | 50 |
1 files changed, 46 insertions, 4 deletions
@@ -6,6 +6,7 @@ | |||
6 | * Address space accounting code <alan@lxorguk.ukuu.org.uk> | 6 | * Address space accounting code <alan@lxorguk.ukuu.org.uk> |
7 | */ | 7 | */ |
8 | 8 | ||
9 | #include <linux/kernel.h> | ||
9 | #include <linux/slab.h> | 10 | #include <linux/slab.h> |
10 | #include <linux/backing-dev.h> | 11 | #include <linux/backing-dev.h> |
11 | #include <linux/mm.h> | 12 | #include <linux/mm.h> |
@@ -550,6 +551,34 @@ static int find_vma_links(struct mm_struct *mm, unsigned long addr, | |||
550 | return 0; | 551 | return 0; |
551 | } | 552 | } |
552 | 553 | ||
554 | static unsigned long count_vma_pages_range(struct mm_struct *mm, | ||
555 | unsigned long addr, unsigned long end) | ||
556 | { | ||
557 | unsigned long nr_pages = 0; | ||
558 | struct vm_area_struct *vma; | ||
559 | |||
560 | /* Find first overlaping mapping */ | ||
561 | vma = find_vma_intersection(mm, addr, end); | ||
562 | if (!vma) | ||
563 | return 0; | ||
564 | |||
565 | nr_pages = (min(end, vma->vm_end) - | ||
566 | max(addr, vma->vm_start)) >> PAGE_SHIFT; | ||
567 | |||
568 | /* Iterate over the rest of the overlaps */ | ||
569 | for (vma = vma->vm_next; vma; vma = vma->vm_next) { | ||
570 | unsigned long overlap_len; | ||
571 | |||
572 | if (vma->vm_start > end) | ||
573 | break; | ||
574 | |||
575 | overlap_len = min(end, vma->vm_end) - vma->vm_start; | ||
576 | nr_pages += overlap_len >> PAGE_SHIFT; | ||
577 | } | ||
578 | |||
579 | return nr_pages; | ||
580 | } | ||
581 | |||
553 | void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, | 582 | void __vma_link_rb(struct mm_struct *mm, struct vm_area_struct *vma, |
554 | struct rb_node **rb_link, struct rb_node *rb_parent) | 583 | struct rb_node **rb_link, struct rb_node *rb_parent) |
555 | { | 584 | { |
@@ -1442,6 +1471,23 @@ unsigned long mmap_region(struct file *file, unsigned long addr, | |||
1442 | unsigned long charged = 0; | 1471 | unsigned long charged = 0; |
1443 | struct inode *inode = file ? file_inode(file) : NULL; | 1472 | struct inode *inode = file ? file_inode(file) : NULL; |
1444 | 1473 | ||
1474 | /* Check against address space limit. */ | ||
1475 | if (!may_expand_vm(mm, len >> PAGE_SHIFT)) { | ||
1476 | unsigned long nr_pages; | ||
1477 | |||
1478 | /* | ||
1479 | * MAP_FIXED may remove pages of mappings that intersects with | ||
1480 | * requested mapping. Account for the pages it would unmap. | ||
1481 | */ | ||
1482 | if (!(vm_flags & MAP_FIXED)) | ||
1483 | return -ENOMEM; | ||
1484 | |||
1485 | nr_pages = count_vma_pages_range(mm, addr, addr + len); | ||
1486 | |||
1487 | if (!may_expand_vm(mm, (len >> PAGE_SHIFT) - nr_pages)) | ||
1488 | return -ENOMEM; | ||
1489 | } | ||
1490 | |||
1445 | /* Clear old maps */ | 1491 | /* Clear old maps */ |
1446 | error = -ENOMEM; | 1492 | error = -ENOMEM; |
1447 | munmap_back: | 1493 | munmap_back: |
@@ -1451,10 +1497,6 @@ munmap_back: | |||
1451 | goto munmap_back; | 1497 | goto munmap_back; |
1452 | } | 1498 | } |
1453 | 1499 | ||
1454 | /* Check against address space limit. */ | ||
1455 | if (!may_expand_vm(mm, len >> PAGE_SHIFT)) | ||
1456 | return -ENOMEM; | ||
1457 | |||
1458 | /* | 1500 | /* |
1459 | * Private writable mapping: check memory availability | 1501 | * Private writable mapping: check memory availability |
1460 | */ | 1502 | */ |