goto out;
BUG_ON(num_bytes > btrfs_super_total_bytes(&root->fs_info->super_copy));
+ mutex_lock(&BTRFS_I(inode)->extent_mutex);
btrfs_drop_extent_cache(inode, start, start + num_bytes - 1);
+ mutex_unlock(&BTRFS_I(inode)->extent_mutex);
while(num_bytes > 0) {
cur_alloc_size = min(num_bytes, root->fs_info->max_extent);
em->len = ins.offset;
em->block_start = ins.objectid;
em->bdev = root->fs_info->fs_devices->latest_bdev;
+ mutex_lock(&BTRFS_I(inode)->extent_mutex);
+ set_bit(EXTENT_FLAG_PINNED, &em->flags);
while(1) {
spin_lock(&em_tree->lock);
ret = add_extent_mapping(em_tree, em);
btrfs_drop_extent_cache(inode, start,
start + ins.offset - 1);
}
+ mutex_unlock(&BTRFS_I(inode)->extent_mutex);
cur_alloc_size = ins.offset;
ret = btrfs_add_ordered_extent(inode, start, ins.objectid,
{
struct btrfs_root *root = BTRFS_I(inode)->root;
int ret = 0;
- struct btrfs_ordered_sum *sums;
- ret = btrfs_csum_one_bio(root, bio, &sums);
- BUG_ON(ret);
-
- ret = btrfs_add_ordered_sum(inode, sums);
+ ret = btrfs_csum_one_bio(root, inode, bio);
BUG_ON(ret);
return btrfs_map_bio(root, rw, bio, mirror_num, 1);
return btrfs_map_bio(root, rw, bio, mirror_num, 0);
}
-static int add_pending_csums(struct btrfs_trans_handle *trans,
+static noinline int add_pending_csums(struct btrfs_trans_handle *trans,
struct inode *inode, u64 file_offset,
struct list_head *list)
{
struct btrfs_ordered_sum *sum;
btrfs_set_trans_block_group(trans, inode);
- while(!list_empty(list)) {
- cur = list->next;
+ list_for_each(cur, list) {
sum = list_entry(cur, struct btrfs_ordered_sum, list);
mutex_lock(&BTRFS_I(inode)->csum_mutex);
btrfs_csum_file_blocks(trans, BTRFS_I(inode)->root,
inode, sum);
mutex_unlock(&BTRFS_I(inode)->csum_mutex);
- list_del(&sum->list);
- kfree(sum);
}
return 0;
}
fixup = container_of(work, struct btrfs_writepage_fixup, work);
page = fixup->page;
-
+again:
lock_page(page);
if (!page->mapping || !PageDirty(page) || !PageChecked(page)) {
ClearPageChecked(page);
page_end = page_offset(page) + PAGE_CACHE_SIZE - 1;
lock_extent(&BTRFS_I(inode)->io_tree, page_start, page_end, GFP_NOFS);
- ordered = btrfs_lookup_ordered_extent(inode, page_start);
- if (ordered)
+
+ /* already ordered? We're done */
+ if (test_range_bit(&BTRFS_I(inode)->io_tree, page_start, page_end,
+ EXTENT_ORDERED, 0)) {
goto out;
+ }
+
+ ordered = btrfs_lookup_ordered_extent(inode, page_start);
+ if (ordered) {
+ unlock_extent(&BTRFS_I(inode)->io_tree, page_start,
+ page_end, GFP_NOFS);
+ unlock_page(page);
+ btrfs_start_ordered_extent(inode, ordered, 1);
+ goto again;
+ }
set_extent_delalloc(&BTRFS_I(inode)->io_tree, page_start, page_end,
GFP_NOFS);
return -EAGAIN;
}
-int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
- struct extent_state *state, int uptodate)
+static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
{
- struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct btrfs_trans_handle *trans;
struct btrfs_ordered_extent *ordered_extent;
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
+ struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
+ struct extent_map *em;
u64 alloc_hint = 0;
+ u64 clear_start;
+ u64 clear_end;
struct list_head list;
struct btrfs_key ins;
int ret;
ret = btrfs_dec_test_ordered_pending(inode, start, end - start + 1);
- if (!ret) {
+ if (!ret)
return 0;
- }
trans = btrfs_join_transaction(root, 1);
ins.objectid = ordered_extent->start;
ins.offset = ordered_extent->len;
ins.type = BTRFS_EXTENT_ITEM_KEY;
+
ret = btrfs_alloc_reserved_extent(trans, root, root->root_key.objectid,
trans->transid, inode->i_ino,
ordered_extent->file_offset, &ins);
BUG_ON(ret);
+
+ mutex_lock(&BTRFS_I(inode)->extent_mutex);
+
ret = btrfs_drop_extents(trans, root, inode,
ordered_extent->file_offset,
ordered_extent->file_offset +
ordered_extent->len,
ordered_extent->len, 0);
BUG_ON(ret);
+
+ spin_lock(&em_tree->lock);
+ clear_start = ordered_extent->file_offset;
+ clear_end = ordered_extent->file_offset + ordered_extent->len;
+ while(clear_start < clear_end) {
+ em = lookup_extent_mapping(em_tree, clear_start,
+ clear_end - clear_start);
+ if (em) {
+ clear_bit(EXTENT_FLAG_PINNED, &em->flags);
+ clear_start = em->start + em->len;
+ free_extent_map(em);
+ } else {
+ break;
+ }
+ }
+ spin_unlock(&em_tree->lock);
+
btrfs_drop_extent_cache(inode, ordered_extent->file_offset,
ordered_extent->file_offset +
ordered_extent->len - 1);
+ mutex_unlock(&BTRFS_I(inode)->extent_mutex);
+
inode->i_blocks += ordered_extent->len >> 9;
unlock_extent(io_tree, ordered_extent->file_offset,
ordered_extent->file_offset + ordered_extent->len - 1,
btrfs_ordered_update_i_size(inode, ordered_extent);
btrfs_remove_ordered_extent(inode, ordered_extent);
+
/* once for us */
btrfs_put_ordered_extent(ordered_extent);
/* once for the tree */
return 0;
}
+int btrfs_writepage_end_io_hook(struct page *page, u64 start, u64 end,
+ struct extent_state *state, int uptodate)
+{
+ return btrfs_finish_ordered_io(page->mapping->host, start, end);
+}
+
int btrfs_readpage_io_hook(struct page *page, u64 start, u64 end)
{
int ret = 0;
path = btrfs_alloc_path();
item = btrfs_lookup_csum(NULL, root, path, inode->i_ino, start, 0);
if (IS_ERR(item)) {
+ /*
+ * It is possible there is an ordered extent that has
+ * not yet finished for this range in the file. If so,
+ * that extent will have a csum cached, and it will insert
+ * the sum after all the blocks in the extent are fully
+ * on disk. So, look for an ordered extent and use the
+ * sum if found.
+ */
+ ret = btrfs_find_ordered_sum(inode, start, &csum);
+ if (ret == 0)
+ goto found;
+
ret = PTR_ERR(item);
/* a csum that isn't present is a preallocated region. */
if (ret == -ENOENT || ret == -EFBIG)
}
read_extent_buffer(path->nodes[0], &csum, (unsigned long)item,
BTRFS_CRC32_SIZE);
+found:
set_state_private(io_tree, start, csum);
out:
if (path)
BTRFS_I(inode)->block_group->key.objectid);
}
-int btrfs_update_inode(struct btrfs_trans_handle *trans,
+int noinline btrfs_update_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct inode *inode)
{
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
unlock_page(page);
page_cache_release(page);
- btrfs_wait_ordered_extent(inode, ordered);
+ btrfs_start_ordered_extent(inode, ordered, 1);
btrfs_put_ordered_extent(ordered);
goto again;
}
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);
(u64)-1);
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 (err)
unsigned long nr;
int ret;
- btrfs_wait_ordered_range(inode, 0, (u64)-1);
truncate_inode_pages(&inode->i_data, 0);
if (is_bad_inode(inode)) {
goto no_delete;
}
+ btrfs_wait_ordered_range(inode, 0, (u64)-1);
btrfs_i_size_write(inode, 0);
trans = btrfs_start_transaction(root, 1);
inode->i_mapping, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
inode->i_mapping, GFP_NOFS);
+ btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
mutex_init(&BTRFS_I(inode)->csum_mutex);
+ mutex_init(&BTRFS_I(inode)->extent_mutex);
return 0;
}
inode->i_mapping, GFP_NOFS);
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
inode->i_mapping, GFP_NOFS);
+ btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
mutex_init(&BTRFS_I(inode)->csum_mutex);
+ mutex_init(&BTRFS_I(inode)->extent_mutex);
BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->disk_i_size = 0;
BTRFS_I(inode)->root = root;
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
inode->i_mapping, GFP_NOFS);
mutex_init(&BTRFS_I(inode)->csum_mutex);
+ mutex_init(&BTRFS_I(inode)->extent_mutex);
BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->disk_i_size = 0;
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+ btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
}
dir->i_sb->s_dirt = 1;
btrfs_update_inode_block_group(trans, inode);
map = &BTRFS_I(page->mapping->host)->extent_tree;
ret = try_release_extent_mapping(map, tree, page, gfp_flags);
if (ret == 1) {
- invalidate_extent_lru(tree, page_offset(page), PAGE_CACHE_SIZE);
ClearPagePrivate(page);
set_page_private(page, 0);
page_cache_release(page);
static int btrfs_releasepage(struct page *page, gfp_t gfp_flags)
{
- struct btrfs_ordered_extent *ordered;
-
- ordered = btrfs_lookup_ordered_extent(page->mapping->host,
- page_offset(page));
- if (ordered) {
- btrfs_put_ordered_extent(ordered);
- return 0;
- }
return __btrfs_releasepage(page, gfp_flags);
}
ordered = btrfs_lookup_ordered_extent(page->mapping->host,
page_offset(page));
if (ordered) {
+ /*
+ * IO on this page will never be started, so we need
+ * to account for any ordered extents now
+ */
clear_extent_bit(tree, page_start, page_end,
EXTENT_DIRTY | EXTENT_DELALLOC |
EXTENT_LOCKED, 1, 0, GFP_NOFS);
- btrfs_writepage_end_io_hook(page, page_start,
- page_end, NULL, 1);
+ btrfs_finish_ordered_io(page->mapping->host,
+ page_start, page_end);
btrfs_put_ordered_extent(ordered);
lock_extent(tree, page_start, page_end, GFP_NOFS);
}
1, 1, GFP_NOFS);
__btrfs_releasepage(page, GFP_NOFS);
+ ClearPageChecked(page);
if (PagePrivate(page)) {
- invalidate_extent_lru(tree, page_offset(page),
- PAGE_CACHE_SIZE);
ClearPagePrivate(page);
set_page_private(page, 0);
page_cache_release(page);
lock_extent(io_tree, page_start, page_end, GFP_NOFS);
set_page_extent_mapped(page);
+ /*
+ * we can't set the delalloc bits if there are pending ordered
+ * extents. Drop our locks and wait for them to finish
+ */
ordered = btrfs_lookup_ordered_extent(inode, page_start);
if (ordered) {
unlock_extent(io_tree, page_start, page_end, GFP_NOFS);
unlock_page(page);
- btrfs_wait_ordered_extent(inode, ordered);
+ btrfs_start_ordered_extent(inode, ordered, 1);
btrfs_put_ordered_extent(ordered);
goto again;
}
return;
btrfs_truncate_page(inode->i_mapping, inode->i_size);
+ btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
trans = btrfs_start_transaction(root, 1);
btrfs_set_trans_block_group(trans, inode);
- btrfs_wait_ordered_range(inode, inode->i_size & (~mask), (u64)-1);
btrfs_i_size_write(inode, inode->i_size);
/* FIXME, add redo link to tree so we don't leak on crash */
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
inode->i_mapping, GFP_NOFS);
mutex_init(&BTRFS_I(inode)->csum_mutex);
+ mutex_init(&BTRFS_I(inode)->extent_mutex);
BTRFS_I(inode)->delalloc_bytes = 0;
BTRFS_I(inode)->disk_i_size = 0;
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
+ btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
}
dir->i_sb->s_dirt = 1;
btrfs_update_inode_block_group(trans, inode);