summaryrefslogtreecommitdiffstats
path: root/fs/btrfs/inode.c
diff options
context:
space:
mode:
authorYan Zheng2008-10-30 19:19:41 +0100
committerChris Mason2008-10-30 19:19:41 +0100
commit9036c10208e1fc496cef7692ba66a78699b360dc (patch)
treea5b272158acc0e01e71731f5ccbc895a8eee1151 /fs/btrfs/inode.c
parentBtrfs: Fix logic to avoid reading checksums for -o nodatasum,compress (diff)
downloadkernel-qcow2-linux-9036c10208e1fc496cef7692ba66a78699b360dc.tar.gz
kernel-qcow2-linux-9036c10208e1fc496cef7692ba66a78699b360dc.tar.xz
kernel-qcow2-linux-9036c10208e1fc496cef7692ba66a78699b360dc.zip
Btrfs: update hole handling v2
This patch splits the hole insertion code out of btrfs_setattr into btrfs_cont_expand and updates btrfs_get_extent to properly handle the case that file extent items are not continuous. Signed-off-by: Yan Zheng <zheng.yan@oracle.com>
Diffstat (limited to 'fs/btrfs/inode.c')
-rw-r--r--fs/btrfs/inode.c189
1 files changed, 100 insertions, 89 deletions
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
index dd9cd01042b8..8254d6fa6910 100644
--- a/fs/btrfs/inode.c
+++ b/fs/btrfs/inode.c
@@ -2296,81 +2296,91 @@ out:
return ret;
}
-static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
+int btrfs_cont_expand(struct inode *inode, loff_t size)
{
- struct inode *inode = dentry->d_inode;
+ struct btrfs_trans_handle *trans;
+ struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ struct extent_map *em;
+ u64 mask = root->sectorsize - 1;
+ u64 hole_start = (inode->i_size + mask) & ~mask;
+ u64 block_end = (size + mask) & ~mask;
+ u64 last_byte;
+ u64 cur_offset;
+ u64 hole_size;
int err;
- err = inode_change_ok(inode, attr);
+ if (size <= hole_start)
+ return 0;
+
+ err = btrfs_check_free_space(root, 1, 0);
if (err)
return err;
- if (S_ISREG(inode->i_mode) &&
- attr->ia_valid & ATTR_SIZE && attr->ia_size > inode->i_size) {
- struct btrfs_trans_handle *trans;
- struct btrfs_root *root = BTRFS_I(inode)->root;
- struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ btrfs_truncate_page(inode->i_mapping, inode->i_size);
- u64 mask = root->sectorsize - 1;
- u64 hole_start = (inode->i_size + mask) & ~mask;
- u64 block_end = (attr->ia_size + mask) & ~mask;
- u64 hole_size;
- u64 alloc_hint = 0;
+ while (1) {
+ struct btrfs_ordered_extent *ordered;
+ btrfs_wait_ordered_range(inode, hole_start,
+ block_end - hole_start);
+ lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
+ ordered = btrfs_lookup_ordered_extent(inode, hole_start);
+ if (!ordered)
+ break;
+ unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
+ btrfs_put_ordered_extent(ordered);
+ }
- if (attr->ia_size <= hole_start)
- goto out;
+ trans = btrfs_start_transaction(root, 1);
+ btrfs_set_trans_block_group(trans, inode);
- err = btrfs_check_free_space(root, 1, 0);
- if (err)
- goto fail;
+ cur_offset = hole_start;
+ while (1) {
+ em = btrfs_get_extent(inode, NULL, 0, cur_offset,
+ block_end - cur_offset, 0);
+ BUG_ON(IS_ERR(em) || !em);
+ last_byte = min(extent_map_end(em), block_end);
+ last_byte = (last_byte + mask) & ~mask;
+ if (test_bit(EXTENT_FLAG_VACANCY, &em->flags)) {
+ hole_size = last_byte - cur_offset;
+ err = btrfs_insert_file_extent(trans, root,
+ inode->i_ino, cur_offset, 0,
+ 0, hole_size, 0, hole_size,
+ 0, 0, 0);
+ btrfs_drop_extent_cache(inode, hole_start,
+ last_byte - 1, 0);
+ }
+ free_extent_map(em);
+ cur_offset = last_byte;
+ if (err || cur_offset >= block_end)
+ break;
+ }
- btrfs_truncate_page(inode->i_mapping, inode->i_size);
+ btrfs_end_transaction(trans, root);
+ unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
+ return err;
+}
- hole_size = block_end - hole_start;
- while(1) {
- struct btrfs_ordered_extent *ordered;
- btrfs_wait_ordered_range(inode, hole_start, hole_size);
-
- lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
- ordered = btrfs_lookup_ordered_extent(inode, hole_start);
- if (ordered) {
- unlock_extent(io_tree, hole_start,
- block_end - 1, GFP_NOFS);
- btrfs_put_ordered_extent(ordered);
- } else {
- break;
- }
- }
+static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+ struct inode *inode = dentry->d_inode;
+ int err;
- trans = btrfs_start_transaction(root, 1);
- btrfs_set_trans_block_group(trans, inode);
- mutex_lock(&BTRFS_I(inode)->extent_mutex);
- err = btrfs_drop_extents(trans, root, inode,
- hole_start, block_end, hole_start,
- &alloc_hint);
+ err = inode_change_ok(inode, attr);
+ if (err)
+ return err;
- if (alloc_hint != EXTENT_MAP_INLINE) {
- err = btrfs_insert_file_extent(trans, root,
- inode->i_ino,
- hole_start, 0, 0,
- hole_size, 0, hole_size,
- 0, 0, 0);
- btrfs_drop_extent_cache(inode, hole_start,
- (u64)-1, 0);
- btrfs_check_file(root, inode);
- }
- mutex_unlock(&BTRFS_I(inode)->extent_mutex);
- btrfs_end_transaction(trans, root);
- unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
+ if (S_ISREG(inode->i_mode) &&
+ attr->ia_valid & ATTR_SIZE && attr->ia_size > inode->i_size) {
+ err = btrfs_cont_expand(inode, attr->ia_size);
if (err)
return err;
}
-out:
+
err = inode_setattr(inode, attr);
if (!err && ((attr->ia_valid & ATTR_MODE)))
err = btrfs_acl_chmod(inode);
-fail:
return err;
}
@@ -3456,27 +3466,44 @@ again:
if (found_type == BTRFS_FILE_EXTENT_REG) {
extent_end = extent_start +
btrfs_file_extent_num_bytes(leaf, item);
- err = 0;
- if (start < extent_start || start >= extent_end) {
- em->start = start;
- if (start < extent_start) {
- if (start + len <= extent_start)
- goto not_found;
- em->len = extent_end - extent_start;
- } else {
- em->len = len;
+ } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
+ size_t size;
+ size = btrfs_file_extent_inline_len(leaf, item);
+ extent_end = (extent_start + size + root->sectorsize - 1) &
+ ~((u64)root->sectorsize - 1);
+ }
+
+ if (start >= extent_end) {
+ path->slots[0]++;
+ if (path->slots[0] >= btrfs_header_nritems(leaf)) {
+ ret = btrfs_next_leaf(root, path);
+ if (ret < 0) {
+ err = ret;
+ goto out;
}
- goto not_found_em;
+ if (ret > 0)
+ goto not_found;
+ leaf = path->nodes[0];
}
+ btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
+ if (found_key.objectid != objectid ||
+ found_key.type != BTRFS_EXTENT_DATA_KEY)
+ goto not_found;
+ if (start + len <= found_key.offset)
+ goto not_found;
+ em->start = start;
+ em->len = found_key.offset - start;
+ goto not_found_em;
+ }
+
+ if (found_type == BTRFS_FILE_EXTENT_REG) {
+ em->start = extent_start;
+ em->len = extent_end - extent_start;
bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
if (bytenr == 0) {
- em->start = extent_start;
- em->len = extent_end - extent_start;
em->block_start = EXTENT_MAP_HOLE;
goto insert;
}
- em->start = extent_start;
- em->len = extent_end - extent_start;
if (compressed) {
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
em->block_start = bytenr;
@@ -3489,38 +3516,21 @@ again:
}
goto insert;
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
- u64 page_start;
unsigned long ptr;
char *map;
size_t size;
size_t extent_offset;
size_t copy_size;
- size = btrfs_file_extent_inline_len(leaf, item);
- extent_end = (extent_start + size + root->sectorsize - 1) &
- ~((u64)root->sectorsize - 1);
- if (start < extent_start || start >= extent_end) {
- em->start = start;
- if (start < extent_start) {
- if (start + len <= extent_start)
- goto not_found;
- em->len = extent_end - extent_start;
- } else {
- em->len = len;
- }
- goto not_found_em;
- }
em->block_start = EXTENT_MAP_INLINE;
-
if (!page || create) {
em->start = extent_start;
- em->len = (size + root->sectorsize - 1) &
- ~((u64)root->sectorsize - 1);
+ em->len = extent_end - extent_start;
goto out;
}
- page_start = page_offset(page) + pg_offset;
- extent_offset = page_start - extent_start;
+ size = btrfs_file_extent_inline_len(leaf, item);
+ extent_offset = page_offset(page) + pg_offset - extent_start;
copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset,
size - extent_offset);
em->start = extent_start + extent_offset;
@@ -3570,6 +3580,7 @@ not_found:
em->len = len;
not_found_em:
em->block_start = EXTENT_MAP_HOLE;
+ set_bit(EXTENT_FLAG_VACANCY, &em->flags);
insert:
btrfs_release_path(root, path);
if (em->start > start || extent_map_end(em) <= start) {