diff options
Diffstat (limited to 'mm/shmem.c')
-rw-r--r-- | mm/shmem.c | 139 |
1 files changed, 83 insertions, 56 deletions
diff --git a/mm/shmem.c b/mm/shmem.c index f65f84062db5..080b09a57a8f 100644 --- a/mm/shmem.c +++ b/mm/shmem.c | |||
@@ -28,6 +28,7 @@ | |||
28 | #include <linux/file.h> | 28 | #include <linux/file.h> |
29 | #include <linux/mm.h> | 29 | #include <linux/mm.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/percpu_counter.h> | ||
31 | #include <linux/swap.h> | 32 | #include <linux/swap.h> |
32 | 33 | ||
33 | static struct vfsmount *shm_mnt; | 34 | static struct vfsmount *shm_mnt; |
@@ -233,10 +234,10 @@ static void shmem_free_blocks(struct inode *inode, long pages) | |||
233 | { | 234 | { |
234 | struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); | 235 | struct shmem_sb_info *sbinfo = SHMEM_SB(inode->i_sb); |
235 | if (sbinfo->max_blocks) { | 236 | if (sbinfo->max_blocks) { |
236 | spin_lock(&sbinfo->stat_lock); | 237 | percpu_counter_add(&sbinfo->used_blocks, -pages); |
237 | sbinfo->free_blocks += pages; | 238 | spin_lock(&inode->i_lock); |
238 | inode->i_blocks -= pages*BLOCKS_PER_PAGE; | 239 | inode->i_blocks -= pages*BLOCKS_PER_PAGE; |
239 | spin_unlock(&sbinfo->stat_lock); | 240 | spin_unlock(&inode->i_lock); |
240 | } | 241 | } |
241 | } | 242 | } |
242 | 243 | ||
@@ -416,19 +417,17 @@ static swp_entry_t *shmem_swp_alloc(struct shmem_inode_info *info, unsigned long | |||
416 | if (sgp == SGP_READ) | 417 | if (sgp == SGP_READ) |
417 | return shmem_swp_map(ZERO_PAGE(0)); | 418 | return shmem_swp_map(ZERO_PAGE(0)); |
418 | /* | 419 | /* |
419 | * Test free_blocks against 1 not 0, since we have 1 data | 420 | * Test used_blocks against 1 less max_blocks, since we have 1 data |
420 | * page (and perhaps indirect index pages) yet to allocate: | 421 | * page (and perhaps indirect index pages) yet to allocate: |
421 | * a waste to allocate index if we cannot allocate data. | 422 | * a waste to allocate index if we cannot allocate data. |
422 | */ | 423 | */ |
423 | if (sbinfo->max_blocks) { | 424 | if (sbinfo->max_blocks) { |
424 | spin_lock(&sbinfo->stat_lock); | 425 | if (percpu_counter_compare(&sbinfo->used_blocks, (sbinfo->max_blocks - 1)) > 0) |
425 | if (sbinfo->free_blocks <= 1) { | ||
426 | spin_unlock(&sbinfo->stat_lock); | ||
427 | return ERR_PTR(-ENOSPC); | 426 | return ERR_PTR(-ENOSPC); |
428 | } | 427 | percpu_counter_inc(&sbinfo->used_blocks); |
429 | sbinfo->free_blocks--; | 428 | spin_lock(&inode->i_lock); |
430 | inode->i_blocks += BLOCKS_PER_PAGE; | 429 | inode->i_blocks += BLOCKS_PER_PAGE; |
431 | spin_unlock(&sbinfo->stat_lock); | 430 | spin_unlock(&inode->i_lock); |
432 | } | 431 | } |
433 | 432 | ||
434 | spin_unlock(&info->lock); | 433 | spin_unlock(&info->lock); |
@@ -767,6 +766,10 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) | |||
767 | loff_t newsize = attr->ia_size; | 766 | loff_t newsize = attr->ia_size; |
768 | int error; | 767 | int error; |
769 | 768 | ||
769 | error = inode_change_ok(inode, attr); | ||
770 | if (error) | ||
771 | return error; | ||
772 | |||
770 | if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) | 773 | if (S_ISREG(inode->i_mode) && (attr->ia_valid & ATTR_SIZE) |
771 | && newsize != inode->i_size) { | 774 | && newsize != inode->i_size) { |
772 | struct page *page = NULL; | 775 | struct page *page = NULL; |
@@ -801,25 +804,22 @@ static int shmem_notify_change(struct dentry *dentry, struct iattr *attr) | |||
801 | } | 804 | } |
802 | } | 805 | } |
803 | 806 | ||
804 | error = simple_setsize(inode, newsize); | 807 | /* XXX(truncate): truncate_setsize should be called last */ |
808 | truncate_setsize(inode, newsize); | ||
805 | if (page) | 809 | if (page) |
806 | page_cache_release(page); | 810 | page_cache_release(page); |
807 | if (error) | ||
808 | return error; | ||
809 | shmem_truncate_range(inode, newsize, (loff_t)-1); | 811 | shmem_truncate_range(inode, newsize, (loff_t)-1); |
810 | } | 812 | } |
811 | 813 | ||
812 | error = inode_change_ok(inode, attr); | 814 | setattr_copy(inode, attr); |
813 | if (!error) | ||
814 | generic_setattr(inode, attr); | ||
815 | #ifdef CONFIG_TMPFS_POSIX_ACL | 815 | #ifdef CONFIG_TMPFS_POSIX_ACL |
816 | if (!error && (attr->ia_valid & ATTR_MODE)) | 816 | if (attr->ia_valid & ATTR_MODE) |
817 | error = generic_acl_chmod(inode); | 817 | error = generic_acl_chmod(inode); |
818 | #endif | 818 | #endif |
819 | return error; | 819 | return error; |
820 | } | 820 | } |
821 | 821 | ||
822 | static void shmem_delete_inode(struct inode *inode) | 822 | static void shmem_evict_inode(struct inode *inode) |
823 | { | 823 | { |
824 | struct shmem_inode_info *info = SHMEM_I(inode); | 824 | struct shmem_inode_info *info = SHMEM_I(inode); |
825 | 825 | ||
@@ -836,7 +836,7 @@ static void shmem_delete_inode(struct inode *inode) | |||
836 | } | 836 | } |
837 | BUG_ON(inode->i_blocks); | 837 | BUG_ON(inode->i_blocks); |
838 | shmem_free_inode(inode->i_sb); | 838 | shmem_free_inode(inode->i_sb); |
839 | clear_inode(inode); | 839 | end_writeback(inode); |
840 | } | 840 | } |
841 | 841 | ||
842 | static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir) | 842 | static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_t *edir) |
@@ -933,7 +933,7 @@ found: | |||
933 | 933 | ||
934 | /* | 934 | /* |
935 | * Move _head_ to start search for next from here. | 935 | * Move _head_ to start search for next from here. |
936 | * But be careful: shmem_delete_inode checks list_empty without taking | 936 | * But be careful: shmem_evict_inode checks list_empty without taking |
937 | * mutex, and there's an instant in list_move_tail when info->swaplist | 937 | * mutex, and there's an instant in list_move_tail when info->swaplist |
938 | * would appear empty, if it were the only one on shmem_swaplist. We | 938 | * would appear empty, if it were the only one on shmem_swaplist. We |
939 | * could avoid doing it if inode NULL; or use this minor optimization. | 939 | * could avoid doing it if inode NULL; or use this minor optimization. |
@@ -1223,6 +1223,7 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, | |||
1223 | struct shmem_sb_info *sbinfo; | 1223 | struct shmem_sb_info *sbinfo; |
1224 | struct page *filepage = *pagep; | 1224 | struct page *filepage = *pagep; |
1225 | struct page *swappage; | 1225 | struct page *swappage; |
1226 | struct page *prealloc_page = NULL; | ||
1226 | swp_entry_t *entry; | 1227 | swp_entry_t *entry; |
1227 | swp_entry_t swap; | 1228 | swp_entry_t swap; |
1228 | gfp_t gfp; | 1229 | gfp_t gfp; |
@@ -1247,7 +1248,6 @@ repeat: | |||
1247 | filepage = find_lock_page(mapping, idx); | 1248 | filepage = find_lock_page(mapping, idx); |
1248 | if (filepage && PageUptodate(filepage)) | 1249 | if (filepage && PageUptodate(filepage)) |
1249 | goto done; | 1250 | goto done; |
1250 | error = 0; | ||
1251 | gfp = mapping_gfp_mask(mapping); | 1251 | gfp = mapping_gfp_mask(mapping); |
1252 | if (!filepage) { | 1252 | if (!filepage) { |
1253 | /* | 1253 | /* |
@@ -1258,7 +1258,19 @@ repeat: | |||
1258 | if (error) | 1258 | if (error) |
1259 | goto failed; | 1259 | goto failed; |
1260 | radix_tree_preload_end(); | 1260 | radix_tree_preload_end(); |
1261 | if (sgp != SGP_READ && !prealloc_page) { | ||
1262 | /* We don't care if this fails */ | ||
1263 | prealloc_page = shmem_alloc_page(gfp, info, idx); | ||
1264 | if (prealloc_page) { | ||
1265 | if (mem_cgroup_cache_charge(prealloc_page, | ||
1266 | current->mm, GFP_KERNEL)) { | ||
1267 | page_cache_release(prealloc_page); | ||
1268 | prealloc_page = NULL; | ||
1269 | } | ||
1270 | } | ||
1271 | } | ||
1261 | } | 1272 | } |
1273 | error = 0; | ||
1262 | 1274 | ||
1263 | spin_lock(&info->lock); | 1275 | spin_lock(&info->lock); |
1264 | shmem_recalc_inode(inode); | 1276 | shmem_recalc_inode(inode); |
@@ -1387,17 +1399,16 @@ repeat: | |||
1387 | shmem_swp_unmap(entry); | 1399 | shmem_swp_unmap(entry); |
1388 | sbinfo = SHMEM_SB(inode->i_sb); | 1400 | sbinfo = SHMEM_SB(inode->i_sb); |
1389 | if (sbinfo->max_blocks) { | 1401 | if (sbinfo->max_blocks) { |
1390 | spin_lock(&sbinfo->stat_lock); | 1402 | if ((percpu_counter_compare(&sbinfo->used_blocks, sbinfo->max_blocks) > 0) || |
1391 | if (sbinfo->free_blocks == 0 || | ||
1392 | shmem_acct_block(info->flags)) { | 1403 | shmem_acct_block(info->flags)) { |
1393 | spin_unlock(&sbinfo->stat_lock); | ||
1394 | spin_unlock(&info->lock); | 1404 | spin_unlock(&info->lock); |
1395 | error = -ENOSPC; | 1405 | error = -ENOSPC; |
1396 | goto failed; | 1406 | goto failed; |
1397 | } | 1407 | } |
1398 | sbinfo->free_blocks--; | 1408 | percpu_counter_inc(&sbinfo->used_blocks); |
1409 | spin_lock(&inode->i_lock); | ||
1399 | inode->i_blocks += BLOCKS_PER_PAGE; | 1410 | inode->i_blocks += BLOCKS_PER_PAGE; |
1400 | spin_unlock(&sbinfo->stat_lock); | 1411 | spin_unlock(&inode->i_lock); |
1401 | } else if (shmem_acct_block(info->flags)) { | 1412 | } else if (shmem_acct_block(info->flags)) { |
1402 | spin_unlock(&info->lock); | 1413 | spin_unlock(&info->lock); |
1403 | error = -ENOSPC; | 1414 | error = -ENOSPC; |
@@ -1407,28 +1418,38 @@ repeat: | |||
1407 | if (!filepage) { | 1418 | if (!filepage) { |
1408 | int ret; | 1419 | int ret; |
1409 | 1420 | ||
1410 | spin_unlock(&info->lock); | 1421 | if (!prealloc_page) { |
1411 | filepage = shmem_alloc_page(gfp, info, idx); | 1422 | spin_unlock(&info->lock); |
1412 | if (!filepage) { | 1423 | filepage = shmem_alloc_page(gfp, info, idx); |
1413 | shmem_unacct_blocks(info->flags, 1); | 1424 | if (!filepage) { |
1414 | shmem_free_blocks(inode, 1); | 1425 | shmem_unacct_blocks(info->flags, 1); |
1415 | error = -ENOMEM; | 1426 | shmem_free_blocks(inode, 1); |
1416 | goto failed; | 1427 | error = -ENOMEM; |
1417 | } | 1428 | goto failed; |
1418 | SetPageSwapBacked(filepage); | 1429 | } |
1430 | SetPageSwapBacked(filepage); | ||
1419 | 1431 | ||
1420 | /* Precharge page while we can wait, compensate after */ | 1432 | /* |
1421 | error = mem_cgroup_cache_charge(filepage, current->mm, | 1433 | * Precharge page while we can wait, compensate |
1422 | GFP_KERNEL); | 1434 | * after |
1423 | if (error) { | 1435 | */ |
1424 | page_cache_release(filepage); | 1436 | error = mem_cgroup_cache_charge(filepage, |
1425 | shmem_unacct_blocks(info->flags, 1); | 1437 | current->mm, GFP_KERNEL); |
1426 | shmem_free_blocks(inode, 1); | 1438 | if (error) { |
1427 | filepage = NULL; | 1439 | page_cache_release(filepage); |
1428 | goto failed; | 1440 | shmem_unacct_blocks(info->flags, 1); |
1441 | shmem_free_blocks(inode, 1); | ||
1442 | filepage = NULL; | ||
1443 | goto failed; | ||
1444 | } | ||
1445 | |||
1446 | spin_lock(&info->lock); | ||
1447 | } else { | ||
1448 | filepage = prealloc_page; | ||
1449 | prealloc_page = NULL; | ||
1450 | SetPageSwapBacked(filepage); | ||
1429 | } | 1451 | } |
1430 | 1452 | ||
1431 | spin_lock(&info->lock); | ||
1432 | entry = shmem_swp_alloc(info, idx, sgp); | 1453 | entry = shmem_swp_alloc(info, idx, sgp); |
1433 | if (IS_ERR(entry)) | 1454 | if (IS_ERR(entry)) |
1434 | error = PTR_ERR(entry); | 1455 | error = PTR_ERR(entry); |
@@ -1469,13 +1490,19 @@ repeat: | |||
1469 | } | 1490 | } |
1470 | done: | 1491 | done: |
1471 | *pagep = filepage; | 1492 | *pagep = filepage; |
1472 | return 0; | 1493 | error = 0; |
1494 | goto out; | ||
1473 | 1495 | ||
1474 | failed: | 1496 | failed: |
1475 | if (*pagep != filepage) { | 1497 | if (*pagep != filepage) { |
1476 | unlock_page(filepage); | 1498 | unlock_page(filepage); |
1477 | page_cache_release(filepage); | 1499 | page_cache_release(filepage); |
1478 | } | 1500 | } |
1501 | out: | ||
1502 | if (prealloc_page) { | ||
1503 | mem_cgroup_uncharge_cache_page(prealloc_page); | ||
1504 | page_cache_release(prealloc_page); | ||
1505 | } | ||
1479 | return error; | 1506 | return error; |
1480 | } | 1507 | } |
1481 | 1508 | ||
@@ -1791,17 +1818,16 @@ static int shmem_statfs(struct dentry *dentry, struct kstatfs *buf) | |||
1791 | buf->f_type = TMPFS_MAGIC; | 1818 | buf->f_type = TMPFS_MAGIC; |
1792 | buf->f_bsize = PAGE_CACHE_SIZE; | 1819 | buf->f_bsize = PAGE_CACHE_SIZE; |
1793 | buf->f_namelen = NAME_MAX; | 1820 | buf->f_namelen = NAME_MAX; |
1794 | spin_lock(&sbinfo->stat_lock); | ||
1795 | if (sbinfo->max_blocks) { | 1821 | if (sbinfo->max_blocks) { |
1796 | buf->f_blocks = sbinfo->max_blocks; | 1822 | buf->f_blocks = sbinfo->max_blocks; |
1797 | buf->f_bavail = buf->f_bfree = sbinfo->free_blocks; | 1823 | buf->f_bavail = buf->f_bfree = |
1824 | sbinfo->max_blocks - percpu_counter_sum(&sbinfo->used_blocks); | ||
1798 | } | 1825 | } |
1799 | if (sbinfo->max_inodes) { | 1826 | if (sbinfo->max_inodes) { |
1800 | buf->f_files = sbinfo->max_inodes; | 1827 | buf->f_files = sbinfo->max_inodes; |
1801 | buf->f_ffree = sbinfo->free_inodes; | 1828 | buf->f_ffree = sbinfo->free_inodes; |
1802 | } | 1829 | } |
1803 | /* else leave those fields 0 like simple_statfs */ | 1830 | /* else leave those fields 0 like simple_statfs */ |
1804 | spin_unlock(&sbinfo->stat_lock); | ||
1805 | return 0; | 1831 | return 0; |
1806 | } | 1832 | } |
1807 | 1833 | ||
@@ -2242,7 +2268,6 @@ static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) | |||
2242 | { | 2268 | { |
2243 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); | 2269 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); |
2244 | struct shmem_sb_info config = *sbinfo; | 2270 | struct shmem_sb_info config = *sbinfo; |
2245 | unsigned long blocks; | ||
2246 | unsigned long inodes; | 2271 | unsigned long inodes; |
2247 | int error = -EINVAL; | 2272 | int error = -EINVAL; |
2248 | 2273 | ||
@@ -2250,9 +2275,8 @@ static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) | |||
2250 | return error; | 2275 | return error; |
2251 | 2276 | ||
2252 | spin_lock(&sbinfo->stat_lock); | 2277 | spin_lock(&sbinfo->stat_lock); |
2253 | blocks = sbinfo->max_blocks - sbinfo->free_blocks; | ||
2254 | inodes = sbinfo->max_inodes - sbinfo->free_inodes; | 2278 | inodes = sbinfo->max_inodes - sbinfo->free_inodes; |
2255 | if (config.max_blocks < blocks) | 2279 | if (percpu_counter_compare(&sbinfo->used_blocks, config.max_blocks) > 0) |
2256 | goto out; | 2280 | goto out; |
2257 | if (config.max_inodes < inodes) | 2281 | if (config.max_inodes < inodes) |
2258 | goto out; | 2282 | goto out; |
@@ -2269,7 +2293,6 @@ static int shmem_remount_fs(struct super_block *sb, int *flags, char *data) | |||
2269 | 2293 | ||
2270 | error = 0; | 2294 | error = 0; |
2271 | sbinfo->max_blocks = config.max_blocks; | 2295 | sbinfo->max_blocks = config.max_blocks; |
2272 | sbinfo->free_blocks = config.max_blocks - blocks; | ||
2273 | sbinfo->max_inodes = config.max_inodes; | 2296 | sbinfo->max_inodes = config.max_inodes; |
2274 | sbinfo->free_inodes = config.max_inodes - inodes; | 2297 | sbinfo->free_inodes = config.max_inodes - inodes; |
2275 | 2298 | ||
@@ -2302,7 +2325,10 @@ static int shmem_show_options(struct seq_file *seq, struct vfsmount *vfs) | |||
2302 | 2325 | ||
2303 | static void shmem_put_super(struct super_block *sb) | 2326 | static void shmem_put_super(struct super_block *sb) |
2304 | { | 2327 | { |
2305 | kfree(sb->s_fs_info); | 2328 | struct shmem_sb_info *sbinfo = SHMEM_SB(sb); |
2329 | |||
2330 | percpu_counter_destroy(&sbinfo->used_blocks); | ||
2331 | kfree(sbinfo); | ||
2306 | sb->s_fs_info = NULL; | 2332 | sb->s_fs_info = NULL; |
2307 | } | 2333 | } |
2308 | 2334 | ||
@@ -2344,7 +2370,8 @@ int shmem_fill_super(struct super_block *sb, void *data, int silent) | |||
2344 | #endif | 2370 | #endif |
2345 | 2371 | ||
2346 | spin_lock_init(&sbinfo->stat_lock); | 2372 | spin_lock_init(&sbinfo->stat_lock); |
2347 | sbinfo->free_blocks = sbinfo->max_blocks; | 2373 | if (percpu_counter_init(&sbinfo->used_blocks, 0)) |
2374 | goto failed; | ||
2348 | sbinfo->free_inodes = sbinfo->max_inodes; | 2375 | sbinfo->free_inodes = sbinfo->max_inodes; |
2349 | 2376 | ||
2350 | sb->s_maxbytes = SHMEM_MAX_BYTES; | 2377 | sb->s_maxbytes = SHMEM_MAX_BYTES; |
@@ -2496,7 +2523,7 @@ static const struct super_operations shmem_ops = { | |||
2496 | .remount_fs = shmem_remount_fs, | 2523 | .remount_fs = shmem_remount_fs, |
2497 | .show_options = shmem_show_options, | 2524 | .show_options = shmem_show_options, |
2498 | #endif | 2525 | #endif |
2499 | .delete_inode = shmem_delete_inode, | 2526 | .evict_inode = shmem_evict_inode, |
2500 | .drop_inode = generic_delete_inode, | 2527 | .drop_inode = generic_delete_inode, |
2501 | .put_super = shmem_put_super, | 2528 | .put_super = shmem_put_super, |
2502 | }; | 2529 | }; |