Skip to content

Commit

Permalink
Btrfs: Fix a number of inline extent problems that Yan Zheng reported.
Browse files Browse the repository at this point in the history
The fixes do a number of things:

1) Most btrfs_drop_extent callers will try to leave the inline extents in
place.  It can truncate bytes off the beginning of the inline extent if
required.

2) writepage can now update the inline extent, allowing mmap writes to
go directly into the inline extent.

3) btrfs_truncate_in_transaction truncates inline extents

4) extent_map.c fixed to not merge inline extent mappings and hole
mappings together

Signed-off-by: Chris Mason <chris.mason@oracle.com>
  • Loading branch information
Chris Mason committed Sep 25, 2008
1 parent 35ebb93 commit 179e29e
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 38 deletions.
52 changes: 46 additions & 6 deletions fs/btrfs/ctree.c
Original file line number Diff line number Diff line change
Expand Up @@ -1930,7 +1930,7 @@ static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u32 new_size)
u32 new_size, int from_end)
{
int ret = 0;
int slot;
Expand All @@ -1946,13 +1946,17 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,

slot_orig = path->slots[0];
leaf = path->nodes[0];
slot = path->slots[0];

old_size = btrfs_item_size_nr(leaf, slot);
if (old_size == new_size)
return 0;

nritems = btrfs_header_nritems(leaf);
data_end = leaf_data_end(root, leaf);

slot = path->slots[0];
old_data_start = btrfs_item_offset_nr(leaf, slot);
old_size = btrfs_item_size_nr(leaf, slot); BUG_ON(old_size <= new_size);

size_diff = old_size - new_size;

BUG_ON(slot < 0);
Expand Down Expand Up @@ -1984,9 +1988,45 @@ int btrfs_truncate_item(struct btrfs_trans_handle *trans,
}

/* shift the data */
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
data_end + size_diff, btrfs_leaf_data(leaf) +
data_end, old_data_start + new_size - data_end);
if (from_end) {
memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
data_end + size_diff, btrfs_leaf_data(leaf) +
data_end, old_data_start + new_size - data_end);
} else {
struct btrfs_disk_key disk_key;
u64 offset;

btrfs_item_key(leaf, &disk_key, slot);

if (btrfs_disk_key_type(&disk_key) == BTRFS_EXTENT_DATA_KEY) {
unsigned long ptr;
struct btrfs_file_extent_item *fi;

fi = btrfs_item_ptr(leaf, slot,
struct btrfs_file_extent_item);
fi = (struct btrfs_file_extent_item *)(
(unsigned long)fi - size_diff);

if (btrfs_file_extent_type(leaf, fi) ==
BTRFS_FILE_EXTENT_INLINE) {
ptr = btrfs_item_ptr_offset(leaf, slot);
memmove_extent_buffer(leaf, ptr,
(unsigned long)fi,
offsetof(struct btrfs_file_extent_item,
disk_bytenr));
}
}

memmove_extent_buffer(leaf, btrfs_leaf_data(leaf) +
data_end + size_diff, btrfs_leaf_data(leaf) +
data_end, old_data_start - data_end);

offset = btrfs_disk_key_offset(&disk_key);
btrfs_set_disk_key_offset(&disk_key, offset + size_diff);
btrfs_set_item_key(leaf, &disk_key, slot);
if (slot == 0)
fixup_low_keys(trans, root, path, &disk_key, 1);
}

item = btrfs_item_nr(leaf, slot);
btrfs_set_item_size(leaf, item, new_size);
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/ctree.h
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,7 @@ int btrfs_extend_item(struct btrfs_trans_handle *trans, struct btrfs_root
int btrfs_truncate_item(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path,
u32 new_size);
u32 new_size, int from_end);
int btrfs_search_slot(struct btrfs_trans_handle *trans, struct btrfs_root
*root, struct btrfs_key *key, struct btrfs_path *p, int
ins_len, int cow);
Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/dir-item.c
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ int btrfs_delete_one_dir_name(struct btrfs_trans_handle *trans,
memmove_extent_buffer(leaf, ptr, ptr + sub_item_len,
item_len - (ptr + sub_item_len - start));
ret = btrfs_truncate_item(trans, root, path,
item_len - sub_item_len);
item_len - sub_item_len, 1);
}
return 0;
}
Expand Down
11 changes: 8 additions & 3 deletions fs/btrfs/extent_map.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,12 @@ int add_extent_mapping(struct extent_map_tree *tree,
if (prev && prev->end + 1 == em->start &&
((em->block_start == EXTENT_MAP_HOLE &&
prev->block_start == EXTENT_MAP_HOLE) ||
(em->block_start == prev->block_end + 1))) {
(em->block_start == EXTENT_MAP_INLINE &&
prev->block_start == EXTENT_MAP_INLINE) ||
(em->block_start == EXTENT_MAP_DELALLOC &&
prev->block_start == EXTENT_MAP_DELALLOC) ||
(em->block_start < EXTENT_MAP_DELALLOC - 1 &&
em->block_start == prev->block_end + 1))) {
em->start = prev->start;
em->block_start = prev->block_start;
rb_erase(&prev->rb_node, &tree->map);
Expand Down Expand Up @@ -1618,13 +1623,13 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
u64 extent_offset;
u64 last_byte = i_size_read(inode);
u64 block_start;
u64 iosize;
sector_t sector;
struct extent_map *em;
struct block_device *bdev;
int ret;
int nr = 0;
size_t page_offset = 0;
size_t iosize;
size_t blocksize;
loff_t i_size = i_size_read(inode);
unsigned long end_index = i_size >> PAGE_CACHE_SHIFT;
Expand Down Expand Up @@ -1684,7 +1689,7 @@ int extent_write_full_page(struct extent_map_tree *tree, struct page *page,
clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
break;
}
em = get_extent(inode, page, page_offset, cur, end, 0);
em = get_extent(inode, page, page_offset, cur, end, 1);
if (IS_ERR(em) || !em) {
SetPageError(page);
break;
Expand Down
1 change: 0 additions & 1 deletion fs/btrfs/extent_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
* page->private values. Every page that is controlled by the extent
* map has page->private set to one.
*/

#define EXTENT_PAGE_PRIVATE 1
#define EXTENT_PAGE_PRIVATE_FIRST_PAGE 3

Expand Down
2 changes: 1 addition & 1 deletion fs/btrfs/file-item.c
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
new_item_size = blocks * BTRFS_CRC32_SIZE;
if (new_item_size >= btrfs_item_size_nr(leaf, slot))
return 0;
ret = btrfs_truncate_item(trans, root, path, new_item_size);
ret = btrfs_truncate_item(trans, root, path, new_item_size, 1);
BUG_ON(ret);
return ret;
}
Expand Down
36 changes: 30 additions & 6 deletions fs/btrfs/file.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,20 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
goto fail;
}
if (ret == 1) {
struct btrfs_key found_key;

if (path->slots[0] == 0)
goto insert;

path->slots[0]--;
leaf = path->nodes[0];
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);

if (found_key.objectid != inode->i_ino)
goto insert;

if (found_key.type != BTRFS_EXTENT_DATA_KEY)
goto insert;
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);

Expand Down Expand Up @@ -152,6 +164,7 @@ static int insert_inline_extent(struct btrfs_trans_handle *trans,
ret = btrfs_search_slot(trans, root, &key, path,
offset + size - found_end, 1);
BUG_ON(ret != 0);

ret = btrfs_extend_item(trans, root, path,
offset + size - found_end);
if (ret) {
Expand Down Expand Up @@ -292,7 +305,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
*/
inline_size = end_pos;
if (isize >= BTRFS_MAX_INLINE_DATA_SIZE(root) ||
inline_size > 8192 ||
inline_size > 32768 ||
inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
u64 last_end;

Expand All @@ -312,7 +325,7 @@ static int dirty_and_release_pages(struct btrfs_trans_handle *trans,
aligned_end = (pos + write_bytes + root->sectorsize - 1) &
~((u64)root->sectorsize - 1);
err = btrfs_drop_extents(trans, root, inode, start_pos,
aligned_end, end_pos, &hint_byte);
aligned_end, aligned_end, &hint_byte);
if (err)
goto failed;
err = insert_inline_extent(trans, root, inode, start_pos,
Expand Down Expand Up @@ -456,13 +469,15 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
goto next_slot;
}

/* FIXME, there's only one inline extent allowed right now */
if (found_inline) {
u64 mask = root->sectorsize - 1;
search_start = (extent_end + mask) & ~mask;
} else
search_start = extent_end;

if (end <= extent_end && start >= key.offset && found_inline) {
*hint_byte = EXTENT_MAP_INLINE;
}
if (end < extent_end && end >= key.offset) {
if (found_extent) {
u64 disk_bytenr =
Expand All @@ -479,8 +494,10 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
BUG_ON(ret);
}
}
if (!found_inline)
bookend = 1;
bookend = 1;
if (found_inline && start <= key.offset &&
inline_end < extent_end)
keep = 1;
}
/* truncate existing extent */
if (start > key.offset) {
Expand Down Expand Up @@ -510,7 +527,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
new_size = btrfs_file_extent_calc_inline_size(
inline_end - key.offset);
btrfs_truncate_item(trans, root, path,
new_size);
new_size, 1);
}
}
/* delete the entire extent */
Expand Down Expand Up @@ -551,6 +568,13 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,
if (!bookend)
continue;
}
if (bookend && found_inline && start <= key.offset &&
inline_end < extent_end) {
u32 new_size;
new_size = btrfs_file_extent_calc_inline_size(
extent_end - inline_end);
btrfs_truncate_item(trans, root, path, new_size, 0);
}
/* create bookend, splitting the extent in two */
if (bookend && found_extent) {
struct btrfs_key ins;
Expand Down
67 changes: 48 additions & 19 deletions fs/btrfs/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ static int run_delalloc_range(struct inode *inode, u64 start, u64 end)
ret = btrfs_drop_extents(trans, root, inode,
start, start + num_bytes, start, &alloc_hint);

if (alloc_hint == EXTENT_MAP_INLINE)
goto out;

ret = btrfs_alloc_extent(trans, root, inode->i_ino, num_bytes, 0,
alloc_hint, (u64)-1, &ins, 1);
if (ret) {
Expand Down Expand Up @@ -558,6 +561,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
u64 item_end = 0;
int found_extent;
int del_item;
int extent_type = -1;

btrfs_drop_extent_cache(inode, inode->i_size, (u64)-1);
path = btrfs_alloc_path();
Expand Down Expand Up @@ -597,18 +601,23 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
if (found_type == BTRFS_EXTENT_DATA_KEY) {
fi = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_file_extent_item);
if (btrfs_file_extent_type(leaf, fi) !=
BTRFS_FILE_EXTENT_INLINE) {
extent_type = btrfs_file_extent_type(leaf, fi);
if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
item_end +=
btrfs_file_extent_num_bytes(leaf, fi);
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
struct btrfs_item *item = btrfs_item_nr(leaf,
path->slots[0]);
item_end += btrfs_file_extent_inline_len(leaf,
item);
}
}
if (found_type == BTRFS_CSUM_ITEM_KEY) {
ret = btrfs_csum_truncate(trans, root, path,
inode->i_size);
BUG_ON(ret);
}
if (item_end < inode->i_size) {
if (item_end <= inode->i_size) {
if (found_type == BTRFS_DIR_ITEM_KEY) {
found_type = BTRFS_INODE_ITEM_KEY;
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
Expand All @@ -629,9 +638,10 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
found_extent = 0;

/* FIXME, shrink the extent if the ref count is only 1 */
if (found_type == BTRFS_EXTENT_DATA_KEY &&
btrfs_file_extent_type(leaf, fi) !=
BTRFS_FILE_EXTENT_INLINE) {
if (found_type != BTRFS_EXTENT_DATA_KEY)
goto delete;

if (extent_type != BTRFS_FILE_EXTENT_INLINE) {
u64 num_dec;
extent_start = btrfs_file_extent_disk_bytenr(leaf, fi);
if (!del_item) {
Expand Down Expand Up @@ -659,7 +669,15 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
inode->i_blocks -= num_dec;
}
}
} 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);
}
delete:
if (del_item) {
ret = btrfs_del_item(trans, root, path);
if (ret)
Expand Down Expand Up @@ -769,7 +787,7 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
u64 pos = (inode->i_size + mask) & ~mask;
u64 block_end = attr->ia_size | mask;
u64 hole_size;
u64 alloc_hint;
u64 alloc_hint = 0;

if (attr->ia_size <= pos)
goto out;
Expand All @@ -786,8 +804,11 @@ static int btrfs_setattr(struct dentry *dentry, struct iattr *attr)
pos, pos + hole_size, pos,
&alloc_hint);

err = btrfs_insert_file_extent(trans, root, inode->i_ino,
pos, 0, 0, hole_size);
if (alloc_hint != EXTENT_MAP_INLINE) {
err = btrfs_insert_file_extent(trans, root,
inode->i_ino,
pos, 0, 0, hole_size);
}
btrfs_end_transaction(trans, root);
mutex_unlock(&root->fs_info->fs_mutex);
unlock_extent(em_tree, pos, block_end, GFP_NOFS);
Expand Down Expand Up @@ -1531,8 +1552,8 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
em->end = EXTENT_MAP_HOLE;
}
em->bdev = inode->i_sb->s_bdev;
ret = btrfs_lookup_file_extent(NULL, root, path,
objectid, start, 0);
ret = btrfs_lookup_file_extent(trans, root, path,
objectid, start, trans != NULL);
if (ret < 0) {
err = ret;
goto out;
Expand Down Expand Up @@ -1627,15 +1648,23 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
((u64)root->sectorsize -1);
map = kmap(page);
ptr = btrfs_file_extent_inline_start(item) + extent_offset;
read_extent_buffer(leaf, map + page_offset, ptr, copy_size);

if (em->start + copy_size <= em->end) {
size = min_t(u64, em->end + 1 - em->start,
PAGE_CACHE_SIZE - page_offset) - copy_size;
memset(map + page_offset + copy_size, 0, size);
if (create == 0 && !PageUptodate(page)) {
read_extent_buffer(leaf, map + page_offset, ptr,
copy_size);
flush_dcache_page(page);
} else if (create && PageUptodate(page)) {
if (!trans) {
kunmap(page);
free_extent_map(em);
em = NULL;
btrfs_release_path(root, path);
trans = btrfs_start_transaction(root, 1);
goto again;
}
write_extent_buffer(leaf, map + page_offset, ptr,
copy_size);
btrfs_mark_buffer_dirty(leaf);
}

flush_dcache_page(page);
kunmap(page);
set_extent_uptodate(em_tree, em->start, em->end, GFP_NOFS);
goto insert;
Expand Down

0 comments on commit 179e29e

Please sign in to comment.