aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs/extent-tree.c
diff options
context:
space:
mode:
authorJosef Bacik <josef@redhat.com>2011-11-08 15:47:34 -0500
committerChris Mason <chris.mason@oracle.com>2011-11-08 15:47:34 -0500
commit7fd2ae21a42d178982679b86086661292b4afe4a (patch)
tree236c22d807ca5f2419a1b0394bd1092aab730cde /fs/btrfs/extent-tree.c
parent917c16b2b69fc2eeb432eabca73258f08c58361e (diff)
Btrfs: fix our reservations for updating an inode when completing io
People have been reporting ENOSPC crashes in finish_ordered_io. This is because we try to steal from the delalloc block rsv to satisfy a reservation to update the inode. The problem with this is we don't explicitly save space for updating the inode when doing delalloc. This is kind of a problem and we've gotten away with this because way back when we just stole from the delalloc reserve without any questions, and this worked out fine because generally speaking the leaf had been modified either by the mtime update when we did the original write or because we just updated the leaf when we inserted the file extent item, only on rare occasions had the leaf not actually been modified, and that was still ok because we'd just use a block or two out of the over-reservation that is delalloc. Then came the delayed inode stuff. This is amazing, except it wants a full reservation for updating the inode since it may do it at some point down the road after we've written the blocks and we have to recow everything again. This worked out because the delayed inode stuff just stole from the global reserve, that is until recently when I changed that because it caused other problems. So here we are, we're doing everything right and being screwed for it. So take an extra reservation for the inode at delalloc reservation time and carry it through the life of the delalloc reservation. If we need it we can steal it in the delayed inode stuff. If we have already stolen it try and do a normal metadata reservation. If that fails try to steal from the delalloc reservation. If _that_ fails we'll get a WARN_ON() so I can start thinking of a better way to solve this and in the meantime we'll steal from the global reserve. With this patch I ran xfstests 13 in a loop for a couple of hours and didn't see any problems. Signed-off-by: Josef Bacik <josef@redhat.com> Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/extent-tree.c')
-rw-r--r--fs/btrfs/extent-tree.c22
1 files changed, 19 insertions, 3 deletions
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 18ea90c8943b..0b044e509e9f 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -4063,23 +4063,30 @@ int btrfs_snap_reserve_metadata(struct btrfs_trans_handle *trans,
4063 */ 4063 */
4064static unsigned drop_outstanding_extent(struct inode *inode) 4064static unsigned drop_outstanding_extent(struct inode *inode)
4065{ 4065{
4066 unsigned drop_inode_space = 0;
4066 unsigned dropped_extents = 0; 4067 unsigned dropped_extents = 0;
4067 4068
4068 BUG_ON(!BTRFS_I(inode)->outstanding_extents); 4069 BUG_ON(!BTRFS_I(inode)->outstanding_extents);
4069 BTRFS_I(inode)->outstanding_extents--; 4070 BTRFS_I(inode)->outstanding_extents--;
4070 4071
4072 if (BTRFS_I(inode)->outstanding_extents == 0 &&
4073 BTRFS_I(inode)->delalloc_meta_reserved) {
4074 drop_inode_space = 1;
4075 BTRFS_I(inode)->delalloc_meta_reserved = 0;
4076 }
4077
4071 /* 4078 /*
4072 * If we have more or the same amount of outsanding extents than we have 4079 * If we have more or the same amount of outsanding extents than we have
4073 * reserved then we need to leave the reserved extents count alone. 4080 * reserved then we need to leave the reserved extents count alone.
4074 */ 4081 */
4075 if (BTRFS_I(inode)->outstanding_extents >= 4082 if (BTRFS_I(inode)->outstanding_extents >=
4076 BTRFS_I(inode)->reserved_extents) 4083 BTRFS_I(inode)->reserved_extents)
4077 return 0; 4084 return drop_inode_space;
4078 4085
4079 dropped_extents = BTRFS_I(inode)->reserved_extents - 4086 dropped_extents = BTRFS_I(inode)->reserved_extents -
4080 BTRFS_I(inode)->outstanding_extents; 4087 BTRFS_I(inode)->outstanding_extents;
4081 BTRFS_I(inode)->reserved_extents -= dropped_extents; 4088 BTRFS_I(inode)->reserved_extents -= dropped_extents;
4082 return dropped_extents; 4089 return dropped_extents + drop_inode_space;
4083} 4090}
4084 4091
4085/** 4092/**
@@ -4165,9 +4172,18 @@ int btrfs_delalloc_reserve_metadata(struct inode *inode, u64 num_bytes)
4165 nr_extents = BTRFS_I(inode)->outstanding_extents - 4172 nr_extents = BTRFS_I(inode)->outstanding_extents -
4166 BTRFS_I(inode)->reserved_extents; 4173 BTRFS_I(inode)->reserved_extents;
4167 BTRFS_I(inode)->reserved_extents += nr_extents; 4174 BTRFS_I(inode)->reserved_extents += nr_extents;
4175 }
4168 4176
4169 to_reserve = btrfs_calc_trans_metadata_size(root, nr_extents); 4177 /*
4178 * Add an item to reserve for updating the inode when we complete the
4179 * delalloc io.
4180 */
4181 if (!BTRFS_I(inode)->delalloc_meta_reserved) {
4182 nr_extents++;
4183 BTRFS_I(inode)->delalloc_meta_reserved = 1;
4170 } 4184 }
4185
4186 to_reserve = btrfs_calc_trans_metadata_size(root, nr_extents);
4171 to_reserve += calc_csum_metadata_size(inode, num_bytes, 1); 4187 to_reserve += calc_csum_metadata_size(inode, num_bytes, 1);
4172 spin_unlock(&BTRFS_I(inode)->lock); 4188 spin_unlock(&BTRFS_I(inode)->lock);
4173 4189