aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2009-10-13 16:46:49 -0400
committerChris Mason <chris.mason@oracle.com>2009-10-14 10:32:47 -0400
commit5d5e103a70f74ae98e3965a4add1ab951d0651d1 (patch)
tree83b1cc73830f72f592aa804207a679912beef101
parent0eda294dfc980c1cbe4f8a0564bf543f86a01ddb (diff)
Btrfs: fix possible ENOSPC problems with truncate
There's a problem where we don't do any space reservation for truncates, which can cause you to OOPs because you will be allowed to go off in the weeds a bit since we don't account for the delalloc bytes that are created as a result of the truncate. Signed-off-by: Josef Bacik <jbacik@redhat.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/inode.c27
1 files changed, 24 insertions, 3 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 5b9567caba0a..78139efe41fc 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -3032,12 +3032,22 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
3032 3032
3033 if ((offset & (blocksize - 1)) == 0) 3033 if ((offset & (blocksize - 1)) == 0)
3034 goto out; 3034 goto out;
3035 ret = btrfs_check_data_free_space(root, inode, PAGE_CACHE_SIZE);
3036 if (ret)
3037 goto out;
3038
3039 ret = btrfs_reserve_metadata_for_delalloc(root, inode, 1);
3040 if (ret)
3041 goto out;
3035 3042
3036 ret = -ENOMEM; 3043 ret = -ENOMEM;
3037again: 3044again:
3038 page = grab_cache_page(mapping, index); 3045 page = grab_cache_page(mapping, index);
3039 if (!page) 3046 if (!page) {
3047 btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE);
3048 btrfs_unreserve_metadata_for_delalloc(root, inode, 1);
3040 goto out; 3049 goto out;
3050 }
3041 3051
3042 page_start = page_offset(page); 3052 page_start = page_offset(page);
3043 page_end = page_start + PAGE_CACHE_SIZE - 1; 3053 page_end = page_start + PAGE_CACHE_SIZE - 1;
@@ -3070,6 +3080,10 @@ again:
3070 goto again; 3080 goto again;
3071 } 3081 }
3072 3082
3083 clear_extent_bits(&BTRFS_I(inode)->io_tree, page_start, page_end,
3084 EXTENT_DIRTY | EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING,
3085 GFP_NOFS);
3086
3073 ret = btrfs_set_extent_delalloc(inode, page_start, page_end); 3087 ret = btrfs_set_extent_delalloc(inode, page_start, page_end);
3074 if (ret) { 3088 if (ret) {
3075 unlock_extent(io_tree, page_start, page_end, GFP_NOFS); 3089 unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
@@ -3088,6 +3102,9 @@ again:
3088 unlock_extent(io_tree, page_start, page_end, GFP_NOFS); 3102 unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
3089 3103
3090out_unlock: 3104out_unlock:
3105 if (ret)
3106 btrfs_free_reserved_data_space(root, inode, PAGE_CACHE_SIZE);
3107 btrfs_unreserve_metadata_for_delalloc(root, inode, 1);
3091 unlock_page(page); 3108 unlock_page(page);
3092 page_cache_release(page); 3109 page_cache_release(page);
3093out: 3110out:
@@ -3111,7 +3128,9 @@ int btrfs_cont_expand(struct inode *inode, loff_t size)
3111 if (size <= hole_start) 3128 if (size <= hole_start)
3112 return 0; 3129 return 0;
3113 3130
3114 btrfs_truncate_page(inode->i_mapping, inode->i_size); 3131 err = btrfs_truncate_page(inode->i_mapping, inode->i_size);
3132 if (err)
3133 return err;
3115 3134
3116 while (1) { 3135 while (1) {
3117 struct btrfs_ordered_extent *ordered; 3136 struct btrfs_ordered_extent *ordered;
@@ -5008,7 +5027,9 @@ static void btrfs_truncate(struct inode *inode)
5008 if (IS_APPEND(inode) || IS_IMMUTABLE(inode)) 5027 if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
5009 return; 5028 return;
5010 5029
5011 btrfs_truncate_page(inode->i_mapping, inode->i_size); 5030 ret = btrfs_truncate_page(inode->i_mapping, inode->i_size);
5031 if (ret)
5032 return;
5012 btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1); 5033 btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
5013 5034
5014 trans = btrfs_start_transaction(root, 1); 5035 trans = btrfs_start_transaction(root, 1);