diff options
| -rw-r--r-- | fs/ramfs/file-nommu.c | 31 | ||||
| -rw-r--r-- | include/linux/mm.h | 1 | ||||
| -rw-r--r-- | mm/nommu.c | 62 |
3 files changed, 64 insertions, 30 deletions
diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 266531343aae..1739a4aba25f 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c | |||
| @@ -123,35 +123,6 @@ add_error: | |||
| 123 | 123 | ||
| 124 | /*****************************************************************************/ | 124 | /*****************************************************************************/ |
| 125 | /* | 125 | /* |
| 126 | * check that file shrinkage doesn't leave any VMAs dangling in midair | ||
| 127 | */ | ||
| 128 | static int ramfs_nommu_check_mappings(struct inode *inode, | ||
| 129 | size_t newsize, size_t size) | ||
| 130 | { | ||
| 131 | struct vm_area_struct *vma; | ||
| 132 | struct prio_tree_iter iter; | ||
| 133 | |||
| 134 | down_write(&nommu_region_sem); | ||
| 135 | |||
| 136 | /* search for VMAs that fall within the dead zone */ | ||
| 137 | vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap, | ||
| 138 | newsize >> PAGE_SHIFT, | ||
| 139 | (size + PAGE_SIZE - 1) >> PAGE_SHIFT | ||
| 140 | ) { | ||
| 141 | /* found one - only interested if it's shared out of the page | ||
| 142 | * cache */ | ||
| 143 | if (vma->vm_flags & VM_SHARED) { | ||
| 144 | up_write(&nommu_region_sem); | ||
| 145 | return -ETXTBSY; /* not quite true, but near enough */ | ||
| 146 | } | ||
| 147 | } | ||
| 148 | |||
| 149 | up_write(&nommu_region_sem); | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | /*****************************************************************************/ | ||
| 154 | /* | ||
| 155 | * | 126 | * |
| 156 | */ | 127 | */ |
| 157 | static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) | 128 | static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) |
| @@ -169,7 +140,7 @@ static int ramfs_nommu_resize(struct inode *inode, loff_t newsize, loff_t size) | |||
| 169 | 140 | ||
| 170 | /* check that a decrease in size doesn't cut off any shared mappings */ | 141 | /* check that a decrease in size doesn't cut off any shared mappings */ |
| 171 | if (newsize < size) { | 142 | if (newsize < size) { |
| 172 | ret = ramfs_nommu_check_mappings(inode, newsize, size); | 143 | ret = nommu_shrink_inode_mappings(inode, size, newsize); |
| 173 | if (ret < 0) | 144 | if (ret < 0) |
| 174 | return ret; | 145 | return ret; |
| 175 | } | 146 | } |
diff --git a/include/linux/mm.h b/include/linux/mm.h index 2265f28eb47a..60c467bfbabd 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h | |||
| @@ -1089,6 +1089,7 @@ extern void zone_pcp_update(struct zone *zone); | |||
| 1089 | 1089 | ||
| 1090 | /* nommu.c */ | 1090 | /* nommu.c */ |
| 1091 | extern atomic_long_t mmap_pages_allocated; | 1091 | extern atomic_long_t mmap_pages_allocated; |
| 1092 | extern int nommu_shrink_inode_mappings(struct inode *, size_t, size_t); | ||
| 1092 | 1093 | ||
| 1093 | /* prio_tree.c */ | 1094 | /* prio_tree.c */ |
| 1094 | void vma_prio_tree_add(struct vm_area_struct *, struct vm_area_struct *old); | 1095 | void vma_prio_tree_add(struct vm_area_struct *, struct vm_area_struct *old); |
diff --git a/mm/nommu.c b/mm/nommu.c index 32be0cf51ba6..48a2ecfaf059 100644 --- a/mm/nommu.c +++ b/mm/nommu.c | |||
| @@ -1914,3 +1914,65 @@ int access_process_vm(struct task_struct *tsk, unsigned long addr, void *buf, in | |||
| 1914 | mmput(mm); | 1914 | mmput(mm); |
| 1915 | return len; | 1915 | return len; |
| 1916 | } | 1916 | } |
| 1917 | |||
| 1918 | /** | ||
| 1919 | * nommu_shrink_inode_mappings - Shrink the shared mappings on an inode | ||
| 1920 | * @inode: The inode to check | ||
| 1921 | * @size: The current filesize of the inode | ||
| 1922 | * @newsize: The proposed filesize of the inode | ||
| 1923 | * | ||
| 1924 | * Check the shared mappings on an inode on behalf of a shrinking truncate to | ||
| 1925 | * make sure that that any outstanding VMAs aren't broken and then shrink the | ||
| 1926 | * vm_regions that extend that beyond so that do_mmap_pgoff() doesn't | ||
| 1927 | * automatically grant mappings that are too large. | ||
| 1928 | */ | ||
| 1929 | int nommu_shrink_inode_mappings(struct inode *inode, size_t size, | ||
| 1930 | size_t newsize) | ||
| 1931 | { | ||
| 1932 | struct vm_area_struct *vma; | ||
| 1933 | struct prio_tree_iter iter; | ||
| 1934 | struct vm_region *region; | ||
| 1935 | pgoff_t low, high; | ||
| 1936 | size_t r_size, r_top; | ||
| 1937 | |||
| 1938 | low = newsize >> PAGE_SHIFT; | ||
| 1939 | high = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; | ||
| 1940 | |||
| 1941 | down_write(&nommu_region_sem); | ||
| 1942 | |||
| 1943 | /* search for VMAs that fall within the dead zone */ | ||
| 1944 | vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap, | ||
| 1945 | low, high) { | ||
| 1946 | /* found one - only interested if it's shared out of the page | ||
| 1947 | * cache */ | ||
| 1948 | if (vma->vm_flags & VM_SHARED) { | ||
| 1949 | up_write(&nommu_region_sem); | ||
| 1950 | return -ETXTBSY; /* not quite true, but near enough */ | ||
| 1951 | } | ||
| 1952 | } | ||
| 1953 | |||
| 1954 | /* reduce any regions that overlap the dead zone - if in existence, | ||
| 1955 | * these will be pointed to by VMAs that don't overlap the dead zone | ||
| 1956 | * | ||
| 1957 | * we don't check for any regions that start beyond the EOF as there | ||
| 1958 | * shouldn't be any | ||
| 1959 | */ | ||
| 1960 | vma_prio_tree_foreach(vma, &iter, &inode->i_mapping->i_mmap, | ||
| 1961 | 0, ULONG_MAX) { | ||
| 1962 | if (!(vma->vm_flags & VM_SHARED)) | ||
| 1963 | continue; | ||
| 1964 | |||
| 1965 | region = vma->vm_region; | ||
| 1966 | r_size = region->vm_top - region->vm_start; | ||
| 1967 | r_top = (region->vm_pgoff << PAGE_SHIFT) + r_size; | ||
| 1968 | |||
| 1969 | if (r_top > newsize) { | ||
| 1970 | region->vm_top -= r_top - newsize; | ||
| 1971 | if (region->vm_end > region->vm_top) | ||
| 1972 | region->vm_end = region->vm_top; | ||
| 1973 | } | ||
| 1974 | } | ||
| 1975 | |||
| 1976 | up_write(&nommu_region_sem); | ||
| 1977 | return 0; | ||
| 1978 | } | ||
