Commit 5b21f2ed authored by Zheng Yan's avatar Zheng Yan Committed by Chris Mason
Browse files

Btrfs: extent_map and data=ordered fixes for space balancing



* Add an EXTENT_BOUNDARY state bit to keep the writepage code
from merging data extents that are in the process of being
relocated.  This allows us to do accounting for them properly.

* The balancing code relocates data extents indepdent of the underlying
inode.  The extent_map code was modified to properly account for
things moving around (invalidating extent_map caches in the inode).

* Don't take the drop_mutex in the create_subvol ioctl.  It isn't
required.

* Fix walking of the ordered extent list to avoid races with sys_unlink

* Change the lock ordering rules.  Transaction start goes outside
the drop_mutex.  This allows btrfs_commit_transaction to directly
drop the relocation trees.

Signed-off-by: default avatarChris Mason <chris.mason@oracle.com>
parent e4657689
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -290,7 +290,6 @@ int noinline btrfs_cow_block(struct btrfs_trans_handle *trans,
		    struct extent_buffer **cow_ret, u64 prealloc_dest)
{
	u64 search_start;
	u64 header_trans;
	int ret;

	if (trans->transaction != root->fs_info->running_transaction) {
@@ -304,9 +303,9 @@ int noinline btrfs_cow_block(struct btrfs_trans_handle *trans,
		WARN_ON(1);
	}

	header_trans = btrfs_header_generation(buf);
	spin_lock(&root->fs_info->hash_lock);
	if (header_trans == trans->transid &&
	if (btrfs_header_generation(buf) == trans->transid &&
	    btrfs_header_owner(buf) == root->root_key.objectid &&
	    !btrfs_header_flag(buf, BTRFS_HEADER_FLAG_WRITTEN)) {
		*cow_ret = buf;
		spin_unlock(&root->fs_info->hash_lock);
@@ -1300,6 +1299,7 @@ again:
			/* is a cow on this block not required */
			spin_lock(&root->fs_info->hash_lock);
			if (btrfs_header_generation(b) == trans->transid &&
			    btrfs_header_owner(b) == root->root_key.objectid &&
			    !btrfs_header_flag(b, BTRFS_HEADER_FLAG_WRITTEN)) {
				spin_unlock(&root->fs_info->hash_lock);
				goto cow_done;
@@ -1396,7 +1396,8 @@ cow_done:

			/* this is only true while dropping a snapshot */
			if (level == lowest_level) {
				break;
				ret = 0;
				goto done;
			}

			blocknr = btrfs_node_blockptr(b, slot);
+10 −1
Original line number Diff line number Diff line
@@ -1486,6 +1486,9 @@ static inline struct dentry *fdentry(struct file *file)

/* extent-tree.c */
int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len);
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
			    struct btrfs_root *root, u64 bytenr,
			    u64 num_bytes, u32 *refs);
int btrfs_update_pinned_extents(struct btrfs_root *root,
				u64 bytenr, u64 num, int pin);
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
@@ -1812,6 +1815,8 @@ void btrfs_destroy_inode(struct inode *inode);
int btrfs_init_cachep(void);
void btrfs_destroy_cachep(void);
long btrfs_ioctl_trans_end(struct file *file);
struct inode *btrfs_ilookup(struct super_block *s, u64 objectid,
			    struct btrfs_root *root, int wait);
struct inode *btrfs_iget_locked(struct super_block *s, u64 objectid,
				struct btrfs_root *root);
struct inode *btrfs_iget(struct super_block *s, struct btrfs_key *location,
@@ -1824,13 +1829,17 @@ struct extent_map *btrfs_get_extent(struct inode *inode, struct page *page,
int btrfs_update_inode(struct btrfs_trans_handle *trans,
			      struct btrfs_root *root,
			      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);
void btrfs_orphan_cleanup(struct btrfs_root *root);

/* ioctl.c */
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);

/* file.c */
int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync);
int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end);
int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
			    int skip_pinned);
int btrfs_check_file(struct btrfs_root *root, struct inode *inode);
extern struct file_operations btrfs_file_operations;
int btrfs_drop_extents(struct btrfs_trans_handle *trans,
+9 −4
Original line number Diff line number Diff line
@@ -292,7 +292,7 @@ static int merge_state(struct extent_io_tree *tree,
	struct extent_state *other;
	struct rb_node *other_node;

	if (state->state & EXTENT_IOBITS)
	if (state->state & (EXTENT_IOBITS | EXTENT_BOUNDARY))
		return 0;

	other_node = rb_prev(&state->rb_node);
@@ -1070,7 +1070,8 @@ search_again:

	while(1) {
		state = rb_entry(node, struct extent_state, rb_node);
		if (found && state->start != cur_start) {
		if (found && (state->start != cur_start ||
			      (state->state & EXTENT_BOUNDARY))) {
			goto out;
		}
		if (!(state->state & EXTENT_DELALLOC)) {
@@ -1078,7 +1079,7 @@ search_again:
				*end = state->end;
			goto out;
		}
		if (!found) {
		if (!found && !(state->state & EXTENT_BOUNDARY)) {
			struct extent_state *prev_state;
			struct rb_node *prev_node = node;
			while(1) {
@@ -1088,7 +1089,11 @@ search_again:
				prev_state = rb_entry(prev_node,
						      struct extent_state,
						      rb_node);
				if (!(prev_state->state & EXTENT_DELALLOC))
				if ((prev_state->end + 1 != state->start) ||
				    !(prev_state->state & EXTENT_DELALLOC))
					break;
				if ((cur_start - prev_state->start) * 2 >
				     max_bytes)
					break;
				state = prev_state;
				node = prev_node;
+1 −0
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
#define EXTENT_BUFFER_FILLED (1 << 8)
#define EXTENT_ORDERED (1 << 9)
#define EXTENT_ORDERED_METADATA (1 << 10)
#define EXTENT_BOUNDARY (1 << 11)
#define EXTENT_IOBITS (EXTENT_LOCKED | EXTENT_WRITEBACK)

/*
+25 −6
Original line number Diff line number Diff line
@@ -294,7 +294,7 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
						       last_pos_in_file,
						       0, 0, hole_size, 0);
			btrfs_drop_extent_cache(inode, last_pos_in_file,
					last_pos_in_file + hole_size -1);
					last_pos_in_file + hole_size - 1, 0);
			mutex_unlock(&BTRFS_I(inode)->extent_mutex);
			btrfs_check_file(root, inode);
		}
@@ -337,7 +337,7 @@ static int noinline dirty_and_release_pages(struct btrfs_trans_handle *trans,
		inline_size -= start_pos;
		err = insert_inline_extent(trans, root, inode, start_pos,
					   inline_size, pages, 0, num_pages);
		btrfs_drop_extent_cache(inode, start_pos, aligned_end - 1);
		btrfs_drop_extent_cache(inode, start_pos, aligned_end - 1, 0);
		BUG_ON(err);
		mutex_unlock(&BTRFS_I(inode)->extent_mutex);

@@ -362,7 +362,8 @@ out_unlock:
	return err;
}

int noinline btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end,
			    int skip_pinned)
{
	struct extent_map *em;
	struct extent_map *split = NULL;
@@ -371,6 +372,7 @@ int noinline btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
	u64 len = end - start + 1;
	int ret;
	int testend = 1;
	unsigned long flags;

	WARN_ON(end < start);
	if (end == (u64)-1) {
@@ -389,6 +391,23 @@ int noinline btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
			spin_unlock(&em_tree->lock);
			break;
		}
		flags = em->flags;
		if (skip_pinned && test_bit(EXTENT_FLAG_PINNED, &em->flags)) {
			spin_unlock(&em_tree->lock);
			if (em->start <= start &&
			    (!testend || em->start + em->len >= start + len)) {
				free_extent_map(em);
				break;
			}
			if (start < em->start) {
				len = em->start - start;
			} else {
				len = start + len - (em->start + em->len);
				start = em->start + em->len;
			}
			free_extent_map(em);
			continue;
		}
		clear_bit(EXTENT_FLAG_PINNED, &em->flags);
		remove_extent_mapping(em_tree, em);

@@ -398,7 +417,7 @@ int noinline btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
			split->len = start - em->start;
			split->block_start = em->block_start;
			split->bdev = em->bdev;
			split->flags = em->flags;
			split->flags = flags;
			ret = add_extent_mapping(em_tree, split);
			BUG_ON(ret);
			free_extent_map(split);
@@ -412,7 +431,7 @@ int noinline btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end)
			split->start = start + len;
			split->len = em->start + em->len - (start + len);
			split->bdev = em->bdev;
			split->flags = em->flags;
			split->flags = flags;

			split->block_start = em->block_start + diff;

@@ -541,7 +560,7 @@ int noinline btrfs_drop_extents(struct btrfs_trans_handle *trans,
	int recow;
	int ret;

	btrfs_drop_extent_cache(inode, start, end - 1);
	btrfs_drop_extent_cache(inode, start, end - 1, 0);

	path = btrfs_alloc_path();
	if (!path)
Loading