aboutsummaryrefslogtreecommitdiffstats
path: root/fs/btrfs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@fb.com>2015-03-17 10:52:28 -0400
committerJosef Bacik <jbacik@fb.com>2015-03-17 16:36:35 -0400
commite1cbbfa5f5aaf40a1fe70856fac4dfcc33e0e651 (patch)
tree0b2a6f3ccbcf72d81aff6119bbba83d02bc3d665 /fs/btrfs
parent6a3891c551268dd4ff0969b883c4c8b8d974db8f (diff)
Btrfs: fix outstanding_extents accounting in DIO
We are keeping track of how many extents we need to reserve properly based on the amount we want to write, but we were still incrementing outstanding_extents if we wrote less than what we requested. This isn't quite right since we will be limited to our max extent size. So instead lets do something horrible! Keep track of how many outstanding_extents we reserved, and decrement each time we allocate an extent. If we use our entire reserve make sure to jack up outstanding_extents on the inode so the accounting works out properly. Thanks, Reported-by: Filipe Manana <fdmanana@suse.com> Signed-off-by: Josef Bacik <jbacik@fb.com>
Diffstat (limited to 'fs/btrfs')
-rw-r--r--fs/btrfs/inode.c37
1 files changed, 35 insertions, 2 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 3717b3d4c2ce..aa1fb534f69f 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -7239,7 +7239,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
7239 u64 start = iblock << inode->i_blkbits; 7239 u64 start = iblock << inode->i_blkbits;
7240 u64 lockstart, lockend; 7240 u64 lockstart, lockend;
7241 u64 len = bh_result->b_size; 7241 u64 len = bh_result->b_size;
7242 u64 orig_len = len; 7242 u64 *outstanding_extents = NULL;
7243 int unlock_bits = EXTENT_LOCKED; 7243 int unlock_bits = EXTENT_LOCKED;
7244 int ret = 0; 7244 int ret = 0;
7245 7245
@@ -7251,6 +7251,16 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock,
7251 lockstart = start; 7251 lockstart = start;
7252 lockend = start + len - 1; 7252 lockend = start + len - 1;
7253 7253
7254 if (current->journal_info) {
7255 /*
7256 * Need to pull our outstanding extents and set journal_info to NULL so
7257 * that anything that needs to check if there's a transction doesn't get
7258 * confused.
7259 */
7260 outstanding_extents = current->journal_info;
7261 current->journal_info = NULL;
7262 }
7263
7254 /* 7264 /*
7255 * If this errors out it's because we couldn't invalidate pagecache for 7265 * If this errors out it's because we couldn't invalidate pagecache for
7256 * this range and we need to fallback to buffered. 7266 * this range and we need to fallback to buffered.
@@ -7374,11 +7384,20 @@ unlock:
7374 if (start + len > i_size_read(inode)) 7384 if (start + len > i_size_read(inode))
7375 i_size_write(inode, start + len); 7385 i_size_write(inode, start + len);
7376 7386
7377 if (len < orig_len) { 7387 /*
7388 * If we have an outstanding_extents count still set then we're
7389 * within our reservation, otherwise we need to adjust our inode
7390 * counter appropriately.
7391 */
7392 if (*outstanding_extents) {
7393 (*outstanding_extents)--;
7394 } else {
7378 spin_lock(&BTRFS_I(inode)->lock); 7395 spin_lock(&BTRFS_I(inode)->lock);
7379 BTRFS_I(inode)->outstanding_extents++; 7396 BTRFS_I(inode)->outstanding_extents++;
7380 spin_unlock(&BTRFS_I(inode)->lock); 7397 spin_unlock(&BTRFS_I(inode)->lock);
7381 } 7398 }
7399
7400 current->journal_info = outstanding_extents;
7382 btrfs_free_reserved_data_space(inode, len); 7401 btrfs_free_reserved_data_space(inode, len);
7383 } 7402 }
7384 7403
@@ -7402,6 +7421,8 @@ unlock:
7402unlock_err: 7421unlock_err:
7403 clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend, 7422 clear_extent_bit(&BTRFS_I(inode)->io_tree, lockstart, lockend,
7404 unlock_bits, 1, 0, &cached_state, GFP_NOFS); 7423 unlock_bits, 1, 0, &cached_state, GFP_NOFS);
7424 if (outstanding_extents)
7425 current->journal_info = outstanding_extents;
7405 return ret; 7426 return ret;
7406} 7427}
7407 7428
@@ -8101,6 +8122,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
8101{ 8122{
8102 struct file *file = iocb->ki_filp; 8123 struct file *file = iocb->ki_filp;
8103 struct inode *inode = file->f_mapping->host; 8124 struct inode *inode = file->f_mapping->host;
8125 u64 outstanding_extents = 0;
8104 size_t count = 0; 8126 size_t count = 0;
8105 int flags = 0; 8127 int flags = 0;
8106 bool wakeup = true; 8128 bool wakeup = true;
@@ -8138,6 +8160,16 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
8138 ret = btrfs_delalloc_reserve_space(inode, count); 8160 ret = btrfs_delalloc_reserve_space(inode, count);
8139 if (ret) 8161 if (ret)
8140 goto out; 8162 goto out;
8163 outstanding_extents = div64_u64(count +
8164 BTRFS_MAX_EXTENT_SIZE - 1,
8165 BTRFS_MAX_EXTENT_SIZE);
8166
8167 /*
8168 * We need to know how many extents we reserved so that we can
8169 * do the accounting properly if we go over the number we
8170 * originally calculated. Abuse current->journal_info for this.
8171 */
8172 current->journal_info = &outstanding_extents;
8141 } else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK, 8173 } else if (test_bit(BTRFS_INODE_READDIO_NEED_LOCK,
8142 &BTRFS_I(inode)->runtime_flags)) { 8174 &BTRFS_I(inode)->runtime_flags)) {
8143 inode_dio_done(inode); 8175 inode_dio_done(inode);
@@ -8150,6 +8182,7 @@ static ssize_t btrfs_direct_IO(int rw, struct kiocb *iocb,
8150 iter, offset, btrfs_get_blocks_direct, NULL, 8182 iter, offset, btrfs_get_blocks_direct, NULL,
8151 btrfs_submit_direct, flags); 8183 btrfs_submit_direct, flags);
8152 if (rw & WRITE) { 8184 if (rw & WRITE) {
8185 current->journal_info = NULL;
8153 if (ret < 0 && ret != -EIOCBQUEUED) 8186 if (ret < 0 && ret != -EIOCBQUEUED)
8154 btrfs_delalloc_release_space(inode, count); 8187 btrfs_delalloc_release_space(inode, count);
8155 else if (ret >= 0 && (size_t)ret < count) 8188 else if (ret >= 0 && (size_t)ret < count)