Btrfs: Fix i_blocks accounting
Now that delayed allocation accounting works, i_blocks accounting is changed
to only modify i_blocks when extents inserted or removed.
The fillattr call is changed to include the delayed allocation byte count
in the i_blocks result.
Signed-off-by: Chris Mason <chris.mason@oracle.com>
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 9a0647d..9b9db9c 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -36,6 +36,7 @@
* transid of the trans_handle that last modified this inode
*/
u64 last_trans;
+ u64 delalloc_bytes;
u32 flags;
};
static inline struct btrfs_inode *BTRFS_I(struct inode *inode)
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index dfb700e..52144b0 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -1161,6 +1161,15 @@
struct btrfs_root *root, struct btrfs_path *path,
u64 isize);
/* inode.c */
+static inline void dec_i_blocks(struct inode *inode, u64 dec)
+{
+ dec = dec >> 9;
+ if (dec <= inode->i_blocks)
+ inode->i_blocks -= dec;
+ else
+ inode->i_blocks = 0;
+}
+
unsigned long btrfs_force_ra(struct address_space *mapping,
struct file_ra_state *ra, struct file *file,
pgoff_t offset, pgoff_t last_index);
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
index bfa4149..f893960 100644
--- a/fs/btrfs/file.c
+++ b/fs/btrfs/file.c
@@ -175,6 +175,7 @@
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
+ inode->i_blocks += (offset + size - found_end) >> 9;
}
if (found_end < offset) {
ptr = btrfs_file_extent_inline_start(ei) + found_size;
@@ -184,6 +185,7 @@
insert:
btrfs_release_path(root, path);
datasize = offset + size - key.offset;
+ inode->i_blocks += datasize >> 9;
datasize = btrfs_file_extent_calc_inline_size(datasize);
ret = btrfs_insert_empty_item(trans, root, path, &key,
datasize);
@@ -256,7 +258,6 @@
goto out_unlock;
}
btrfs_set_trans_block_group(trans, inode);
- inode->i_blocks += num_bytes >> 9;
hint_byte = 0;
if ((end_of_last_block & 4095) == 0) {
@@ -410,7 +411,7 @@
if (found_key.type != BTRFS_EXTENT_DATA_KEY)
goto out;
- if (found_key.offset != last_offset) {
+ if (found_key.offset < last_offset) {
WARN_ON(1);
btrfs_print_leaf(root, leaf);
printk("inode %lu found offset %Lu expected %Lu\n",
@@ -435,7 +436,7 @@
last_offset = extent_end;
path->slots[0]++;
}
- if (last_offset < inode->i_size) {
+ if (0 && last_offset < inode->i_size) {
WARN_ON(1);
btrfs_print_leaf(root, leaf);
printk("inode %lu found offset %Lu size %Lu\n", inode->i_ino,
@@ -608,8 +609,7 @@
extent);
if (btrfs_file_extent_disk_bytenr(leaf,
extent)) {
- inode->i_blocks -=
- (old_num - new_num) >> 9;
+ dec_i_blocks(inode, old_num - new_num);
}
btrfs_set_file_extent_num_bytes(leaf, extent,
new_num);
@@ -620,6 +620,8 @@
u32 new_size;
new_size = btrfs_file_extent_calc_inline_size(
inline_limit - key.offset);
+ dec_i_blocks(inode, (extent_end - key.offset) -
+ (inline_limit - key.offset));
btrfs_truncate_item(trans, root, path,
new_size, 1);
}
@@ -653,7 +655,7 @@
btrfs_release_path(root, path);
extent = NULL;
if (found_extent && disk_bytenr != 0) {
- inode->i_blocks -= extent_num_bytes >> 9;
+ dec_i_blocks(inode, extent_num_bytes);
ret = btrfs_free_extent(trans, root,
disk_bytenr,
disk_num_bytes,
@@ -674,6 +676,8 @@
u32 new_size;
new_size = btrfs_file_extent_calc_inline_size(
extent_end - end);
+ dec_i_blocks(inode, (extent_end - key.offset) -
+ (extent_end - end));
btrfs_truncate_item(trans, root, path, new_size, 0);
}
/* create bookend, splitting the extent in two */
@@ -718,6 +722,7 @@
}
out:
btrfs_free_path(path);
+ btrfs_check_file(root, inode);
return ret;
}
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index 44fc94f..913ab12 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -134,6 +134,7 @@
ret = btrfs_insert_file_extent(trans, root, inode->i_ino,
start, ins.objectid, ins.offset,
ins.offset);
+ inode->i_blocks += ins.offset >> 9;
btrfs_check_file(root, inode);
num_bytes -= cur_alloc_size;
alloc_hint = ins.objectid + ins.offset;
@@ -142,6 +143,7 @@
btrfs_drop_extent_cache(inode, orig_start,
orig_start + orig_num_bytes - 1);
btrfs_add_ordered_inode(inode);
+ btrfs_update_inode(trans, root, inode);
out:
btrfs_end_transaction(trans, root);
return ret;
@@ -265,6 +267,7 @@
if (!(old & EXTENT_DELALLOC) && (bits & EXTENT_DELALLOC)) {
struct btrfs_root *root = BTRFS_I(inode)->root;
spin_lock(&root->fs_info->delalloc_lock);
+ BTRFS_I(inode)->delalloc_bytes += end - start + 1;
root->fs_info->delalloc_bytes += end - start + 1;
spin_unlock(&root->fs_info->delalloc_lock);
}
@@ -281,8 +284,10 @@
printk("warning: delalloc account %Lu %Lu\n",
end - start + 1, root->fs_info->delalloc_bytes);
root->fs_info->delalloc_bytes = 0;
+ BTRFS_I(inode)->delalloc_bytes = 0;
} else {
root->fs_info->delalloc_bytes -= end - start + 1;
+ BTRFS_I(inode)->delalloc_bytes -= end - start + 1;
}
spin_unlock(&root->fs_info->delalloc_lock);
}
@@ -833,32 +838,37 @@
btrfs_set_file_extent_num_bytes(leaf, fi,
extent_num_bytes);
num_dec = (orig_num_bytes -
- extent_num_bytes) >> 9;
- if (extent_start != 0) {
- inode->i_blocks -= num_dec;
- }
+ extent_num_bytes);
+ if (extent_start != 0)
+ dec_i_blocks(inode, num_dec);
btrfs_mark_buffer_dirty(leaf);
} else {
extent_num_bytes =
btrfs_file_extent_disk_num_bytes(leaf,
fi);
/* FIXME blocksize != 4096 */
- num_dec = btrfs_file_extent_num_bytes(leaf,
- fi) >> 9;
+ num_dec = btrfs_file_extent_num_bytes(leaf, fi);
if (extent_start != 0) {
found_extent = 1;
- inode->i_blocks -= num_dec;
+ dec_i_blocks(inode, num_dec);
}
root_gen = btrfs_header_generation(leaf);
root_owner = btrfs_header_owner(leaf);
}
- } else if (extent_type == BTRFS_FILE_EXTENT_INLINE &&
- !del_item) {
- u32 newsize = inode->i_size - found_key.offset;
- newsize = btrfs_file_extent_calc_inline_size(newsize);
- ret = btrfs_truncate_item(trans, root, path,
- newsize, 1);
- BUG_ON(ret);
+ } else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
+ if (!del_item) {
+ u32 newsize = inode->i_size - found_key.offset;
+ dec_i_blocks(inode, item_end + 1 -
+ found_key.offset - newsize);
+ newsize =
+ btrfs_file_extent_calc_inline_size(newsize);
+ ret = btrfs_truncate_item(trans, root, path,
+ newsize, 1);
+ BUG_ON(ret);
+ } else {
+ dec_i_blocks(inode, item_end + 1 -
+ found_key.offset);
+ }
}
delete:
if (del_item) {
@@ -1222,6 +1232,7 @@
struct btrfs_iget_args *args = p;
inode->i_ino = args->ino;
BTRFS_I(inode)->root = args->root;
+ BTRFS_I(inode)->delalloc_bytes = 0;
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
inode->i_mapping, GFP_NOFS);
@@ -1528,6 +1539,7 @@
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
inode->i_mapping, GFP_NOFS);
+ BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->root = root;
if (mode & S_IFDIR)
@@ -1746,6 +1758,7 @@
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
inode->i_mapping, GFP_NOFS);
+ BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
}
dir->i_sb->s_dirt = 1;
@@ -2797,6 +2810,7 @@
struct inode *inode = dentry->d_inode;
generic_fillattr(inode, stat);
stat->blksize = PAGE_CACHE_SIZE;
+ stat->blocks = inode->i_blocks + (BTRFS_I(inode)->delalloc_bytes >> 9);
return 0;
}
@@ -2912,6 +2926,7 @@
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
inode->i_mapping, GFP_NOFS);
+ BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
}
dir->i_sb->s_dirt = 1;