diff options
Diffstat (limited to 'mm/mmap.c')
| -rw-r--r-- | mm/mmap.c | 126 |
1 files changed, 66 insertions, 60 deletions
| @@ -181,26 +181,36 @@ static void __remove_shared_vm_struct(struct vm_area_struct *vma, | |||
| 181 | } | 181 | } |
| 182 | 182 | ||
| 183 | /* | 183 | /* |
| 184 | * Remove one vm structure and free it. | 184 | * Unlink a file-based vm structure from its prio_tree, to hide |
| 185 | * vma from rmap and vmtruncate before freeing its page tables. | ||
| 185 | */ | 186 | */ |
| 186 | static void remove_vm_struct(struct vm_area_struct *vma) | 187 | void unlink_file_vma(struct vm_area_struct *vma) |
| 187 | { | 188 | { |
| 188 | struct file *file = vma->vm_file; | 189 | struct file *file = vma->vm_file; |
| 189 | 190 | ||
| 190 | might_sleep(); | ||
| 191 | if (file) { | 191 | if (file) { |
| 192 | struct address_space *mapping = file->f_mapping; | 192 | struct address_space *mapping = file->f_mapping; |
| 193 | spin_lock(&mapping->i_mmap_lock); | 193 | spin_lock(&mapping->i_mmap_lock); |
| 194 | __remove_shared_vm_struct(vma, file, mapping); | 194 | __remove_shared_vm_struct(vma, file, mapping); |
| 195 | spin_unlock(&mapping->i_mmap_lock); | 195 | spin_unlock(&mapping->i_mmap_lock); |
| 196 | } | 196 | } |
| 197 | } | ||
| 198 | |||
| 199 | /* | ||
| 200 | * Close a vm structure and free it, returning the next. | ||
| 201 | */ | ||
| 202 | static struct vm_area_struct *remove_vma(struct vm_area_struct *vma) | ||
| 203 | { | ||
| 204 | struct vm_area_struct *next = vma->vm_next; | ||
| 205 | |||
| 206 | might_sleep(); | ||
| 197 | if (vma->vm_ops && vma->vm_ops->close) | 207 | if (vma->vm_ops && vma->vm_ops->close) |
| 198 | vma->vm_ops->close(vma); | 208 | vma->vm_ops->close(vma); |
| 199 | if (file) | 209 | if (vma->vm_file) |
| 200 | fput(file); | 210 | fput(vma->vm_file); |
| 201 | anon_vma_unlink(vma); | ||
| 202 | mpol_free(vma_policy(vma)); | 211 | mpol_free(vma_policy(vma)); |
| 203 | kmem_cache_free(vm_area_cachep, vma); | 212 | kmem_cache_free(vm_area_cachep, vma); |
| 213 | return next; | ||
| 204 | } | 214 | } |
| 205 | 215 | ||
| 206 | asmlinkage unsigned long sys_brk(unsigned long brk) | 216 | asmlinkage unsigned long sys_brk(unsigned long brk) |
| @@ -832,7 +842,7 @@ none: | |||
| 832 | } | 842 | } |
| 833 | 843 | ||
| 834 | #ifdef CONFIG_PROC_FS | 844 | #ifdef CONFIG_PROC_FS |
| 835 | void __vm_stat_account(struct mm_struct *mm, unsigned long flags, | 845 | void vm_stat_account(struct mm_struct *mm, unsigned long flags, |
| 836 | struct file *file, long pages) | 846 | struct file *file, long pages) |
| 837 | { | 847 | { |
| 838 | const unsigned long stack_flags | 848 | const unsigned long stack_flags |
| @@ -1070,6 +1080,17 @@ munmap_back: | |||
| 1070 | error = file->f_op->mmap(file, vma); | 1080 | error = file->f_op->mmap(file, vma); |
| 1071 | if (error) | 1081 | if (error) |
| 1072 | goto unmap_and_free_vma; | 1082 | goto unmap_and_free_vma; |
| 1083 | if ((vma->vm_flags & (VM_SHARED | VM_WRITE | VM_RESERVED)) | ||
| 1084 | == (VM_WRITE | VM_RESERVED)) { | ||
| 1085 | printk(KERN_WARNING "program %s is using MAP_PRIVATE, " | ||
| 1086 | "PROT_WRITE mmap of VM_RESERVED memory, which " | ||
| 1087 | "is deprecated. Please report this to " | ||
| 1088 | "linux-kernel@vger.kernel.org\n",current->comm); | ||
| 1089 | if (vma->vm_ops && vma->vm_ops->close) | ||
| 1090 | vma->vm_ops->close(vma); | ||
| 1091 | error = -EACCES; | ||
| 1092 | goto unmap_and_free_vma; | ||
| 1093 | } | ||
| 1073 | } else if (vm_flags & VM_SHARED) { | 1094 | } else if (vm_flags & VM_SHARED) { |
| 1074 | error = shmem_zero_setup(vma); | 1095 | error = shmem_zero_setup(vma); |
| 1075 | if (error) | 1096 | if (error) |
| @@ -1110,7 +1131,7 @@ munmap_back: | |||
| 1110 | } | 1131 | } |
| 1111 | out: | 1132 | out: |
| 1112 | mm->total_vm += len >> PAGE_SHIFT; | 1133 | mm->total_vm += len >> PAGE_SHIFT; |
| 1113 | __vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); | 1134 | vm_stat_account(mm, vm_flags, file, len >> PAGE_SHIFT); |
| 1114 | if (vm_flags & VM_LOCKED) { | 1135 | if (vm_flags & VM_LOCKED) { |
| 1115 | mm->locked_vm += len >> PAGE_SHIFT; | 1136 | mm->locked_vm += len >> PAGE_SHIFT; |
| 1116 | make_pages_present(addr, addr + len); | 1137 | make_pages_present(addr, addr + len); |
| @@ -1475,15 +1496,19 @@ static int acct_stack_growth(struct vm_area_struct * vma, unsigned long size, un | |||
| 1475 | mm->total_vm += grow; | 1496 | mm->total_vm += grow; |
| 1476 | if (vma->vm_flags & VM_LOCKED) | 1497 | if (vma->vm_flags & VM_LOCKED) |
| 1477 | mm->locked_vm += grow; | 1498 | mm->locked_vm += grow; |
| 1478 | __vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow); | 1499 | vm_stat_account(mm, vma->vm_flags, vma->vm_file, grow); |
| 1479 | return 0; | 1500 | return 0; |
| 1480 | } | 1501 | } |
| 1481 | 1502 | ||
| 1482 | #ifdef CONFIG_STACK_GROWSUP | 1503 | #if defined(CONFIG_STACK_GROWSUP) || defined(CONFIG_IA64) |
| 1483 | /* | 1504 | /* |
| 1484 | * vma is the first one with address > vma->vm_end. Have to extend vma. | 1505 | * PA-RISC uses this for its stack; IA64 for its Register Backing Store. |
| 1506 | * vma is the last one with address > vma->vm_end. Have to extend vma. | ||
| 1485 | */ | 1507 | */ |
| 1486 | int expand_stack(struct vm_area_struct * vma, unsigned long address) | 1508 | #ifdef CONFIG_STACK_GROWSUP |
| 1509 | static inline | ||
| 1510 | #endif | ||
| 1511 | int expand_upwards(struct vm_area_struct *vma, unsigned long address) | ||
| 1487 | { | 1512 | { |
| 1488 | int error; | 1513 | int error; |
| 1489 | 1514 | ||
| @@ -1521,6 +1546,13 @@ int expand_stack(struct vm_area_struct * vma, unsigned long address) | |||
| 1521 | anon_vma_unlock(vma); | 1546 | anon_vma_unlock(vma); |
| 1522 | return error; | 1547 | return error; |
| 1523 | } | 1548 | } |
| 1549 | #endif /* CONFIG_STACK_GROWSUP || CONFIG_IA64 */ | ||
| 1550 | |||
| 1551 | #ifdef CONFIG_STACK_GROWSUP | ||
| 1552 | int expand_stack(struct vm_area_struct *vma, unsigned long address) | ||
| 1553 | { | ||
| 1554 | return expand_upwards(vma, address); | ||
| 1555 | } | ||
| 1524 | 1556 | ||
| 1525 | struct vm_area_struct * | 1557 | struct vm_area_struct * |
| 1526 | find_extend_vma(struct mm_struct *mm, unsigned long addr) | 1558 | find_extend_vma(struct mm_struct *mm, unsigned long addr) |
| @@ -1603,36 +1635,24 @@ find_extend_vma(struct mm_struct * mm, unsigned long addr) | |||
| 1603 | } | 1635 | } |
| 1604 | #endif | 1636 | #endif |
| 1605 | 1637 | ||
| 1606 | /* Normal function to fix up a mapping | ||
| 1607 | * This function is the default for when an area has no specific | ||
| 1608 | * function. This may be used as part of a more specific routine. | ||
| 1609 | * | ||
| 1610 | * By the time this function is called, the area struct has been | ||
| 1611 | * removed from the process mapping list. | ||
| 1612 | */ | ||
| 1613 | static void unmap_vma(struct mm_struct *mm, struct vm_area_struct *area) | ||
| 1614 | { | ||
| 1615 | size_t len = area->vm_end - area->vm_start; | ||
| 1616 | |||
| 1617 | area->vm_mm->total_vm -= len >> PAGE_SHIFT; | ||
| 1618 | if (area->vm_flags & VM_LOCKED) | ||
| 1619 | area->vm_mm->locked_vm -= len >> PAGE_SHIFT; | ||
| 1620 | vm_stat_unaccount(area); | ||
| 1621 | remove_vm_struct(area); | ||
| 1622 | } | ||
| 1623 | |||
| 1624 | /* | 1638 | /* |
| 1625 | * Update the VMA and inode share lists. | 1639 | * Ok - we have the memory areas we should free on the vma list, |
| 1626 | * | ||
| 1627 | * Ok - we have the memory areas we should free on the 'free' list, | ||
| 1628 | * so release them, and do the vma updates. | 1640 | * so release them, and do the vma updates. |
| 1641 | * | ||
| 1642 | * Called with the mm semaphore held. | ||
| 1629 | */ | 1643 | */ |
| 1630 | static void unmap_vma_list(struct mm_struct *mm, struct vm_area_struct *vma) | 1644 | static void remove_vma_list(struct mm_struct *mm, struct vm_area_struct *vma) |
| 1631 | { | 1645 | { |
| 1646 | /* Update high watermark before we lower total_vm */ | ||
| 1647 | update_hiwater_vm(mm); | ||
| 1632 | do { | 1648 | do { |
| 1633 | struct vm_area_struct *next = vma->vm_next; | 1649 | long nrpages = vma_pages(vma); |
| 1634 | unmap_vma(mm, vma); | 1650 | |
| 1635 | vma = next; | 1651 | mm->total_vm -= nrpages; |
| 1652 | if (vma->vm_flags & VM_LOCKED) | ||
| 1653 | mm->locked_vm -= nrpages; | ||
| 1654 | vm_stat_account(mm, vma->vm_flags, vma->vm_file, -nrpages); | ||
| 1655 | vma = remove_vma(vma); | ||
| 1636 | } while (vma); | 1656 | } while (vma); |
| 1637 | validate_mm(mm); | 1657 | validate_mm(mm); |
| 1638 | } | 1658 | } |
| @@ -1651,14 +1671,13 @@ static void unmap_region(struct mm_struct *mm, | |||
| 1651 | unsigned long nr_accounted = 0; | 1671 | unsigned long nr_accounted = 0; |
| 1652 | 1672 | ||
| 1653 | lru_add_drain(); | 1673 | lru_add_drain(); |
| 1654 | spin_lock(&mm->page_table_lock); | ||
| 1655 | tlb = tlb_gather_mmu(mm, 0); | 1674 | tlb = tlb_gather_mmu(mm, 0); |
| 1656 | unmap_vmas(&tlb, mm, vma, start, end, &nr_accounted, NULL); | 1675 | update_hiwater_rss(mm); |
| 1676 | unmap_vmas(&tlb, vma, start, end, &nr_accounted, NULL); | ||
| 1657 | vm_unacct_memory(nr_accounted); | 1677 | vm_unacct_memory(nr_accounted); |
| 1658 | free_pgtables(&tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, | 1678 | free_pgtables(&tlb, vma, prev? prev->vm_end: FIRST_USER_ADDRESS, |
| 1659 | next? next->vm_start: 0); | 1679 | next? next->vm_start: 0); |
| 1660 | tlb_finish_mmu(tlb, start, end); | 1680 | tlb_finish_mmu(tlb, start, end); |
| 1661 | spin_unlock(&mm->page_table_lock); | ||
| 1662 | } | 1681 | } |
| 1663 | 1682 | ||
| 1664 | /* | 1683 | /* |
| @@ -1799,7 +1818,7 @@ int do_munmap(struct mm_struct *mm, unsigned long start, size_t len) | |||
| 1799 | unmap_region(mm, vma, prev, start, end); | 1818 | unmap_region(mm, vma, prev, start, end); |
| 1800 | 1819 | ||
| 1801 | /* Fix up all other VM information */ | 1820 | /* Fix up all other VM information */ |
| 1802 | unmap_vma_list(mm, vma); | 1821 | remove_vma_list(mm, vma); |
| 1803 | 1822 | ||
| 1804 | return 0; | 1823 | return 0; |
| 1805 | } | 1824 | } |
| @@ -1933,34 +1952,21 @@ void exit_mmap(struct mm_struct *mm) | |||
| 1933 | unsigned long end; | 1952 | unsigned long end; |
| 1934 | 1953 | ||
| 1935 | lru_add_drain(); | 1954 | lru_add_drain(); |
| 1936 | |||
| 1937 | spin_lock(&mm->page_table_lock); | ||
| 1938 | |||
| 1939 | flush_cache_mm(mm); | 1955 | flush_cache_mm(mm); |
| 1940 | tlb = tlb_gather_mmu(mm, 1); | 1956 | tlb = tlb_gather_mmu(mm, 1); |
| 1957 | /* Don't update_hiwater_rss(mm) here, do_exit already did */ | ||
| 1941 | /* Use -1 here to ensure all VMAs in the mm are unmapped */ | 1958 | /* Use -1 here to ensure all VMAs in the mm are unmapped */ |
| 1942 | end = unmap_vmas(&tlb, mm, vma, 0, -1, &nr_accounted, NULL); | 1959 | end = unmap_vmas(&tlb, vma, 0, -1, &nr_accounted, NULL); |
| 1943 | vm_unacct_memory(nr_accounted); | 1960 | vm_unacct_memory(nr_accounted); |
| 1944 | free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0); | 1961 | free_pgtables(&tlb, vma, FIRST_USER_ADDRESS, 0); |
| 1945 | tlb_finish_mmu(tlb, 0, end); | 1962 | tlb_finish_mmu(tlb, 0, end); |
| 1946 | 1963 | ||
| 1947 | mm->mmap = mm->mmap_cache = NULL; | ||
| 1948 | mm->mm_rb = RB_ROOT; | ||
| 1949 | set_mm_counter(mm, rss, 0); | ||
| 1950 | mm->total_vm = 0; | ||
| 1951 | mm->locked_vm = 0; | ||
| 1952 | |||
| 1953 | spin_unlock(&mm->page_table_lock); | ||
| 1954 | |||
| 1955 | /* | 1964 | /* |
| 1956 | * Walk the list again, actually closing and freeing it | 1965 | * Walk the list again, actually closing and freeing it, |
| 1957 | * without holding any MM locks. | 1966 | * with preemption enabled, without holding any MM locks. |
| 1958 | */ | 1967 | */ |
| 1959 | while (vma) { | 1968 | while (vma) |
| 1960 | struct vm_area_struct *next = vma->vm_next; | 1969 | vma = remove_vma(vma); |
| 1961 | remove_vm_struct(vma); | ||
| 1962 | vma = next; | ||
| 1963 | } | ||
| 1964 | 1970 | ||
| 1965 | BUG_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT); | 1971 | BUG_ON(mm->nr_ptes > (FIRST_USER_ADDRESS+PMD_SIZE-1)>>PMD_SHIFT); |
| 1966 | } | 1972 | } |
