Commit 9036c102 authored by Yan Zheng's avatar Yan Zheng Committed by Chris Mason

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: default avatarYan Zheng <zheng.yan@oracle.com>
parent 19b9bdb0
...@@ -1908,6 +1908,7 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans, ...@@ -1908,6 +1908,7 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans,
int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode); int btrfs_orphan_add(struct btrfs_trans_handle *trans, struct inode *inode);
int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode); int btrfs_orphan_del(struct btrfs_trans_handle *trans, struct inode *inode);
void btrfs_orphan_cleanup(struct btrfs_root *root); void btrfs_orphan_cleanup(struct btrfs_root *root);
int btrfs_cont_expand(struct inode *inode, loff_t size);
/* ioctl.c */ /* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg); long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
/* bits for the flags field */ /* bits for the flags field */
#define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */ #define EXTENT_FLAG_PINNED 0 /* this entry not yet on disk, don't free it */
#define EXTENT_FLAG_COMPRESSED 1 #define EXTENT_FLAG_COMPRESSED 1
#define EXTENT_FLAG_VACANCY 2 /* no file extent item found */
struct extent_map { struct extent_map {
struct rb_node rb_node; struct rb_node rb_node;
......
...@@ -142,40 +142,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -142,40 +142,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
} }
set_extent_uptodate(io_tree, start_pos, end_of_last_block, GFP_NOFS); set_extent_uptodate(io_tree, start_pos, end_of_last_block, GFP_NOFS);
/* FIXME...EIEIO, ENOSPC and more */
/* insert any holes we need to create */
if (isize < start_pos) {
u64 last_pos_in_file;
u64 hole_size;
u64 mask = root->sectorsize - 1;
last_pos_in_file = (isize + mask) & ~mask;
hole_size = (start_pos - last_pos_in_file + mask) & ~mask;
if (hole_size > 0) {
btrfs_wait_ordered_range(inode, last_pos_in_file,
last_pos_in_file + hole_size);
mutex_lock(&BTRFS_I(inode)->extent_mutex);
err = btrfs_drop_extents(trans, root, inode,
last_pos_in_file,
last_pos_in_file + hole_size,
last_pos_in_file,
&hint_byte);
if (err)
goto failed;
err = btrfs_insert_file_extent(trans, root,
inode->i_ino,
last_pos_in_file,
0, 0, hole_size, 0,
hole_size, 0, 0, 0);
btrfs_drop_extent_cache(inode, last_pos_in_file,
last_pos_in_file + hole_size - 1, 0);
mutex_unlock(&BTRFS_I(inode)->extent_mutex);
btrfs_check_file(root, inode);
}
if (err)
goto failed;
}
/* check for reserved extents on each page, we don't want /* check for reserved extents on each page, we don't want
* to reset the delalloc bit on things that already have * to reset the delalloc bit on things that already have
* extents reserved. * extents reserved.
...@@ -191,7 +157,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -191,7 +157,6 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
i_size_write(inode, end_pos); i_size_write(inode, end_pos);
btrfs_update_inode(trans, root, inode); btrfs_update_inode(trans, root, inode);
} }
failed:
err = btrfs_end_transaction(trans, root); err = btrfs_end_transaction(trans, root);
out_unlock: out_unlock:
unlock_extent(io_tree, start_pos, end_of_last_block, GFP_NOFS); unlock_extent(io_tree, start_pos, end_of_last_block, GFP_NOFS);
...@@ -697,6 +662,12 @@ static int noinline prepare_pages(struct btrfs_root *root, struct file *file, ...@@ -697,6 +662,12 @@ static int noinline prepare_pages(struct btrfs_root *root, struct file *file,
start_pos = pos & ~((u64)root->sectorsize - 1); start_pos = pos & ~((u64)root->sectorsize - 1);
last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT; last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT;
if (start_pos > inode->i_size) {
err = btrfs_cont_expand(inode, start_pos);
if (err)
return err;
}
memset(pages, 0, num_pages * sizeof(struct page *)); memset(pages, 0, num_pages * sizeof(struct page *));
again: again:
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
......
...@@ -2296,81 +2296,91 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from) ...@@ -2296,81 +2296,91 @@ static int btrfs_truncate_page(struct address_space *mapping, loff_t from)
return ret; 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;
int err;
err = inode_change_ok(inode, attr);
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_trans_handle *trans;
struct btrfs_root *root = BTRFS_I(inode)->root; struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
struct extent_map *em;
u64 mask = root->sectorsize - 1; u64 mask = root->sectorsize - 1;
u64 hole_start = (inode->i_size + mask) & ~mask; u64 hole_start = (inode->i_size + mask) & ~mask;
u64 block_end = (attr->ia_size + mask) & ~mask; u64 block_end = (size + mask) & ~mask;
u64 last_byte;
u64 cur_offset;
u64 hole_size; u64 hole_size;
u64 alloc_hint = 0; int err;
if (attr->ia_size <= hole_start) if (size <= hole_start)
goto out; return 0;
err = btrfs_check_free_space(root, 1, 0); err = btrfs_check_free_space(root, 1, 0);
if (err) if (err)
goto fail; return err;
btrfs_truncate_page(inode->i_mapping, inode->i_size); btrfs_truncate_page(inode->i_mapping, inode->i_size);
hole_size = block_end - hole_start; while (1) {
while(1) {
struct btrfs_ordered_extent *ordered; struct btrfs_ordered_extent *ordered;
btrfs_wait_ordered_range(inode, hole_start, hole_size); btrfs_wait_ordered_range(inode, hole_start,
block_end - hole_start);
lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); lock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
ordered = btrfs_lookup_ordered_extent(inode, hole_start); ordered = btrfs_lookup_ordered_extent(inode, hole_start);
if (ordered) { if (!ordered)
unlock_extent(io_tree, hole_start,
block_end - 1, GFP_NOFS);
btrfs_put_ordered_extent(ordered);
} else {
break; break;
} unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
btrfs_put_ordered_extent(ordered);
} }
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode); 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);
if (alloc_hint != EXTENT_MAP_INLINE) { 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, err = btrfs_insert_file_extent(trans, root,
inode->i_ino, inode->i_ino, cur_offset, 0,
hole_start, 0, 0, 0, hole_size, 0, hole_size,
hole_size, 0, hole_size,
0, 0, 0); 0, 0, 0);
btrfs_drop_extent_cache(inode, hole_start, btrfs_drop_extent_cache(inode, hole_start,
(u64)-1, 0); last_byte - 1, 0);
btrfs_check_file(root, inode);
} }
mutex_unlock(&BTRFS_I(inode)->extent_mutex); free_extent_map(em);
cur_offset = last_byte;
if (err || cur_offset >= block_end)
break;
}
btrfs_end_transaction(trans, root); btrfs_end_transaction(trans, root);
unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS); unlock_extent(io_tree, hole_start, block_end - 1, GFP_NOFS);
return err;
}
static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
int err;
err = inode_change_ok(inode, attr);
if (err)
return err;
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) if (err)
return err; return err;
} }
out:
err = inode_setattr(inode, attr); err = inode_setattr(inode, attr);
if (!err && ((attr->ia_valid & ATTR_MODE))) if (!err && ((attr->ia_valid & ATTR_MODE)))
err = btrfs_acl_chmod(inode); err = btrfs_acl_chmod(inode);
fail:
return err; return err;
} }
...@@ -3456,27 +3466,44 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, ...@@ -3456,27 +3466,44 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
if (found_type == BTRFS_FILE_EXTENT_REG) { if (found_type == BTRFS_FILE_EXTENT_REG) {
extent_end = extent_start + extent_end = extent_start +
btrfs_file_extent_num_bytes(leaf, item); btrfs_file_extent_num_bytes(leaf, item);
err = 0; } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
if (start < extent_start || start >= extent_end) { size_t size;
em->start = start; size = btrfs_file_extent_inline_len(leaf, item);
if (start < extent_start) { extent_end = (extent_start + size + root->sectorsize - 1) &
if (start + len <= extent_start) ~((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;
}
if (ret > 0)
goto not_found; goto not_found;
em->len = extent_end - extent_start; leaf = path->nodes[0];
} else {
em->len = len;
} }
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; goto not_found_em;
} }
bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
if (bytenr == 0) { if (found_type == BTRFS_FILE_EXTENT_REG) {
em->start = extent_start; em->start = extent_start;
em->len = extent_end - extent_start; em->len = extent_end - extent_start;
bytenr = btrfs_file_extent_disk_bytenr(leaf, item);
if (bytenr == 0) {
em->block_start = EXTENT_MAP_HOLE; em->block_start = EXTENT_MAP_HOLE;
goto insert; goto insert;
} }
em->start = extent_start;
em->len = extent_end - extent_start;
if (compressed) { if (compressed) {
set_bit(EXTENT_FLAG_COMPRESSED, &em->flags); set_bit(EXTENT_FLAG_COMPRESSED, &em->flags);
em->block_start = bytenr; em->block_start = bytenr;
...@@ -3489,38 +3516,21 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, ...@@ -3489,38 +3516,21 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
} }
goto insert; goto insert;
} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { } else if (found_type == BTRFS_FILE_EXTENT_INLINE) {
u64 page_start;
unsigned long ptr; unsigned long ptr;
char *map; char *map;
size_t size; size_t size;
size_t extent_offset; size_t extent_offset;
size_t copy_size; 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; em->block_start = EXTENT_MAP_INLINE;
if (!page || create) { if (!page || create) {
em->start = extent_start; em->start = extent_start;
em->len = (size + root->sectorsize - 1) & em->len = extent_end - extent_start;
~((u64)root->sectorsize - 1);
goto out; goto out;
} }
page_start = page_offset(page) + pg_offset; size = btrfs_file_extent_inline_len(leaf, item);
extent_offset = page_start - extent_start; extent_offset = page_offset(page) + pg_offset - extent_start;
copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset, copy_size = min_t(u64, PAGE_CACHE_SIZE - pg_offset,
size - extent_offset); size - extent_offset);
em->start = extent_start + extent_offset; em->start = extent_start + extent_offset;
...@@ -3570,6 +3580,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page, ...@@ -3570,6 +3580,7 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
em->len = len; em->len = len;
not_found_em: not_found_em:
em->block_start = EXTENT_MAP_HOLE; em->block_start = EXTENT_MAP_HOLE;
set_bit(EXTENT_FLAG_VACANCY, &em->flags);
insert: insert:
btrfs_release_path(root, path); btrfs_release_path(root, path);
if (em->start > start || extent_map_end(em) <= start) { if (em->start > start || extent_map_end(em) <= start) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment