diff options
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 193 |
1 files changed, 58 insertions, 135 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index 4ce02e0673db..bd106361be4b 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -264,46 +264,55 @@ static int shmem_radix_tree_replace(struct address_space *mapping, | |||
264 | } | 264 | } |
265 | 265 | ||
266 | /* | 266 | /* |
267 | * Sometimes, before we decide whether to proceed or to fail, we must check | ||
268 | * that an entry was not already brought back from swap by a racing thread. | ||
269 | * | ||
270 | * Checking page is not enough: by the time a SwapCache page is locked, it | ||
271 | * might be reused, and again be SwapCache, using the same swap as before. | ||
272 | */ | ||
273 | static bool shmem_confirm_swap(struct address_space *mapping, | ||
274 | pgoff_t index, swp_entry_t swap) | ||
275 | { | ||
276 | void *item; | ||
277 | |||
278 | rcu_read_lock(); | ||
279 | item = radix_tree_lookup(&mapping->page_tree, index); | ||
280 | rcu_read_unlock(); | ||
281 | return item == swp_to_radix_entry(swap); | ||
282 | } | ||
283 | |||
284 | /* | ||
267 | * Like add_to_page_cache_locked, but error if expected item has gone. | 285 | * Like add_to_page_cache_locked, but error if expected item has gone. |
268 | */ | 286 | */ |
269 | static int shmem_add_to_page_cache(struct page *page, | 287 | static int shmem_add_to_page_cache(struct page *page, |
270 | struct address_space *mapping, | 288 | struct address_space *mapping, |
271 | pgoff_t index, gfp_t gfp, void *expected) | 289 | pgoff_t index, gfp_t gfp, void *expected) |
272 | { | 290 | { |
273 | int error = 0; | 291 | int error; |
274 | 292 | ||
275 | VM_BUG_ON(!PageLocked(page)); | 293 | VM_BUG_ON(!PageLocked(page)); |
276 | VM_BUG_ON(!PageSwapBacked(page)); | 294 | VM_BUG_ON(!PageSwapBacked(page)); |
277 | 295 | ||
296 | page_cache_get(page); | ||
297 | page->mapping = mapping; | ||
298 | page->index = index; | ||
299 | |||
300 | spin_lock_irq(&mapping->tree_lock); | ||
278 | if (!expected) | 301 | if (!expected) |
279 | error = radix_tree_preload(gfp & GFP_RECLAIM_MASK); | 302 | error = radix_tree_insert(&mapping->page_tree, index, page); |
303 | else | ||
304 | error = shmem_radix_tree_replace(mapping, index, expected, | ||
305 | page); | ||
280 | if (!error) { | 306 | if (!error) { |
281 | page_cache_get(page); | 307 | mapping->nrpages++; |
282 | page->mapping = mapping; | 308 | __inc_zone_page_state(page, NR_FILE_PAGES); |
283 | page->index = index; | 309 | __inc_zone_page_state(page, NR_SHMEM); |
284 | 310 | spin_unlock_irq(&mapping->tree_lock); | |
285 | spin_lock_irq(&mapping->tree_lock); | 311 | } else { |
286 | if (!expected) | 312 | page->mapping = NULL; |
287 | error = radix_tree_insert(&mapping->page_tree, | 313 | spin_unlock_irq(&mapping->tree_lock); |
288 | index, page); | 314 | page_cache_release(page); |
289 | else | ||
290 | error = shmem_radix_tree_replace(mapping, index, | ||
291 | expected, page); | ||
292 | if (!error) { | ||
293 | mapping->nrpages++; | ||
294 | __inc_zone_page_state(page, NR_FILE_PAGES); | ||
295 | __inc_zone_page_state(page, NR_SHMEM); | ||
296 | spin_unlock_irq(&mapping->tree_lock); | ||
297 | } else { | ||
298 | page->mapping = NULL; | ||
299 | spin_unlock_irq(&mapping->tree_lock); | ||
300 | page_cache_release(page); | ||
301 | } | ||
302 | if (!expected) | ||
303 | radix_tree_preload_end(); | ||
304 | } | 315 | } |
305 | if (error) | ||
306 | mem_cgroup_uncharge_cache_page(page); | ||
307 | return error; | 316 | return error; |
308 | } | 317 | } |
309 | 318 | ||
@@ -1124,9 +1133,9 @@ repeat: | |||
1124 | /* We have to do this with page locked to prevent races */ | 1133 | /* We have to do this with page locked to prevent races */ |
1125 | lock_page(page); | 1134 | lock_page(page); |
1126 | if (!PageSwapCache(page) || page_private(page) != swap.val || | 1135 | if (!PageSwapCache(page) || page_private(page) != swap.val || |
1127 | page->mapping) { | 1136 | !shmem_confirm_swap(mapping, index, swap)) { |
1128 | error = -EEXIST; /* try again */ | 1137 | error = -EEXIST; /* try again */ |
1129 | goto failed; | 1138 | goto unlock; |
1130 | } | 1139 | } |
1131 | if (!PageUptodate(page)) { | 1140 | if (!PageUptodate(page)) { |
1132 | error = -EIO; | 1141 | error = -EIO; |
@@ -1142,9 +1151,12 @@ repeat: | |||
1142 | 1151 | ||
1143 | error = mem_cgroup_cache_charge(page, current->mm, | 1152 | error = mem_cgroup_cache_charge(page, current->mm, |
1144 | gfp & GFP_RECLAIM_MASK); | 1153 | gfp & GFP_RECLAIM_MASK); |
1145 | if (!error) | 1154 | if (!error) { |
1146 | error = shmem_add_to_page_cache(page, mapping, index, | 1155 | error = shmem_add_to_page_cache(page, mapping, index, |
1147 | gfp, swp_to_radix_entry(swap)); | 1156 | gfp, swp_to_radix_entry(swap)); |
1157 | /* We already confirmed swap, and make no allocation */ | ||
1158 | VM_BUG_ON(error); | ||
1159 | } | ||
1148 | if (error) | 1160 | if (error) |
1149 | goto failed; | 1161 | goto failed; |
1150 | 1162 | ||
@@ -1181,11 +1193,18 @@ repeat: | |||
1181 | __set_page_locked(page); | 1193 | __set_page_locked(page); |
1182 | error = mem_cgroup_cache_charge(page, current->mm, | 1194 | error = mem_cgroup_cache_charge(page, current->mm, |
1183 | gfp & GFP_RECLAIM_MASK); | 1195 | gfp & GFP_RECLAIM_MASK); |
1184 | if (!error) | ||
1185 | error = shmem_add_to_page_cache(page, mapping, index, | ||
1186 | gfp, NULL); | ||
1187 | if (error) | 1196 | if (error) |
1188 | goto decused; | 1197 | goto decused; |
1198 | error = radix_tree_preload(gfp & GFP_RECLAIM_MASK); | ||
1199 | if (!error) { | ||
1200 | error = shmem_add_to_page_cache(page, mapping, index, | ||
1201 | gfp, NULL); | ||
1202 | radix_tree_preload_end(); | ||
1203 | } | ||
1204 | if (error) { | ||
1205 | mem_cgroup_uncharge_cache_page(page); | ||
1206 | goto decused; | ||
1207 | } | ||
1189 | lru_cache_add_anon(page); | 1208 | lru_cache_add_anon(page); |
1190 | 1209 | ||
1191 | spin_lock(&info->lock); | 1210 | spin_lock(&info->lock); |
@@ -1245,14 +1264,10 @@ decused: | |||
1245 | unacct: | 1264 | unacct: |
1246 | shmem_unacct_blocks(info->flags, 1); | 1265 | shmem_unacct_blocks(info->flags, 1); |
1247 | failed: | 1266 | failed: |
1248 | if (swap.val && error != -EINVAL) { | 1267 | if (swap.val && error != -EINVAL && |
1249 | struct page *test = find_get_page(mapping, index); | 1268 | !shmem_confirm_swap(mapping, index, swap)) |
1250 | if (test && !radix_tree_exceptional_entry(test)) | 1269 | error = -EEXIST; |
1251 | page_cache_release(test); | 1270 | unlock: |
1252 | /* Have another try if the entry has changed */ | ||
1253 | if (test != swp_to_radix_entry(swap)) | ||
1254 | error = -EEXIST; | ||
1255 | } | ||
1256 | if (page) { | 1271 | if (page) { |
1257 | unlock_page(page); | 1272 | unlock_page(page); |
1258 | page_cache_release(page); | 1273 | page_cache_release(page); |
@@ -1264,7 +1279,7 @@ failed: | |||
1264 | spin_unlock(&info->lock); | 1279 | spin_unlock(&info->lock); |
1265 | goto repeat; | 1280 | goto repeat; |
1266 | } | 1281 | } |
1267 | if (error == -EEXIST) | 1282 | if (error == -EEXIST) /* from above or from radix_tree_insert */ |
1268 | goto repeat; | 1283 | goto repeat; |
1269 | return error; | 1284 | return error; |
1270 | } | 1285 | } |
@@ -1692,98 +1707,6 @@ static ssize_t shmem_file_splice_read(struct file *in, loff_t *ppos, | |||
1692 | return error; | 1707 | return error; |
1693 | } | 1708 | } |
1694 | 1709 | ||
1695 | /* | ||
1696 | * llseek SEEK_DATA or SEEK_HOLE through the radix_tree. | ||
1697 | */ | ||
1698 | static pgoff_t shmem_seek_hole_data(struct address_space *mapping, | ||
1699 | pgoff_t index, pgoff_t end, int origin) | ||
1700 | { | ||
1701 | struct page *page; | ||
1702 | struct pagevec pvec; | ||
1703 | pgoff_t indices[PAGEVEC_SIZE]; | ||
1704 | bool done = false; | ||
1705 | int i; | ||
1706 | |||
1707 | pagevec_init(&pvec, 0); | ||
1708 | pvec.nr = 1; /* start small: we may be there already */ | ||
1709 | while (!done) { | ||
1710 | pvec.nr = shmem_find_get_pages_and_swap(mapping, index, | ||
1711 | pvec.nr, pvec.pages, indices); | ||
1712 | if (!pvec.nr) { | ||
1713 | if (origin == SEEK_DATA) | ||
1714 | index = end; | ||
1715 | break; | ||
1716 | } | ||
1717 | for (i = 0; i < pvec.nr; i++, index++) { | ||
1718 | if (index < indices[i]) { | ||
1719 | if (origin == SEEK_HOLE) { | ||
1720 | done = true; | ||
1721 | break; | ||
1722 | } | ||
1723 | index = indices[i]; | ||
1724 | } | ||
1725 | page = pvec.pages[i]; | ||
1726 | if (page && !radix_tree_exceptional_entry(page)) { | ||
1727 | if (!PageUptodate(page)) | ||
1728 | page = NULL; | ||
1729 | } | ||
1730 | if (index >= end || | ||
1731 | (page && origin == SEEK_DATA) || | ||
1732 | (!page && origin == SEEK_HOLE)) { | ||
1733 | done = true; | ||
1734 | break; | ||
1735 | } | ||
1736 | } | ||
1737 | shmem_deswap_pagevec(&pvec); | ||
1738 | pagevec_release(&pvec); | ||
1739 | pvec.nr = PAGEVEC_SIZE; | ||
1740 | cond_resched(); | ||
1741 | } | ||
1742 | return index; | ||
1743 | } | ||
1744 | |||
1745 | static loff_t shmem_file_llseek(struct file *file, loff_t offset, int origin) | ||
1746 | { | ||
1747 | struct address_space *mapping; | ||
1748 | struct inode *inode; | ||
1749 | pgoff_t start, end; | ||
1750 | loff_t new_offset; | ||
1751 | |||
1752 | if (origin != SEEK_DATA && origin != SEEK_HOLE) | ||
1753 | return generic_file_llseek_size(file, offset, origin, | ||
1754 | MAX_LFS_FILESIZE); | ||
1755 | mapping = file->f_mapping; | ||
1756 | inode = mapping->host; | ||
1757 | mutex_lock(&inode->i_mutex); | ||
1758 | /* We're holding i_mutex so we can access i_size directly */ | ||
1759 | |||
1760 | if (offset < 0) | ||
1761 | offset = -EINVAL; | ||
1762 | else if (offset >= inode->i_size) | ||
1763 | offset = -ENXIO; | ||
1764 | else { | ||
1765 | start = offset >> PAGE_CACHE_SHIFT; | ||
1766 | end = (inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
1767 | new_offset = shmem_seek_hole_data(mapping, start, end, origin); | ||
1768 | new_offset <<= PAGE_CACHE_SHIFT; | ||
1769 | if (new_offset > offset) { | ||
1770 | if (new_offset < inode->i_size) | ||
1771 | offset = new_offset; | ||
1772 | else if (origin == SEEK_DATA) | ||
1773 | offset = -ENXIO; | ||
1774 | else | ||
1775 | offset = inode->i_size; | ||
1776 | } | ||
1777 | } | ||
1778 | |||
1779 | if (offset >= 0 && offset != file->f_pos) { | ||
1780 | file->f_pos = offset; | ||
1781 | file->f_version = 0; | ||
1782 | } | ||
1783 | mutex_unlock(&inode->i_mutex); | ||
1784 | return offset; | ||
1785 | } | ||
1786 | |||
1787 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, | 1710 | static long shmem_fallocate(struct file *file, int mode, loff_t offset, |
1788 | loff_t len) | 1711 | loff_t len) |
1789 | { | 1712 | { |
@@ -2787,7 +2710,7 @@ static const struct address_space_operations shmem_aops = { | |||
2787 | static const struct file_operations shmem_file_operations = { | 2710 | static const struct file_operations shmem_file_operations = { |
2788 | .mmap = shmem_mmap, | 2711 | .mmap = shmem_mmap, |
2789 | #ifdef CONFIG_TMPFS | 2712 | #ifdef CONFIG_TMPFS |
2790 | .llseek = shmem_file_llseek, | 2713 | .llseek = generic_file_llseek, |
2791 | .read = do_sync_read, | 2714 | .read = do_sync_read, |
2792 | .write = do_sync_write, | 2715 | .write = do_sync_write, |
2793 | .aio_read = shmem_file_aio_read, | 2716 | .aio_read = shmem_file_aio_read, |