Commit e6dcd2dc authored by Chris Mason's avatar Chris Mason

Btrfs: New data=ordered implementation

The old data=ordered code would force commit to wait until
all the data extents from the transaction were fully on disk.  This
introduced large latencies into the commit and stalled new writers
in the transaction for a long time.

The new code changes the way data allocations and extents work:

* When delayed allocation is filled, data extents are reserved, and
  the extent bit EXTENT_ORDERED is set on the entire range of the extent.
  A struct btrfs_ordered_extent is allocated an inserted into a per-inode
  rbtree to track the pending extents.

* As each page is written EXTENT_ORDERED is cleared on the bytes corresponding
  to that page.

* When all of the bytes corresponding to a single struct btrfs_ordered_extent
  are written, The previously reserved extent is inserted into the FS
  btree and into the extent allocation trees.  The checksums for the file
  data are also updated.
Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent 77a41afb
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "extent_map.h" #include "extent_map.h"
#include "extent_io.h" #include "extent_io.h"
#include "ordered-data.h"
/* in memory btrfs inode */ /* in memory btrfs inode */
struct btrfs_inode { struct btrfs_inode {
...@@ -32,9 +33,8 @@ struct btrfs_inode { ...@@ -32,9 +33,8 @@ struct btrfs_inode {
struct extent_io_tree io_failure_tree; struct extent_io_tree io_failure_tree;
struct mutex csum_mutex; struct mutex csum_mutex;
struct inode vfs_inode; struct inode vfs_inode;
atomic_t ordered_writeback; struct btrfs_ordered_inode_tree ordered_tree;
u64 ordered_trans;
/* /*
* transid of the trans_handle that last modified this inode * transid of the trans_handle that last modified this inode
*/ */
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/backing-dev.h> #include <linux/backing-dev.h>
#include <linux/wait.h>
#include <asm/kmap_types.h> #include <asm/kmap_types.h>
#include "bit-radix.h" #include "bit-radix.h"
#include "extent_io.h" #include "extent_io.h"
...@@ -37,6 +38,7 @@ extern struct kmem_cache *btrfs_trans_handle_cachep; ...@@ -37,6 +38,7 @@ extern struct kmem_cache *btrfs_trans_handle_cachep;
extern struct kmem_cache *btrfs_transaction_cachep; extern struct kmem_cache *btrfs_transaction_cachep;
extern struct kmem_cache *btrfs_bit_radix_cachep; extern struct kmem_cache *btrfs_bit_radix_cachep;
extern struct kmem_cache *btrfs_path_cachep; extern struct kmem_cache *btrfs_path_cachep;
struct btrfs_ordered_sum;
#define BTRFS_MAGIC "_B5RfS_M" #define BTRFS_MAGIC "_B5RfS_M"
...@@ -510,6 +512,7 @@ struct btrfs_fs_info { ...@@ -510,6 +512,7 @@ struct btrfs_fs_info {
u64 max_inline; u64 max_inline;
u64 alloc_start; u64 alloc_start;
struct btrfs_transaction *running_transaction; struct btrfs_transaction *running_transaction;
wait_queue_head_t transaction_throttle;
struct btrfs_super_block super_copy; struct btrfs_super_block super_copy;
struct btrfs_super_block super_for_commit; struct btrfs_super_block super_for_commit;
struct block_device *__bdev; struct block_device *__bdev;
...@@ -541,6 +544,7 @@ struct btrfs_fs_info { ...@@ -541,6 +544,7 @@ struct btrfs_fs_info {
*/ */
struct btrfs_workers workers; struct btrfs_workers workers;
struct btrfs_workers endio_workers; struct btrfs_workers endio_workers;
struct btrfs_workers endio_write_workers;
struct btrfs_workers submit_workers; struct btrfs_workers submit_workers;
struct task_struct *transaction_kthread; struct task_struct *transaction_kthread;
struct task_struct *cleaner_kthread; struct task_struct *cleaner_kthread;
...@@ -1384,6 +1388,17 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1384,6 +1388,17 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
u64 owner, u64 owner_offset, u64 owner, u64 owner_offset,
u64 empty_size, u64 hint_byte, u64 empty_size, u64 hint_byte,
u64 search_end, struct btrfs_key *ins, u64 data); u64 search_end, struct btrfs_key *ins, u64 data);
int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
struct btrfs_key *ins);
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size,
u64 empty_size, u64 hint_byte,
u64 search_end, struct btrfs_key *ins,
u64 data);
int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root,
struct extent_buffer *buf); struct extent_buffer *buf);
int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_root
...@@ -1556,9 +1571,9 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, ...@@ -1556,9 +1571,9 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
u64 bytenr, int mod); u64 bytenr, int mod);
int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode, struct btrfs_root *root, struct inode *inode,
struct bio *bio, char *sums); struct btrfs_ordered_sum *sums);
int btrfs_csum_one_bio(struct btrfs_root *root, int btrfs_csum_one_bio(struct btrfs_root *root,
struct bio *bio, char **sums_ret); struct bio *bio, struct btrfs_ordered_sum **sums_ret);
struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans, struct btrfs_csum_item *btrfs_lookup_csum(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct btrfs_path *path, struct btrfs_path *path,
......
...@@ -407,7 +407,11 @@ static int end_workqueue_bio(struct bio *bio, ...@@ -407,7 +407,11 @@ static int end_workqueue_bio(struct bio *bio,
end_io_wq->error = err; end_io_wq->error = err;
end_io_wq->work.func = end_workqueue_fn; end_io_wq->work.func = end_workqueue_fn;
end_io_wq->work.flags = 0; end_io_wq->work.flags = 0;
btrfs_queue_worker(&fs_info->endio_workers, &end_io_wq->work); if (bio->bi_rw & (1 << BIO_RW))
btrfs_queue_worker(&fs_info->endio_write_workers,
&end_io_wq->work);
else
btrfs_queue_worker(&fs_info->endio_workers, &end_io_wq->work);
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23) #if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,23)
return 0; return 0;
...@@ -1286,6 +1290,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1286,6 +1290,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
mutex_init(&fs_info->transaction_kthread_mutex); mutex_init(&fs_info->transaction_kthread_mutex);
mutex_init(&fs_info->cleaner_mutex); mutex_init(&fs_info->cleaner_mutex);
mutex_init(&fs_info->volume_mutex); mutex_init(&fs_info->volume_mutex);
init_waitqueue_head(&fs_info->transaction_throttle);
#if 0 #if 0
ret = add_hasher(fs_info, "crc32c"); ret = add_hasher(fs_info, "crc32c");
...@@ -1325,9 +1330,13 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1325,9 +1330,13 @@ struct btrfs_root *open_ctree(struct super_block *sb,
btrfs_init_workers(&fs_info->workers, fs_info->thread_pool_size); btrfs_init_workers(&fs_info->workers, fs_info->thread_pool_size);
btrfs_init_workers(&fs_info->submit_workers, fs_info->thread_pool_size); btrfs_init_workers(&fs_info->submit_workers, fs_info->thread_pool_size);
btrfs_init_workers(&fs_info->endio_workers, fs_info->thread_pool_size); btrfs_init_workers(&fs_info->endio_workers, fs_info->thread_pool_size);
btrfs_init_workers(&fs_info->endio_write_workers,
fs_info->thread_pool_size);
btrfs_start_workers(&fs_info->workers, 1); btrfs_start_workers(&fs_info->workers, 1);
btrfs_start_workers(&fs_info->submit_workers, 1); btrfs_start_workers(&fs_info->submit_workers, 1);
btrfs_start_workers(&fs_info->endio_workers, fs_info->thread_pool_size); btrfs_start_workers(&fs_info->endio_workers, fs_info->thread_pool_size);
btrfs_start_workers(&fs_info->endio_write_workers,
fs_info->thread_pool_size);
err = -EINVAL; err = -EINVAL;
if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) { if (btrfs_super_num_devices(disk_super) > fs_devices->open_devices) {
...@@ -1447,6 +1456,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1447,6 +1456,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
extent_io_tree_empty_lru(&BTRFS_I(fs_info->btree_inode)->io_tree); extent_io_tree_empty_lru(&BTRFS_I(fs_info->btree_inode)->io_tree);
btrfs_stop_workers(&fs_info->workers); btrfs_stop_workers(&fs_info->workers);
btrfs_stop_workers(&fs_info->endio_workers); btrfs_stop_workers(&fs_info->endio_workers);
btrfs_stop_workers(&fs_info->endio_write_workers);
btrfs_stop_workers(&fs_info->submit_workers); btrfs_stop_workers(&fs_info->submit_workers);
fail_iput: fail_iput:
iput(fs_info->btree_inode); iput(fs_info->btree_inode);
...@@ -1702,6 +1712,7 @@ int close_ctree(struct btrfs_root *root) ...@@ -1702,6 +1712,7 @@ int close_ctree(struct btrfs_root *root)
btrfs_stop_workers(&fs_info->workers); btrfs_stop_workers(&fs_info->workers);
btrfs_stop_workers(&fs_info->endio_workers); btrfs_stop_workers(&fs_info->endio_workers);
btrfs_stop_workers(&fs_info->endio_write_workers);
btrfs_stop_workers(&fs_info->submit_workers); btrfs_stop_workers(&fs_info->submit_workers);
iput(fs_info->btree_inode); iput(fs_info->btree_inode);
......
...@@ -1895,36 +1895,17 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans, ...@@ -1895,36 +1895,17 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
return ret; return ret;
} }
/* static int __btrfs_reserve_extent(struct btrfs_trans_handle *trans,
* finds a free extent and does all the dirty work required for allocation struct btrfs_root *root,
* returns the key for the extent through ins, and a tree buffer for u64 num_bytes, u64 min_alloc_size,
* the first block of the extent through buf. u64 empty_size, u64 hint_byte,
* u64 search_end, struct btrfs_key *ins,
* returns 0 if everything worked, non-zero otherwise. u64 data)
*/
int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
u64 empty_size, u64 hint_byte,
u64 search_end, struct btrfs_key *ins, u64 data)
{ {
int ret; int ret;
int pending_ret;
u64 super_used;
u64 root_used;
u64 search_start = 0; u64 search_start = 0;
u64 alloc_profile; u64 alloc_profile;
u32 sizes[2];
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct btrfs_extent_item *extent_item;
struct btrfs_extent_ref *ref;
struct btrfs_path *path;
struct btrfs_key keys[2];
maybe_lock_mutex(root);
if (data) { if (data) {
alloc_profile = info->avail_data_alloc_bits & alloc_profile = info->avail_data_alloc_bits &
...@@ -1974,11 +1955,48 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1974,11 +1955,48 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
} }
if (ret) { if (ret) {
printk("allocation failed flags %Lu\n", data); printk("allocation failed flags %Lu\n", data);
}
if (ret) {
BUG(); BUG();
goto out;
} }
clear_extent_dirty(&root->fs_info->free_space_cache,
ins->objectid, ins->objectid + ins->offset - 1,
GFP_NOFS);
return 0;
}
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size,
u64 empty_size, u64 hint_byte,
u64 search_end, struct btrfs_key *ins,
u64 data)
{
int ret;
maybe_lock_mutex(root);
ret = __btrfs_reserve_extent(trans, root, num_bytes, min_alloc_size,
empty_size, hint_byte, search_end, ins,
data);
maybe_unlock_mutex(root);
return ret;
}
static int __btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
struct btrfs_key *ins)
{
int ret;
int pending_ret;
u64 super_used;
u64 root_used;
u64 num_bytes = ins->offset;
u32 sizes[2];
struct btrfs_fs_info *info = root->fs_info;
struct btrfs_root *extent_root = info->extent_root;
struct btrfs_extent_item *extent_item;
struct btrfs_extent_ref *ref;
struct btrfs_path *path;
struct btrfs_key keys[2];
/* block accounting for super block */ /* block accounting for super block */
spin_lock_irq(&info->delalloc_lock); spin_lock_irq(&info->delalloc_lock);
...@@ -1990,10 +2008,6 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -1990,10 +2008,6 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
root_used = btrfs_root_used(&root->root_item); root_used = btrfs_root_used(&root->root_item);
btrfs_set_root_used(&root->root_item, root_used + num_bytes); btrfs_set_root_used(&root->root_item, root_used + num_bytes);
clear_extent_dirty(&root->fs_info->free_space_cache,
ins->objectid, ins->objectid + ins->offset - 1,
GFP_NOFS);
if (root == extent_root) { if (root == extent_root) {
set_extent_bits(&root->fs_info->extent_ins, ins->objectid, set_extent_bits(&root->fs_info->extent_ins, ins->objectid,
ins->objectid + ins->offset - 1, ins->objectid + ins->offset - 1,
...@@ -2001,10 +2015,6 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -2001,10 +2015,6 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
goto update_block; goto update_block;
} }
WARN_ON(trans->alloc_exclude_nr);
trans->alloc_exclude_start = ins->objectid;
trans->alloc_exclude_nr = ins->offset;
memcpy(&keys[0], ins, sizeof(*ins)); memcpy(&keys[0], ins, sizeof(*ins));
keys[1].offset = hash_extent_ref(root_objectid, ref_generation, keys[1].offset = hash_extent_ref(root_objectid, ref_generation,
owner, owner_offset); owner, owner_offset);
...@@ -2054,6 +2064,51 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans, ...@@ -2054,6 +2064,51 @@ int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
BUG(); BUG();
} }
out: out:
return ret;
}
int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
struct btrfs_key *ins)
{
int ret;
maybe_lock_mutex(root);
ret = __btrfs_alloc_reserved_extent(trans, root, root_objectid,
ref_generation, owner,
owner_offset, ins);
maybe_unlock_mutex(root);
return ret;
}
/*
* finds a free extent and does all the dirty work required for allocation
* returns the key for the extent through ins, and a tree buffer for
* the first block of the extent through buf.
*
* returns 0 if everything worked, non-zero otherwise.
*/
int btrfs_alloc_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
u64 empty_size, u64 hint_byte,
u64 search_end, struct btrfs_key *ins, u64 data)
{
int ret;
maybe_lock_mutex(root);
ret = __btrfs_reserve_extent(trans, root, num_bytes,
min_alloc_size, empty_size, hint_byte,
search_end, ins, data);
BUG_ON(ret);
ret = __btrfs_alloc_reserved_extent(trans, root, root_objectid,
ref_generation, owner,
owner_offset, ins);
BUG_ON(ret);
maybe_unlock_mutex(root); maybe_unlock_mutex(root);
return ret; return ret;
} }
...@@ -2288,8 +2343,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans, ...@@ -2288,8 +2343,8 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
mutex_lock(&root->fs_info->alloc_mutex); mutex_lock(&root->fs_info->alloc_mutex);
/* we've dropped the lock, double check */ /* we've dropped the lock, double check */
ret = drop_snap_lookup_refcount(root, bytenr, ret = lookup_extent_ref(NULL, root, bytenr, blocksize,
blocksize, &refs); &refs);
BUG_ON(ret); BUG_ON(ret);
if (refs != 1) { if (refs != 1) {
parent = path->nodes[*level]; parent = path->nodes[*level];
...@@ -2584,7 +2639,6 @@ static int noinline relocate_inode_pages(struct inode *inode, u64 start, ...@@ -2584,7 +2639,6 @@ static int noinline relocate_inode_pages(struct inode *inode, u64 start,
kfree(ra); kfree(ra);
trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1); trans = btrfs_start_transaction(BTRFS_I(inode)->root, 1);
if (trans) { if (trans) {
btrfs_add_ordered_inode(inode);
btrfs_end_transaction(trans, BTRFS_I(inode)->root); btrfs_end_transaction(trans, BTRFS_I(inode)->root);
mark_inode_dirty(inode); mark_inode_dirty(inode);
} }
......
...@@ -793,6 +793,13 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -793,6 +793,13 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
} }
EXPORT_SYMBOL(set_extent_dirty); EXPORT_SYMBOL(set_extent_dirty);
int set_extent_ordered(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask)
{
return set_extent_bit(tree, start, end, EXTENT_ORDERED, 0, NULL, mask);
}
EXPORT_SYMBOL(set_extent_ordered);
int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
int bits, gfp_t mask) int bits, gfp_t mask)
{ {
...@@ -812,8 +819,8 @@ int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -812,8 +819,8 @@ int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask) gfp_t mask)
{ {
return set_extent_bit(tree, start, end, return set_extent_bit(tree, start, end,
EXTENT_DELALLOC | EXTENT_DIRTY, 0, NULL, EXTENT_DELALLOC | EXTENT_DIRTY,
mask); 0, NULL, mask);
} }
EXPORT_SYMBOL(set_extent_delalloc); EXPORT_SYMBOL(set_extent_delalloc);
...@@ -825,6 +832,13 @@ int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -825,6 +832,13 @@ int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
} }
EXPORT_SYMBOL(clear_extent_dirty); EXPORT_SYMBOL(clear_extent_dirty);
int clear_extent_ordered(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask)
{
return clear_extent_bit(tree, start, end, EXTENT_ORDERED, 1, 0, mask);
}
EXPORT_SYMBOL(clear_extent_ordered);
int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_new(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask) gfp_t mask)
{ {
...@@ -1395,10 +1409,9 @@ static int end_bio_extent_writepage(struct bio *bio, ...@@ -1395,10 +1409,9 @@ static int end_bio_extent_writepage(struct bio *bio,
if (--bvec >= bio->bi_io_vec) if (--bvec >= bio->bi_io_vec)
prefetchw(&bvec->bv_page->flags); prefetchw(&bvec->bv_page->flags);
if (tree->ops && tree->ops->writepage_end_io_hook) { if (tree->ops && tree->ops->writepage_end_io_hook) {
ret = tree->ops->writepage_end_io_hook(page, start, ret = tree->ops->writepage_end_io_hook(page, start,
end, state); end, state, uptodate);
if (ret) if (ret)
uptodate = 0; uptodate = 0;
} }
...@@ -1868,9 +1881,14 @@ static int __extent_read_full_page(struct extent_io_tree *tree, ...@@ -1868,9 +1881,14 @@ static int __extent_read_full_page(struct extent_io_tree *tree,
unlock_extent(tree, cur, end, GFP_NOFS); unlock_extent(tree, cur, end, GFP_NOFS);
break; break;
} }
extent_offset = cur - em->start; extent_offset = cur - em->start;
if (extent_map_end(em) <= cur) {
printk("bad mapping em [%Lu %Lu] cur %Lu\n", em->start, extent_map_end(em), cur);
}
BUG_ON(extent_map_end(em) <= cur); BUG_ON(extent_map_end(em) <= cur);
if (end < cur) {
printk("2bad mapping end %Lu cur %Lu\n", end, cur);
}
BUG_ON(end < cur); BUG_ON(end < cur);
iosize = min(extent_map_end(em) - cur, end - cur + 1); iosize = min(extent_map_end(em) - cur, end - cur + 1);
...@@ -1976,6 +1994,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -1976,6 +1994,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
u64 last_byte = i_size_read(inode); u64 last_byte = i_size_read(inode);
u64 block_start; u64 block_start;
u64 iosize; u64 iosize;
u64 unlock_start;
sector_t sector; sector_t sector;
struct extent_map *em; struct extent_map *em;
struct block_device *bdev; struct block_device *bdev;
...@@ -1988,7 +2007,6 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -1988,7 +2007,6 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
u64 nr_delalloc; u64 nr_delalloc;
u64 delalloc_end; u64 delalloc_end;
WARN_ON(!PageLocked(page)); WARN_ON(!PageLocked(page));
page_offset = i_size & (PAGE_CACHE_SIZE - 1); page_offset = i_size & (PAGE_CACHE_SIZE - 1);
if (page->index > end_index || if (page->index > end_index ||
...@@ -2030,6 +2048,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2030,6 +2048,7 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
delalloc_start = delalloc_end + 1; delalloc_start = delalloc_end + 1;
} }
lock_extent(tree, start, page_end, GFP_NOFS); lock_extent(tree, start, page_end, GFP_NOFS);
unlock_start = start;
end = page_end; end = page_end;
if (test_range_bit(tree, start, page_end, EXTENT_DELALLOC, 0)) { if (test_range_bit(tree, start, page_end, EXTENT_DELALLOC, 0)) {
...@@ -2038,6 +2057,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2038,6 +2057,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
if (last_byte <= start) { if (last_byte <= start) {
clear_extent_dirty(tree, start, page_end, GFP_NOFS); clear_extent_dirty(tree, start, page_end, GFP_NOFS);
unlock_extent(tree, start, page_end, GFP_NOFS);
if (tree->ops && tree->ops->writepage_end_io_hook)
tree->ops->writepage_end_io_hook(page, start,
page_end, NULL, 1);
unlock_start = page_end + 1;
goto done; goto done;
} }
...@@ -2047,6 +2071,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2047,6 +2071,11 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
while (cur <= end) { while (cur <= end) {
if (cur >= last_byte) { if (cur >= last_byte) {
clear_extent_dirty(tree, cur, page_end, GFP_NOFS); clear_extent_dirty(tree, cur, page_end, GFP_NOFS);
unlock_extent(tree, unlock_start, page_end, GFP_NOFS);
if (tree->ops && tree->ops->writepage_end_io_hook)
tree->ops->writepage_end_io_hook(page, cur,
page_end, NULL, 1);
unlock_start = page_end + 1;
break; break;
} }
em = epd->get_extent(inode, page, page_offset, cur, em = epd->get_extent(inode, page, page_offset, cur,
...@@ -2071,8 +2100,16 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2071,8 +2100,16 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
block_start == EXTENT_MAP_INLINE) { block_start == EXTENT_MAP_INLINE) {
clear_extent_dirty(tree, cur, clear_extent_dirty(tree, cur,
cur + iosize - 1, GFP_NOFS); cur + iosize - 1, GFP_NOFS);
unlock_extent(tree, unlock_start, cur + iosize -1,
GFP_NOFS);
if (tree->ops && tree->ops->writepage_end_io_hook)
tree->ops->writepage_end_io_hook(page, cur,
cur + iosize - 1,
NULL, 1);
cur = cur + iosize; cur = cur + iosize;
page_offset += iosize; page_offset += iosize;
unlock_start = cur;
continue; continue;
} }
...@@ -2119,7 +2156,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc, ...@@ -2119,7 +2156,8 @@ static int __extent_writepage(struct page *page, struct writeback_control *wbc,
set_page_writeback(page); set_page_writeback(page);
end_page_writeback(page); end_page_writeback(page);
} }
unlock_extent(tree, start, page_end, GFP_NOFS); if (unlock_start <= page_end)
unlock_extent(tree, unlock_start, page_end, GFP_NOFS);
unlock_page(page); unlock_page(page);
return 0; return 0;
} }
......
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
#define EXTENT_DEFRAG (1 << 6) #define EXTENT_DEFRAG (1 << 6)
#define EXTENT_DEFRAG_DONE (1 << 7) #define EXTENT_DEFRAG_DONE (1 << 7)
#define EXTENT_BUFFER_FILLED (1 << 8) #define EXTENT_BUFFER_FILLED (1 << 8)
#define EXTENT_ORDERED (1 << 9)
#define EXTENT_ORDERED_METADATA (1 << 10)
#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK) #define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)
/* /*
...@@ -42,7 +44,7 @@ struct extent_io_ops { ...@@ -42,7 +44,7 @@ struct extent_io_ops {
int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end, int (*readpage_end_io_hook)(struct page *page, u64 start, u64 end,
struct extent_state *state); struct extent_state *state);
int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end, int (*writepage_end_io_hook)(struct page *page, u64 start, u64 end,
struct extent_state *state); struct extent_state *state, int uptodate);
int (*set_bit_hook)(struct inode *inode, u64 start, u64 end, int (*set_bit_hook)(struct inode *inode, u64 start, u64 end,
unsigned long old, unsigned long bits); unsigned long old, unsigned long bits);
int (*clear_bit_hook)(struct inode *inode, u64 start, u64 end, int (*clear_bit_hook)(struct inode *inode, u64 start, u64 end,
...@@ -131,6 +133,8 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -131,6 +133,8 @@ int test_range_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bits, int filled); int bits, int filled);
int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int clear_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
int bits, gfp_t mask); int bits, gfp_t mask);
int clear_extent_bit(struct extent_io_tree *tree, u64 start, u64 end,
int bits, int wake, int delete, gfp_t mask);
int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_bits(struct extent_io_tree *tree, u64 start, u64 end,
int bits, gfp_t mask); int bits, gfp_t mask);
int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_uptodate(struct extent_io_tree *tree, u64 start, u64 end,
...@@ -141,8 +145,14 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, ...@@ -141,8 +145,14 @@ int set_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask); gfp_t mask);
int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end, int clear_extent_dirty(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask); gfp_t mask);
int clear_extent_ordered(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask);
int clear_extent_ordered_metadata(struct extent_io_tree *tree, u64 start,
u64 end, gfp_t mask);
int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end, int set_extent_delalloc(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask); gfp_t mask);
int set_extent_ordered(struct extent_io_tree *tree, u64 start, u64 end,
gfp_t mask);
int find_first_extent_bit(struct extent_io_tree *tree, u64 start, int find_first_extent_bit(struct extent_io_tree *tree, u64 start,
u64 *start_ret, u64 *end_ret, int bits); u64 *start_ret, u64 *end_ret, int bits);
struct extent_state *find_first_extent_bit_state(struct extent_io_tree *tree, struct extent_state *find_first_extent_bit_state(struct extent_io_tree *tree,
...@@ -209,6 +219,8 @@ void memset_extent_buffer(struct extent_buffer *eb, char c, ...@@ -209,6 +219,8 @@ void memset_extent_buffer(struct extent_buffer *eb, char c,
unsigned long start, unsigned long len); unsigned long start, unsigned long len);
int wait_on_extent_buffer_writeback(struct extent_io_tree *tree, int wait_on_extent_buffer_writeback(struct extent_io_tree *tree,
struct extent_buffer *eb); struct extent_buffer *eb);
int wait_on_extent_writeback(struct extent_io_tree *tree, u64 start, u64 end);
int wait_extent_bit(struct extent_io_tree *tree, u64 start, u64 end, int bits);
int clear_extent_buffer_dirty(struct extent_io_tree *tree, int clear_extent_buffer_dirty(struct extent_io_tree *tree,
struct extent_buffer *eb); struct extent_buffer *eb);
int set_extent_buffer_dirty(struct extent_io_tree *tree, int set_extent_buffer_dirty(struct extent_io_tree *tree,
......
...@@ -206,10 +206,11 @@ int add_extent_mapping(struct extent_map_tree *tree, ...@@ -206,10 +206,11 @@ int add_extent_mapping(struct extent_map_tree *tree,
struct extent_map *merge = NULL; struct extent_map *merge = NULL;
struct rb_node *rb; struct rb_node *rb;
BUG_ON(spin_trylock(&tree->lock));
rb = tree_insert(&tree->map, em->start, &em->rb_node); rb = tree_insert(&tree->map, em->start, &em->rb_node);
if (rb) { if (rb) {
merge = rb_entry(rb, struct extent_map, rb_node);
ret = -EEXIST; ret = -EEXIST;
free_extent_map(merge);
goto out; goto out;
} }
atomic_inc(&em->refs); atomic_inc(&em->refs);
...@@ -268,6 +269,7 @@ struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree, ...@@ -268,6 +269,7 @@ struct extent_map *lookup_extent_mapping(struct extent_map_tree *tree,
struct rb_node *next = NULL; struct rb_node *next = NULL;
u64 end = range_end(start, len); u64 end = range_end(start, len);
BUG_ON(spin_trylock(&tree->lock));
em = tree->last; em = tree->last;
if (em && end > em->start && start < extent_map_end(em)) if (em && end > em->start && start < extent_map_end(em))
goto found; goto found;
...@@ -318,6 +320,7 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em) ...@@ -318,6 +320,7 @@ int remove_extent_mapping(struct extent_map_tree *tree, struct extent_map *em)
{ {
int ret = 0; int ret = 0;
BUG_ON(spin_trylock(&tree->lock));
rb_erase(&em->rb_node, &tree->map); rb_erase(&em->rb_node, &tree->map);
em->in_tree = 0; em->in_tree = 0;
if (tree->last == em) if (tree->last == em)
......
...@@ -135,26 +135,37 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans, ...@@ -135,26 +135,37 @@ int btrfs_lookup_file_extent(struct btrfs_trans_handle *trans,
} }
int btrfs_csum_one_bio(struct btrfs_root *root, int btrfs_csum_one_bio(struct btrfs_root *root,
struct bio *bio, char **sums_ret) struct bio *bio, struct btrfs_ordered_sum **sums_ret)
{ {
u32 *sums; struct btrfs_ordered_sum *sums;
struct btrfs_sector_sum *sector_sum;
char *data; char *data;
struct bio_vec *bvec = bio->bi_io_vec; struct bio_vec *bvec = bio->bi_io_vec;
int bio_index = 0; int bio_index = 0;
sums = kmalloc(bio->bi_vcnt * BTRFS_CRC32_SIZE, GFP_NOFS); WARN_ON(bio->bi_vcnt <= 0);
sums = kzalloc(btrfs_ordered_sum_size(root, bio->bi_size), GFP_NOFS);
if (!sums) if (!sums)
return -ENOMEM; return -ENOMEM;
*sums_ret = (char *)sums; *sums_ret = sums;
sector_sum = &sums->sums;
sums->file_offset = page_offset(bvec->bv_page);
sums->len = bio->bi_size;
INIT_LIST_HEAD(&sums->list);
while(bio_index < bio->bi_vcnt) { while(bio_index < bio->bi_vcnt) {
data = kmap_atomic(bvec->bv_page, KM_USER0); data = kmap_atomic(bvec->bv_page, KM_USER0);
*sums = ~(u32)0; sector_sum->sum = ~(u32)0;
*sums = btrfs_csum_data(root, data + bvec->bv_offset, sector_sum->sum = btrfs_csum_data(root,
*sums, bvec->bv_len); data + bvec->bv_offset,
sector_sum->sum,
bvec->bv_len);
kunmap_atomic(data, KM_USER0); kunmap_atomic(data, KM_USER0);
btrfs_csum_final(*sums, (char *)sums); btrfs_csum_final(sector_sum->sum,
sums++; (char *)&sector_sum->sum);
sector_sum->offset = page_offset(bvec->bv_page) +
bvec->bv_offset;
sector_sum++;
bio_index++; bio_index++;
bvec++; bvec++;
} }
...@@ -163,7 +174,7 @@ int btrfs_csum_one_bio(struct btrfs_root *root, ...@@ -163,7 +174,7 @@ int btrfs_csum_one_bio(struct btrfs_root *root,
int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode, struct btrfs_root *root, struct inode *inode,
struct bio *bio, char *sums) struct btrfs_ordered_sum *sums)
{ {
u64 objectid = inode->i_ino; u64 objectid = inode->i_ino;
u64 offset; u64 offset;
...@@ -171,17 +182,16 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -171,17 +182,16 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
struct btrfs_key file_key; struct btrfs_key file_key;
struct btrfs_key found_key; struct btrfs_key found_key;
u64 next_offset; u64 next_offset;
u64 total_bytes = 0;
int found_next; int found_next;
struct btrfs_path *path; struct btrfs_path *path;
struct btrfs_csum_item *item; struct btrfs_csum_item *item;
struct btrfs_csum_item *item_end; struct btrfs_csum_item *item_end;
struct extent_buffer *leaf = NULL; struct extent_buffer *leaf = NULL;
u64 csum_offset; u64 csum_offset;
u32 *sums32 = (u32 *)sums; struct btrfs_sector_sum *sector_sum;
u32 nritems; u32 nritems;
u32 ins_size; u32 ins_size;
int bio_index = 0;
struct bio_vec *bvec = bio->bi_io_vec;
char *eb_map; char *eb_map;
char *eb_token; char *eb_token;
unsigned long map_len; unsigned long map_len;
...@@ -189,10 +199,11 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -189,10 +199,11 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
path = btrfs_alloc_path(); path = btrfs_alloc_path();
BUG_ON(!path); BUG_ON(!path);
sector_sum = &sums->sums;
again: again:
next_offset = (u64)-1; next_offset = (u64)-1;
found_next = 0; found_next = 0;
offset = page_offset(bvec->bv_page) + bvec->bv_offset; offset = sector_sum->offset;
file_key.objectid = objectid; file_key.objectid = objectid;
file_key.offset = offset; file_key.offset = offset;
btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY); btrfs_set_key_type(&file_key, BTRFS_CSUM_ITEM_KEY);
...@@ -303,7 +314,7 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -303,7 +314,7 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
item_end = (struct btrfs_csum_item *)((unsigned char *)item_end + item_end = (struct btrfs_csum_item *)((unsigned char *)item_end +
btrfs_item_size_nr(leaf, path->slots[0])); btrfs_item_size_nr(leaf, path->slots[0]));
eb_token = NULL; eb_token = NULL;
next_bvec: next_sector:
if (!eb_token || if (!eb_token ||
(unsigned long)item + BTRFS_CRC32_SIZE >= map_start + map_len) { (unsigned long)item + BTRFS_CRC32_SIZE >= map_start + map_len) {
...@@ -321,21 +332,20 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -321,21 +332,20 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
} }
if (eb_token) { if (eb_token) {
memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)), memcpy(eb_token + ((unsigned long)item & (PAGE_CACHE_SIZE - 1)),
sums32, BTRFS_CRC32_SIZE); &sector_sum->sum, BTRFS_CRC32_SIZE);
} else { } else {
write_extent_buffer(leaf, sums32, (unsigned long)item, write_extent_buffer(leaf, &sector_sum->sum,
BTRFS_CRC32_SIZE); (unsigned long)item, BTRFS_CRC32_SIZE);
} }
bio_index++; total_bytes += root->sectorsize;
bvec++; sector_sum++;
sums32++; if (total_bytes < sums->len) {
if (bio_index < bio->bi_vcnt) {
item = (struct btrfs_csum_item *)((char *)item + item = (struct btrfs_csum_item *)((char *)item +
BTRFS_CRC32_SIZE); BTRFS_CRC32_SIZE);
if (item < item_end && offset + PAGE_CACHE_SIZE == if (item < item_end && offset + PAGE_CACHE_SIZE ==
page_offset(bvec->bv_page)) { sector_sum->offset) {
offset = page_offset(bvec->bv_page); offset = sector_sum->offset;
goto next_bvec; goto next_sector;
} }
} }
if (eb_token) { if (eb_token) {
...@@ -343,7 +353,7 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -343,7 +353,7 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
eb_token = NULL; eb_token = NULL;
} }
btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_mark_buffer_dirty(path->nodes[0]);
if (bio_index < bio->bi_vcnt) { if (total_bytes < sums->len) {
btrfs_release_path(root, path); btrfs_release_path(root, path);
goto again; goto again;
} }
......
...@@ -34,7 +34,6 @@ ...@@ -34,7 +34,6 @@
#include "disk-io.h" #include "disk-io.h"
#include "transaction.h" #include "transaction.h"
#include "btrfs_inode.h" #include "btrfs_inode.h"
#include "ordered-data.h"
#include "ioctl.h" #include "ioctl.h"
#include "print-tree.h" #include "print-tree.h"
#include "compat.h" #include "compat.h"
...@@ -273,7 +272,9 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -273,7 +272,9 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
u64 mask = root->sectorsize - 1; u64 mask = root->sectorsize - 1;
last_pos_in_file = (isize + mask) & ~mask; last_pos_in_file = (isize + mask) & ~mask;
hole_size = (start_pos - last_pos_in_file + mask) & ~mask; hole_size = (start_pos - last_pos_in_file + mask) & ~mask;
if (last_pos_in_file < start_pos) { if (hole_size > 0) {
btrfs_wait_ordered_range(inode, last_pos_in_file,
last_pos_in_file + hole_size);
err = btrfs_drop_extents(trans, root, inode, err = btrfs_drop_extents(trans, root, inode,
last_pos_in_file, last_pos_in_file,
last_pos_in_file + hole_size, last_pos_in_file + hole_size,
...@@ -303,19 +304,17 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans, ...@@ -303,19 +304,17 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
inline_size > root->fs_info->max_inline || inline_size > root->fs_info->max_inline ||
(inline_size & (root->sectorsize -1)) == 0 || (inline_size & (root->sectorsize -1)) == 0 ||
inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) { inline_size >= BTRFS_MAX_INLINE_DATA_SIZE(root)) {
u64 last_end; /* check for reserved extents on each page, we don't want
* to reset the delalloc bit on things that already have
* extents reserved.
*/
set_extent_delalloc(io_tree, start_pos,
end_of_last_block, GFP_NOFS);
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
struct page *p = pages[i]; struct page *p = pages[i];
SetPageUptodate(p); SetPageUptodate(p);
set_page_dirty(p); set_page_dirty(p);
} }
last_end = (u64)(pages[num_pages -1]->index) <<
PAGE_CACHE_SHIFT;
last_end += PAGE_CACHE_SIZE - 1;
set_extent_delalloc(io_tree, start_pos, end_of_last_block,
GFP_NOFS);
btrfs_add_ordered_inode(inode);
} else { } else {
u64 aligned_end; u64 aligned_end;
/* step one, delete the existing extents in this range */ /* step one, delete the existing extents in this range */
...@@ -350,10 +349,13 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end) ...@@ -350,10 +349,13 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
struct extent_map *split = NULL; struct extent_map *split = NULL;
struct extent_map *split2 = NULL; struct extent_map *split2 = NULL;
struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree; struct extent_map_tree *em_tree = &BTRFS_I(inode)->extent_tree;
struct extent_map *tmp;
u64 len = end - start + 1; u64 len = end - start + 1;
u64 next_start;
int ret; int ret;
int testend = 1; int testend = 1;
WARN_ON(end < start);
if (end == (u64)-1) { if (end == (u64)-1) {
len = (u64)-1; len = (u64)-1;
testend = 0; testend = 0;
...@@ -370,6 +372,8 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end) ...@@ -370,6 +372,8 @@ int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
spin_unlock(&em_tree->lock); spin_unlock(&em_tree->lock);
break; break;
} }
tmp = rb_entry(&em->rb_node, struct extent_map, rb_node);
next_start = tmp->start;
remove_extent_mapping(em_tree, em); remove_extent_mapping(em_tree, em);
if (em->block_start < EXTENT_MAP_LAST_BYTE && if (em->block_start < EXTENT_MAP_LAST_BYTE &&
...@@ -778,37 +782,58 @@ static int prepare_pages(struct btrfs_root *root, struct file *file, ...@@ -778,37 +782,58 @@ static int prepare_pages(struct btrfs_root *root, struct file *file,
struct inode *inode = fdentry(file)->d_inode; struct inode *inode = fdentry(file)->d_inode;
int err = 0; int err = 0;
u64 start_pos; u64 start_pos;
u64 last_pos;
start_pos = pos & ~((u64)root->sectorsize - 1); start_pos = pos & ~((u64)root->sectorsize - 1);
last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT;
memset(pages, 0, num_pages * sizeof(struct page *)); memset(pages, 0, num_pages * sizeof(struct page *));
again:
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
pages[i] = grab_cache_page(inode->i_mapping, index + i); pages[i] = grab_cache_page(inode->i_mapping, index + i);
if (!pages[i]) { if (!pages[i]) {
err = -ENOMEM; err = -ENOMEM;
BUG_ON(1); BUG_ON(1);
} }
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
ClearPageDirty(pages[i]);
#else
cancel_dirty_page(pages[i], PAGE_CACHE_SIZE);
#endif
wait_on_page_writeback(pages[i]); wait_on_page_writeback(pages[i]);
set_page_extent_mapped(pages[i]);
WARN_ON(!PageLocked(pages[i]));
} }
if (start_pos < inode->i_size) { if (start_pos < inode->i_size) {
u64 last_pos; struct btrfs_ordered_extent *ordered;
last_pos = ((u64)index + num_pages) << PAGE_CACHE_SHIFT;
lock_extent(&BTRFS_I(inode)->io_tree, lock_extent(&BTRFS_I(inode)->io_tree,
start_pos, last_pos - 1, GFP_NOFS); start_pos, last_pos - 1, GFP_NOFS);
ordered = btrfs_lookup_first_ordered_extent(inode, last_pos -1);
if (ordered &&
ordered->file_offset + ordered->len > start_pos &&
ordered->file_offset < last_pos) {
btrfs_put_ordered_extent(ordered);
unlock_extent(&BTRFS_I(inode)->io_tree,
start_pos, last_pos - 1, GFP_NOFS);
for (i = 0; i < num_pages; i++) {
unlock_page(pages[i]);
page_cache_release(pages[i]);
}
btrfs_wait_ordered_range(inode, start_pos,
last_pos - start_pos);
goto again;
}
if (ordered)
btrfs_put_ordered_extent(ordered);
clear_extent_bits(&BTRFS_I(inode)->io_tree, start_pos, clear_extent_bits(&BTRFS_I(inode)->io_tree, start_pos,
last_pos - 1, EXTENT_DIRTY | EXTENT_DELALLOC, last_pos - 1, EXTENT_DIRTY | EXTENT_DELALLOC,
GFP_NOFS); GFP_NOFS);
unlock_extent(&BTRFS_I(inode)->io_tree, unlock_extent(&BTRFS_I(inode)->io_tree,
start_pos, last_pos - 1, GFP_NOFS); start_pos, last_pos - 1, GFP_NOFS);
} }
for (i = 0; i < num_pages; i++) {
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
ClearPageDirty(pages[i]);
#else
cancel_dirty_page(pages[i], PAGE_CACHE_SIZE);
#endif
set_page_extent_mapped(pages[i]);
WARN_ON(!PageLocked(pages[i]));
}
return 0; return 0;
} }
...@@ -969,13 +994,11 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -969,13 +994,11 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
(start_pos + num_written - 1) >> PAGE_CACHE_SHIFT); (start_pos + num_written - 1) >> PAGE_CACHE_SHIFT);
} }
current->backing_dev_info = NULL; current->backing_dev_info = NULL;
btrfs_ordered_throttle(root, inode);
return num_written ? num_written : err; return num_written ? num_written : err;
} }
int btrfs_release_file(struct inode * inode, struct file * filp) int btrfs_release_file(struct inode * inode, struct file * filp)
{ {
btrfs_del_ordered_inode(inode, 0);
if (filp->private_data) if (filp->private_data)
btrfs_ioctl_trans_end(filp); btrfs_ioctl_trans_end(filp);
return 0; return 0;
......
This diff is collapsed.
This diff is collapsed.
...@@ -20,24 +20,73 @@ ...@@ -20,24 +20,73 @@
#define __BTRFS_ORDERED_DATA__ #define __BTRFS_ORDERED_DATA__
struct btrfs_ordered_inode_tree { struct btrfs_ordered_inode_tree {
rwlock_t lock; struct mutex mutex;
struct rb_root tree; struct rb_root tree;
struct rb_node *last;
}; };
struct btrfs_sector_sum {
u64 offset;
u32 sum;
};
struct btrfs_ordered_sum {
u64 file_offset;
u64 len;
struct list_head list;
struct btrfs_sector_sum sums;
};
/* bits for the flags field */
#define BTRFS_ORDERED_IO_DONE 0 /* set when all the pages are written */
#define BTRFS_ORDERED_COMPLETE 1 /* set when removed from the tree */
#define BTRFS_ORDERED_START 2 /* set when tree setup */
struct btrfs_ordered_extent {
u64 file_offset;
u64 start;
u64 len;
unsigned long flags;
atomic_t refs;
struct list_head list;
struct inode *inode;
wait_queue_head_t wait;
struct rb_node rb_node;
};
static inline int btrfs_ordered_sum_size(struct btrfs_root *root, u64 bytes)
{
unsigned long num_sectors = (bytes + root->sectorsize - 1) /
root->sectorsize;
return sizeof(struct btrfs_ordered_sum) +
num_sectors * sizeof(struct btrfs_sector_sum);
}
static inline void static inline void
btrfs_ordered_inode_tree_init(struct btrfs_ordered_inode_tree *t) btrfs_ordered_inode_tree_init(struct btrfs_ordered_inode_tree *t)
{ {
rwlock_init(&t->lock); mutex_init(&t->mutex);
t->tree.rb_node = NULL; t->tree.rb_node = NULL;
t->last = NULL;
} }
int btrfs_add_ordered_inode(struct inode *inode); int btrfs_put_ordered_extent(struct btrfs_ordered_extent *entry);
int btrfs_find_del_first_ordered_inode(struct btrfs_ordered_inode_tree *tree, int btrfs_remove_ordered_extent(struct inode *inode,
u64 *root_objectid, u64 *objectid, struct btrfs_ordered_extent *entry);
struct inode **inode); int btrfs_dec_test_ordered_pending(struct inode *inode,
int btrfs_find_first_ordered_inode(struct btrfs_ordered_inode_tree *tree, u64 file_offset, u64 io_size);
u64 *root_objectid, u64 *objectid, int btrfs_add_ordered_extent(struct inode *inode, u64 file_offset,
struct inode **inode); u64 start, u64 len);
void btrfs_del_ordered_inode(struct inode *inode, int force); int btrfs_add_ordered_sum(struct inode *inode, struct btrfs_ordered_sum *sum);
int btrfs_ordered_throttle(struct btrfs_root *root, struct inode *inode); struct btrfs_ordered_extent *btrfs_lookup_ordered_extent(struct inode *inode,
u64 file_offset);
void btrfs_wait_ordered_extent(struct inode *inode,
struct btrfs_ordered_extent *entry);
void btrfs_wait_ordered_range(struct inode *inode, u64 start, u64 len);
struct btrfs_ordered_extent *
btrfs_lookup_first_ordered_extent(struct inode * inode, u64 file_offset);
int btrfs_add_ordered_pending(struct inode *inode,
struct btrfs_ordered_extent *ordered,
u64 start, u64 len);
#endif #endif
...@@ -67,7 +67,6 @@ static noinline int join_transaction(struct btrfs_root *root) ...@@ -67,7 +67,6 @@ static noinline int join_transaction(struct btrfs_root *root)
cur_trans->start_time = get_seconds(); cur_trans->start_time = get_seconds();
INIT_LIST_HEAD(&cur_trans->pending_snapshots); INIT_LIST_HEAD(&cur_trans->pending_snapshots);
list_add_tail(&cur_trans->list, &root->fs_info->trans_list); list_add_tail(&cur_trans->list, &root->fs_info->trans_list);
btrfs_ordered_inode_tree_init(&cur_trans->ordered_inode_tree);
extent_io_tree_init(&cur_trans->dirty_pages, extent_io_tree_init(&cur_trans->dirty_pages,
root->fs_info->btree_inode->i_mapping, root->fs_info->btree_inode->i_mapping,
GFP_NOFS); GFP_NOFS);
...@@ -158,10 +157,12 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans, ...@@ -158,10 +157,12 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
wake_up(&cur_trans->writer_wait); wake_up(&cur_trans->writer_wait);
if (cur_trans->in_commit && throttle) { if (cur_trans->in_commit && throttle) {
int ret; DEFINE_WAIT(wait);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
ret = wait_for_commit(root, cur_trans); prepare_to_wait(&root->fs_info->transaction_throttle, &wait,
BUG_ON(ret); TASK_UNINTERRUPTIBLE);
schedule();
finish_wait(&root->fs_info->transaction_throttle, &wait);
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
} }
...@@ -486,58 +487,6 @@ static noinline int drop_dirty_roots(struct btrfs_root *tree_root, ...@@ -486,58 +487,6 @@ static noinline int drop_dirty_roots(struct btrfs_root *tree_root,
return ret; return ret;
} }
int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
struct btrfs_root *root)
{
struct btrfs_transaction *cur_trans = trans->transaction;
struct inode *inode;
u64 root_objectid = 0;
u64 objectid = 0;
int ret;
atomic_inc(&root->fs_info->throttles);
while(1) {
ret = btrfs_find_first_ordered_inode(
&cur_trans->ordered_inode_tree,
&root_objectid, &objectid, &inode);
if (!ret)
break;
mutex_unlock(&root->fs_info->trans_mutex);
if (S_ISREG(inode->i_mode)) {
atomic_inc(&BTRFS_I(inode)->ordered_writeback);
filemap_fdatawrite(inode->i_mapping);
atomic_dec(&BTRFS_I(inode)->ordered_writeback);
}
iput(inode);
mutex_lock(&root->fs_info->trans_mutex);
}
while(1) {
root_objectid = 0;
objectid = 0;
ret = btrfs_find_del_first_ordered_inode(
&cur_trans->ordered_inode_tree,
&root_objectid, &objectid, &inode);
if (!ret)
break;
mutex_unlock(&root->fs_info->trans_mutex);
if (S_ISREG(inode->i_mode)) {
atomic_inc(&BTRFS_I(inode)->ordered_writeback);
filemap_write_and_wait(inode->i_mapping);
atomic_dec(&BTRFS_I(inode)->ordered_writeback);
}
atomic_dec(&inode->i_count);
iput(inode);
mutex_lock(&root->fs_info->trans_mutex);
}
atomic_dec(&root->fs_info->throttles);
return 0;
}
static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans, static noinline int create_pending_snapshot(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info, struct btrfs_fs_info *fs_info,
struct btrfs_pending_snapshot *pending) struct btrfs_pending_snapshot *pending)
...@@ -666,6 +615,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -666,6 +615,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
extent_io_tree_init(pinned_copy, extent_io_tree_init(pinned_copy,
root->fs_info->btree_inode->i_mapping, GFP_NOFS); root->fs_info->btree_inode->i_mapping, GFP_NOFS);
printk("commit trans %Lu\n", trans->transid);
trans->transaction->in_commit = 1; trans->transaction->in_commit = 1;
cur_trans = trans->transaction; cur_trans = trans->transaction;
if (cur_trans->list.prev != &root->fs_info->trans_list) { if (cur_trans->list.prev != &root->fs_info->trans_list) {
...@@ -699,8 +649,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -699,8 +649,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
finish_wait(&cur_trans->writer_wait, &wait); finish_wait(&cur_trans->writer_wait, &wait);
ret = btrfs_write_ordered_inodes(trans, root);
} while (cur_trans->num_writers > 1 || } while (cur_trans->num_writers > 1 ||
(cur_trans->num_joined != joined)); (cur_trans->num_joined != joined));
...@@ -736,6 +684,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -736,6 +684,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
btrfs_copy_pinned(root, pinned_copy); btrfs_copy_pinned(root, pinned_copy);
wake_up(&root->fs_info->transaction_throttle);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
ret = btrfs_write_and_wait_transaction(trans, root); ret = btrfs_write_and_wait_transaction(trans, root);
BUG_ON(ret); BUG_ON(ret);
...@@ -758,6 +708,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans, ...@@ -758,6 +708,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots); list_splice_init(&dirty_fs_roots, &root->fs_info->dead_roots);
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
printk("done commit trans %Lu\n", trans->transid);
kmem_cache_free(btrfs_trans_handle_cachep, trans); kmem_cache_free(btrfs_trans_handle_cachep, trans);
if (root->fs_info->closing) { if (root->fs_info->closing) {
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#ifndef __BTRFS_TRANSACTION__ #ifndef __BTRFS_TRANSACTION__
#define __BTRFS_TRANSACTION__ #define __BTRFS_TRANSACTION__
#include "btrfs_inode.h" #include "btrfs_inode.h"
#include "ordered-data.h"
struct btrfs_transaction { struct btrfs_transaction {
u64 transid; u64 transid;
...@@ -31,7 +30,6 @@ struct btrfs_transaction { ...@@ -31,7 +30,6 @@ struct btrfs_transaction {
struct list_head list; struct list_head list;
struct extent_io_tree dirty_pages; struct extent_io_tree dirty_pages;
unsigned long start_time; unsigned long start_time;
struct btrfs_ordered_inode_tree ordered_inode_tree;
wait_queue_head_t writer_wait; wait_queue_head_t writer_wait;
wait_queue_head_t commit_wait; wait_queue_head_t commit_wait;
struct list_head pending_snapshots; struct list_head pending_snapshots;
...@@ -88,8 +86,6 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly); ...@@ -88,8 +86,6 @@ int btrfs_defrag_root(struct btrfs_root *root, int cacheonly);
int btrfs_clean_old_snapshots(struct btrfs_root *root); int btrfs_clean_old_snapshots(struct btrfs_root *root);
int btrfs_commit_transaction(struct btrfs_trans_handle *trans, int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_write_ordered_inodes(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans, int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
#endif #endif
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