Btrfs: do extent allocation and reference count updates in the background

The extent allocation tree maintains a reference count and full
back reference information for every extent allocated in the
filesystem.  For subvolume and snapshot trees, every time
a block goes through COW, the new copy of the block adds a reference
on every block it points to.

If a btree node points to 150 leaves, then the COW code needs to go
and add backrefs on 150 different extents, which might be spread all
over the extent allocation tree.

These updates currently happen during btrfs_cow_block, and most COWs
happen during btrfs_search_slot.  btrfs_search_slot has locks held
on both the parent and the node we are COWing, and so we really want
to avoid IO during the COW if we can.

This commit adds an rbtree of pending reference count updates and extent
allocations.  The tree is ordered by byte number of the extent and byte number
of the parent for the back reference.  The tree allows us to:

1) Modify back references in something close to disk order, reducing seeks
2) Significantly reduce the number of modifications made as block pointers
are balanced around
3) Do all of the extent insertion and back reference modifications outside
of the performance critical btrfs_search_slot code.

#3 has the added benefit of greatly reducing the btrfs stack footprint.
The extent allocation tree modifications are done without the deep
(and somewhat recursive) call chains used in the past.

These delayed back reference updates must be done before the transaction
commits, and so the rbtree is tied to the transaction.  Throttling is
implemented to help keep the queue of backrefs at a reasonable size.

Since there was a similar mechanism in place for the extent tree
extents, that is removed and replaced by the delayed reference tree.

Yan Zheng <yan.zheng@oracle.com> helped review and fixup this code.

Signed-off-by: Chris Mason <chris.mason@oracle.com>
This commit is contained in:
Chris Mason 2009-03-13 10:10:06 -04:00
parent 9fa8cfe706
commit 56bec294de
11 changed files with 1236 additions and 1142 deletions

View file

@ -8,7 +8,7 @@ btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \ extent_map.o sysfs.o struct-funcs.o xattr.o ordered-data.o \
extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \ extent_io.o volumes.o async-thread.o ioctl.o locking.o orphan.o \
ref-cache.o export.o tree-log.o acl.o free-space-cache.o zlib.o \ ref-cache.o export.o tree-log.o acl.o free-space-cache.o zlib.o \
compression.o compression.o delayed-ref.o
else else
# Normal Makefile # Normal Makefile

View file

@ -922,6 +922,7 @@ static noinline int balance_level(struct btrfs_trans_handle *trans,
spin_unlock(&root->node_lock); spin_unlock(&root->node_lock);
ret = btrfs_update_extent_ref(trans, root, child->start, ret = btrfs_update_extent_ref(trans, root, child->start,
child->len,
mid->start, child->start, mid->start, child->start,
root->root_key.objectid, root->root_key.objectid,
trans->transid, level - 1); trans->transid, level - 1);
@ -2075,7 +2076,7 @@ static noinline int insert_new_root(struct btrfs_trans_handle *trans,
spin_unlock(&root->node_lock); spin_unlock(&root->node_lock);
ret = btrfs_update_extent_ref(trans, root, lower->start, ret = btrfs_update_extent_ref(trans, root, lower->start,
lower->start, c->start, lower->len, lower->start, c->start,
root->root_key.objectid, root->root_key.objectid,
trans->transid, level - 1); trans->transid, level - 1);
BUG_ON(ret); BUG_ON(ret);

View file

@ -688,8 +688,6 @@ struct btrfs_fs_info {
struct rb_root block_group_cache_tree; struct rb_root block_group_cache_tree;
struct extent_io_tree pinned_extents; struct extent_io_tree pinned_extents;
struct extent_io_tree pending_del;
struct extent_io_tree extent_ins;
/* logical->physical extent mapping */ /* logical->physical extent mapping */
struct btrfs_mapping_tree mapping_tree; struct btrfs_mapping_tree mapping_tree;
@ -717,7 +715,6 @@ struct btrfs_fs_info {
struct mutex tree_log_mutex; struct mutex tree_log_mutex;
struct mutex transaction_kthread_mutex; struct mutex transaction_kthread_mutex;
struct mutex cleaner_mutex; struct mutex cleaner_mutex;
struct mutex extent_ins_mutex;
struct mutex pinned_mutex; struct mutex pinned_mutex;
struct mutex chunk_mutex; struct mutex chunk_mutex;
struct mutex drop_mutex; struct mutex drop_mutex;
@ -1704,18 +1701,15 @@ static inline struct dentry *fdentry(struct file *file)
} }
/* extent-tree.c */ /* extent-tree.c */
int btrfs_run_delayed_refs(struct btrfs_trans_handle *trans,
struct btrfs_root *root, unsigned long count);
int btrfs_lookup_extent(struct btrfs_root *root, u64 start, u64 len); 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, int btrfs_update_pinned_extents(struct btrfs_root *root,
u64 bytenr, u64 num, int pin); u64 bytenr, u64 num, int pin);
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans, int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *leaf); struct btrfs_root *root, struct extent_buffer *leaf);
int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans, int btrfs_cross_ref_exist(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 objectid, u64 bytenr); struct btrfs_root *root, u64 objectid, u64 bytenr);
int btrfs_extent_post_op(struct btrfs_trans_handle *trans,
struct btrfs_root *root);
int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy); int btrfs_copy_pinned(struct btrfs_root *root, struct extent_io_tree *copy);
struct btrfs_block_group_cache *btrfs_lookup_block_group( struct btrfs_block_group_cache *btrfs_lookup_block_group(
struct btrfs_fs_info *info, struct btrfs_fs_info *info,
@ -1777,7 +1771,7 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 ref_generation,
u64 owner_objectid); u64 owner_objectid);
int btrfs_update_extent_ref(struct btrfs_trans_handle *trans, int btrfs_update_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr, struct btrfs_root *root, u64 bytenr, u64 num_bytes,
u64 orig_parent, u64 parent, u64 orig_parent, u64 parent,
u64 root_objectid, u64 ref_generation, u64 root_objectid, u64 ref_generation,
u64 owner_objectid); u64 owner_objectid);

585
fs/btrfs/delayed-ref.c Normal file
View file

@ -0,0 +1,585 @@
/*
* Copyright (C) 2009 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#include <linux/sched.h>
#include <linux/sort.h>
#include <linux/ftrace.h>
#include "ctree.h"
#include "delayed-ref.h"
#include "transaction.h"
/*
* delayed back reference update tracking. For subvolume trees
* we queue up extent allocations and backref maintenance for
* delayed processing. This avoids deep call chains where we
* add extents in the middle of btrfs_search_slot, and it allows
* us to buffer up frequently modified backrefs in an rb tree instead
* of hammering updates on the extent allocation tree.
*
* Right now this code is only used for reference counted trees, but
* the long term goal is to get rid of the similar code for delayed
* extent tree modifications.
*/
/*
* entries in the rb tree are ordered by the byte number of the extent
* and by the byte number of the parent block.
*/
static int comp_entry(struct btrfs_delayed_ref_node *ref,
u64 bytenr, u64 parent)
{
if (bytenr < ref->bytenr)
return -1;
if (bytenr > ref->bytenr)
return 1;
if (parent < ref->parent)
return -1;
if (parent > ref->parent)
return 1;
return 0;
}
/*
* insert a new ref into the rbtree. This returns any existing refs
* for the same (bytenr,parent) tuple, or NULL if the new node was properly
* inserted.
*/
static struct btrfs_delayed_ref_node *tree_insert(struct rb_root *root,
u64 bytenr, u64 parent,
struct rb_node *node)
{
struct rb_node **p = &root->rb_node;
struct rb_node *parent_node = NULL;
struct btrfs_delayed_ref_node *entry;
int cmp;
while (*p) {
parent_node = *p;
entry = rb_entry(parent_node, struct btrfs_delayed_ref_node,
rb_node);
cmp = comp_entry(entry, bytenr, parent);
if (cmp < 0)
p = &(*p)->rb_left;
else if (cmp > 0)
p = &(*p)->rb_right;
else
return entry;
}
entry = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
rb_link_node(node, parent_node, p);
rb_insert_color(node, root);
return NULL;
}
/*
* find an entry based on (bytenr,parent). This returns the delayed
* ref if it was able to find one, or NULL if nothing was in that spot
*/
static struct btrfs_delayed_ref_node *tree_search(struct rb_root *root,
u64 bytenr, u64 parent)
{
struct rb_node *n = root->rb_node;
struct btrfs_delayed_ref_node *entry;
int cmp;
while (n) {
entry = rb_entry(n, struct btrfs_delayed_ref_node, rb_node);
WARN_ON(!entry->in_tree);
cmp = comp_entry(entry, bytenr, parent);
if (cmp < 0)
n = n->rb_left;
else if (cmp > 0)
n = n->rb_right;
else
return entry;
}
return NULL;
}
/*
* Locking on delayed refs is done by taking a lock on the head node,
* which has the (impossible) parent id of (u64)-1. Once a lock is held
* on the head node, you're allowed (and required) to process all the
* delayed refs for a given byte number in the tree.
*
* This will walk forward in the rbtree until it finds a head node it
* is able to lock. It might not lock the delayed ref you asked for,
* and so it will return the one it did lock in next_ret and return 0.
*
* If no locks are taken, next_ret is set to null and 1 is returned. This
* means there are no more unlocked head nodes in the rbtree.
*/
int btrfs_lock_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref,
struct btrfs_delayed_ref_head **next_ret)
{
struct rb_node *node;
struct btrfs_delayed_ref_head *head;
int ret = 0;
while (1) {
if (btrfs_delayed_ref_is_head(ref)) {
head = btrfs_delayed_node_to_head(ref);
if (mutex_trylock(&head->mutex)) {
*next_ret = head;
ret = 0;
break;
}
}
node = rb_next(&ref->rb_node);
if (!node) {
ret = 1;
*next_ret = NULL;
break;
}
ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
}
return ret;
}
/*
* This checks to see if there are any delayed refs in the
* btree for a given bytenr. It returns one if it finds any
* and zero otherwise.
*
* If it only finds a head node, it returns 0.
*
* The idea is to use this when deciding if you can safely delete an
* extent from the extent allocation tree. There may be a pending
* ref in the rbtree that adds or removes references, so as long as this
* returns one you need to leave the BTRFS_EXTENT_ITEM in the extent
* allocation tree.
*/
int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr)
{
struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_root *delayed_refs;
struct rb_node *prev_node;
int ret = 0;
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1);
if (ref) {
prev_node = rb_prev(&ref->rb_node);
if (!prev_node)
goto out;
ref = rb_entry(prev_node, struct btrfs_delayed_ref_node,
rb_node);
if (ref->bytenr == bytenr)
ret = 1;
}
out:
spin_unlock(&delayed_refs->lock);
return ret;
}
/*
* helper function to lookup reference count
*
* the head node for delayed ref is used to store the sum of all the
* reference count modifications queued up in the rbtree. This way you
* can check to see what the reference count would be if all of the
* delayed refs are processed.
*/
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u32 *refs)
{
struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_head *head;
struct btrfs_delayed_ref_root *delayed_refs;
struct btrfs_path *path;
struct extent_buffer *leaf;
struct btrfs_extent_item *ei;
struct btrfs_key key;
u32 num_refs;
int ret;
path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
key.objectid = bytenr;
key.type = BTRFS_EXTENT_ITEM_KEY;
key.offset = num_bytes;
delayed_refs = &trans->transaction->delayed_refs;
again:
ret = btrfs_search_slot(trans, root->fs_info->extent_root,
&key, path, 0, 0);
if (ret < 0)
goto out;
if (ret == 0) {
leaf = path->nodes[0];
ei = btrfs_item_ptr(leaf, path->slots[0],
struct btrfs_extent_item);
num_refs = btrfs_extent_refs(leaf, ei);
} else {
num_refs = 0;
ret = 0;
}
spin_lock(&delayed_refs->lock);
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1);
if (ref) {
head = btrfs_delayed_node_to_head(ref);
if (mutex_trylock(&head->mutex)) {
num_refs += ref->ref_mod;
mutex_unlock(&head->mutex);
*refs = num_refs;
goto out;
}
atomic_inc(&ref->refs);
spin_unlock(&delayed_refs->lock);
btrfs_release_path(root->fs_info->extent_root, path);
mutex_lock(&head->mutex);
mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(ref);
goto again;
} else {
*refs = num_refs;
}
out:
spin_unlock(&delayed_refs->lock);
btrfs_free_path(path);
return ret;
}
/*
* helper function to update an extent delayed ref in the
* rbtree. existing and update must both have the same
* bytenr and parent
*
* This may free existing if the update cancels out whatever
* operation it was doing.
*/
static noinline void
update_existing_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_root *delayed_refs,
struct btrfs_delayed_ref_node *existing,
struct btrfs_delayed_ref_node *update)
{
struct btrfs_delayed_ref *existing_ref;
struct btrfs_delayed_ref *ref;
existing_ref = btrfs_delayed_node_to_ref(existing);
ref = btrfs_delayed_node_to_ref(update);
if (ref->pin)
existing_ref->pin = 1;
if (ref->action != existing_ref->action) {
/*
* this is effectively undoing either an add or a
* drop. We decrement the ref_mod, and if it goes
* down to zero we just delete the entry without
* every changing the extent allocation tree.
*/
existing->ref_mod--;
if (existing->ref_mod == 0) {
rb_erase(&existing->rb_node,
&delayed_refs->root);
existing->in_tree = 0;
btrfs_put_delayed_ref(existing);
delayed_refs->num_entries--;
if (trans->delayed_ref_updates)
trans->delayed_ref_updates--;
}
} else {
if (existing_ref->action == BTRFS_ADD_DELAYED_REF) {
/* if we're adding refs, make sure all the
* details match up. The extent could
* have been totally freed and reallocated
* by a different owner before the delayed
* ref entries were removed.
*/
existing_ref->owner_objectid = ref->owner_objectid;
existing_ref->generation = ref->generation;
existing_ref->root = ref->root;
existing->num_bytes = update->num_bytes;
}
/*
* the action on the existing ref matches
* the action on the ref we're trying to add.
* Bump the ref_mod by one so the backref that
* is eventually added/removed has the correct
* reference count
*/
existing->ref_mod += update->ref_mod;
}
}
/*
* helper function to update the accounting in the head ref
* existing and update must have the same bytenr
*/
static noinline void
update_existing_head_ref(struct btrfs_delayed_ref_node *existing,
struct btrfs_delayed_ref_node *update)
{
struct btrfs_delayed_ref_head *existing_ref;
struct btrfs_delayed_ref_head *ref;
existing_ref = btrfs_delayed_node_to_head(existing);
ref = btrfs_delayed_node_to_head(update);
if (ref->must_insert_reserved) {
/* if the extent was freed and then
* reallocated before the delayed ref
* entries were processed, we can end up
* with an existing head ref without
* the must_insert_reserved flag set.
* Set it again here
*/
existing_ref->must_insert_reserved = ref->must_insert_reserved;
/*
* update the num_bytes so we make sure the accounting
* is done correctly
*/
existing->num_bytes = update->num_bytes;
}
/*
* update the reference mod on the head to reflect this new operation
*/
existing->ref_mod += update->ref_mod;
}
/*
* helper function to actually insert a delayed ref into the rbtree.
* this does all the dirty work in terms of maintaining the correct
* overall modification count in the head node and properly dealing
* with updating existing nodes as new modifications are queued.
*/
static noinline int __btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
u64 ref_generation, u64 owner_objectid, int action,
int pin)
{
struct btrfs_delayed_ref_node *existing;
struct btrfs_delayed_ref *full_ref;
struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs;
int count_mod = 1;
int must_insert_reserved = 0;
/*
* the head node stores the sum of all the mods, so dropping a ref
* should drop the sum in the head node by one.
*/
if (parent == (u64)-1 && action == BTRFS_DROP_DELAYED_REF)
count_mod = -1;
/*
* BTRFS_ADD_DELAYED_EXTENT means that we need to update
* the reserved accounting when the extent is finally added, or
* if a later modification deletes the delayed ref without ever
* inserting the extent into the extent allocation tree.
* ref->must_insert_reserved is the flag used to record
* that accounting mods are required.
*
* Once we record must_insert_reserved, switch the action to
* BTRFS_ADD_DELAYED_REF because other special casing is not required.
*/
if (action == BTRFS_ADD_DELAYED_EXTENT) {
must_insert_reserved = 1;
action = BTRFS_ADD_DELAYED_REF;
} else {
must_insert_reserved = 0;
}
delayed_refs = &trans->transaction->delayed_refs;
/* first set the basic ref node struct up */
atomic_set(&ref->refs, 1);
ref->bytenr = bytenr;
ref->parent = parent;
ref->ref_mod = count_mod;
ref->in_tree = 1;
ref->num_bytes = num_bytes;
if (btrfs_delayed_ref_is_head(ref)) {
head_ref = btrfs_delayed_node_to_head(ref);
head_ref->must_insert_reserved = must_insert_reserved;
mutex_init(&head_ref->mutex);
} else {
full_ref = btrfs_delayed_node_to_ref(ref);
full_ref->root = ref_root;
full_ref->generation = ref_generation;
full_ref->owner_objectid = owner_objectid;
full_ref->pin = pin;
full_ref->action = action;
}
existing = tree_insert(&delayed_refs->root, bytenr,
parent, &ref->rb_node);
if (existing) {
if (btrfs_delayed_ref_is_head(ref))
update_existing_head_ref(existing, ref);
else
update_existing_ref(trans, delayed_refs, existing, ref);
/*
* we've updated the existing ref, free the newly
* allocated ref
*/
kfree(ref);
} else {
delayed_refs->num_entries++;
trans->delayed_ref_updates++;
}
return 0;
}
/*
* add a delayed ref to the tree. This does all of the accounting required
* to make sure the delayed ref is eventually processed before this
* transaction commits.
*/
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
u64 ref_generation, u64 owner_objectid, int action,
int pin)
{
struct btrfs_delayed_ref *ref;
struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs;
int ret;
ref = kmalloc(sizeof(*ref), GFP_NOFS);
if (!ref)
return -ENOMEM;
/*
* the parent = 0 case comes from cases where we don't actually
* know the parent yet. It will get updated later via a add/drop
* pair.
*/
if (parent == 0)
parent = bytenr;
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
if (!head_ref) {
kfree(ref);
return -ENOMEM;
}
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
/*
* insert both the head node and the new ref without dropping
* the spin lock
*/
ret = __btrfs_add_delayed_ref(trans, &head_ref->node, bytenr, num_bytes,
(u64)-1, 0, 0, 0, action, pin);
BUG_ON(ret);
ret = __btrfs_add_delayed_ref(trans, &ref->node, bytenr, num_bytes,
parent, ref_root, ref_generation,
owner_objectid, action, pin);
BUG_ON(ret);
spin_unlock(&delayed_refs->lock);
return 0;
}
/*
* add a delayed ref to the tree. This does all of the accounting required
* to make sure the delayed ref is eventually processed before this
* transaction commits.
*
* The main point of this call is to add and remove a backreference in a single
* shot, taking the lock only once, and only searching for the head node once.
*
* It is the same as doing a ref add and delete in two separate calls.
*/
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 orig_parent,
u64 parent, u64 orig_ref_root, u64 ref_root,
u64 orig_ref_generation, u64 ref_generation,
u64 owner_objectid, int pin)
{
struct btrfs_delayed_ref *ref;
struct btrfs_delayed_ref *old_ref;
struct btrfs_delayed_ref_head *head_ref;
struct btrfs_delayed_ref_root *delayed_refs;
int ret;
ref = kmalloc(sizeof(*ref), GFP_NOFS);
if (!ref)
return -ENOMEM;
old_ref = kmalloc(sizeof(*old_ref), GFP_NOFS);
if (!old_ref) {
kfree(ref);
return -ENOMEM;
}
/*
* the parent = 0 case comes from cases where we don't actually
* know the parent yet. It will get updated later via a add/drop
* pair.
*/
if (parent == 0)
parent = bytenr;
if (orig_parent == 0)
orig_parent = bytenr;
head_ref = kmalloc(sizeof(*head_ref), GFP_NOFS);
if (!head_ref) {
kfree(ref);
kfree(old_ref);
return -ENOMEM;
}
delayed_refs = &trans->transaction->delayed_refs;
spin_lock(&delayed_refs->lock);
/*
* insert both the head node and the new ref without dropping
* the spin lock
*/
ret = __btrfs_add_delayed_ref(trans, &head_ref->node, bytenr, num_bytes,
(u64)-1, 0, 0, 0,
BTRFS_ADD_DELAYED_REF, 0);
BUG_ON(ret);
ret = __btrfs_add_delayed_ref(trans, &ref->node, bytenr, num_bytes,
parent, ref_root, ref_generation,
owner_objectid, BTRFS_ADD_DELAYED_REF, 0);
BUG_ON(ret);
ret = __btrfs_add_delayed_ref(trans, &old_ref->node, bytenr, num_bytes,
orig_parent, orig_ref_root,
orig_ref_generation, owner_objectid,
BTRFS_DROP_DELAYED_REF, pin);
BUG_ON(ret);
spin_unlock(&delayed_refs->lock);
return 0;
}

182
fs/btrfs/delayed-ref.h Normal file
View file

@ -0,0 +1,182 @@
/*
* Copyright (C) 2008 Oracle. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License v2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 021110-1307, USA.
*/
#ifndef __DELAYED_REF__
#define __DELAYED_REF__
/* these are the possible values of struct btrfs_delayed_ref->action */
#define BTRFS_ADD_DELAYED_REF 1 /* add one backref to the tree */
#define BTRFS_DROP_DELAYED_REF 2 /* delete one backref from the tree */
#define BTRFS_ADD_DELAYED_EXTENT 3 /* record a full extent allocation */
struct btrfs_delayed_ref_node {
struct rb_node rb_node;
/* the starting bytenr of the extent */
u64 bytenr;
/* the parent our backref will point to */
u64 parent;
/* the size of the extent */
u64 num_bytes;
/* ref count on this data structure */
atomic_t refs;
/*
* how many refs is this entry adding or deleting. For
* head refs, this may be a negative number because it is keeping
* track of the total mods done to the reference count.
* For individual refs, this will always be a positive number
*
* It may be more than one, since it is possible for a single
* parent to have more than one ref on an extent
*/
int ref_mod;
/* is this node still in the rbtree? */
unsigned int in_tree:1;
};
/*
* the head refs are used to hold a lock on a given extent, which allows us
* to make sure that only one process is running the delayed refs
* at a time for a single extent. They also store the sum of all the
* reference count modifications we've queued up.
*/
struct btrfs_delayed_ref_head {
struct btrfs_delayed_ref_node node;
/*
* the mutex is held while running the refs, and it is also
* held when checking the sum of reference modifications.
*/
struct mutex mutex;
/*
* when a new extent is allocated, it is just reserved in memory
* The actual extent isn't inserted into the extent allocation tree
* until the delayed ref is processed. must_insert_reserved is
* used to flag a delayed ref so the accounting can be updated
* when a full insert is done.
*
* It is possible the extent will be freed before it is ever
* inserted into the extent allocation tree. In this case
* we need to update the in ram accounting to properly reflect
* the free has happened.
*/
unsigned int must_insert_reserved:1;
};
struct btrfs_delayed_ref {
struct btrfs_delayed_ref_node node;
/* the root objectid our ref will point to */
u64 root;
/* the generation for the backref */
u64 generation;
/* owner_objectid of the backref */
u64 owner_objectid;
/* operation done by this entry in the rbtree */
u8 action;
/* if pin == 1, when the extent is freed it will be pinned until
* transaction commit
*/
unsigned int pin:1;
};
struct btrfs_delayed_ref_root {
struct rb_root root;
/* this spin lock protects the rbtree and the entries inside */
spinlock_t lock;
/* how many delayed ref updates we've queued, used by the
* throttling code
*/
unsigned long num_entries;
/*
* set when the tree is flushing before a transaction commit,
* used by the throttling code to decide if new updates need
* to be run right away
*/
int flushing;
};
static inline void btrfs_put_delayed_ref(struct btrfs_delayed_ref_node *ref)
{
WARN_ON(atomic_read(&ref->refs) == 0);
if (atomic_dec_and_test(&ref->refs)) {
WARN_ON(ref->in_tree);
kfree(ref);
}
}
int btrfs_add_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 parent, u64 ref_root,
u64 ref_generation, u64 owner_objectid, int action,
int pin);
struct btrfs_delayed_ref *
btrfs_find_delayed_ref(struct btrfs_trans_handle *trans, u64 bytenr,
u64 parent);
int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr);
int btrfs_lock_delayed_ref(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_node *ref,
struct btrfs_delayed_ref_head **next_ret);
int btrfs_lookup_extent_ref(struct btrfs_trans_handle *trans,
struct btrfs_root *root, u64 bytenr,
u64 num_bytes, u32 *refs);
int btrfs_update_delayed_ref(struct btrfs_trans_handle *trans,
u64 bytenr, u64 num_bytes, u64 orig_parent,
u64 parent, u64 orig_ref_root, u64 ref_root,
u64 orig_ref_generation, u64 ref_generation,
u64 owner_objectid, int pin);
/*
* a node might live in a head or a regular ref, this lets you
* test for the proper type to use.
*/
static int btrfs_delayed_ref_is_head(struct btrfs_delayed_ref_node *node)
{
return node->parent == (u64)-1;
}
/*
* helper functions to cast a node into its container
*/
static inline struct btrfs_delayed_ref *
btrfs_delayed_node_to_ref(struct btrfs_delayed_ref_node *node)
{
WARN_ON(btrfs_delayed_ref_is_head(node));
return container_of(node, struct btrfs_delayed_ref, node);
}
static inline struct btrfs_delayed_ref_head *
btrfs_delayed_node_to_head(struct btrfs_delayed_ref_node *node)
{
WARN_ON(!btrfs_delayed_ref_is_head(node));
return container_of(node, struct btrfs_delayed_ref_head, node);
}
#endif

View file

@ -1458,6 +1458,7 @@ static int transaction_kthread(void *arg)
struct btrfs_root *root = arg; struct btrfs_root *root = arg;
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
struct btrfs_transaction *cur; struct btrfs_transaction *cur;
struct btrfs_fs_info *info = root->fs_info;
unsigned long now; unsigned long now;
unsigned long delay; unsigned long delay;
int ret; int ret;
@ -1471,12 +1472,6 @@ static int transaction_kthread(void *arg)
vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE); vfs_check_frozen(root->fs_info->sb, SB_FREEZE_WRITE);
mutex_lock(&root->fs_info->transaction_kthread_mutex); mutex_lock(&root->fs_info->transaction_kthread_mutex);
if (root->fs_info->total_ref_cache_size > 20 * 1024 * 1024) {
printk(KERN_INFO "btrfs: total reference cache "
"size %llu\n",
root->fs_info->total_ref_cache_size);
}
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
cur = root->fs_info->running_transaction; cur = root->fs_info->running_transaction;
if (!cur) { if (!cur) {
@ -1486,13 +1481,30 @@ static int transaction_kthread(void *arg)
now = get_seconds(); now = get_seconds();
if (now < cur->start_time || now - cur->start_time < 30) { if (now < cur->start_time || now - cur->start_time < 30) {
unsigned long num_delayed;
num_delayed = cur->delayed_refs.num_entries;
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
delay = HZ * 5; delay = HZ * 5;
/*
* we may have been woken up early to start
* processing the delayed extent ref updates
* If so, run some of them and then loop around again
* to see if we need to force a commit
*/
if (num_delayed > 64) {
mutex_unlock(&info->transaction_kthread_mutex);
trans = btrfs_start_transaction(root, 1);
btrfs_run_delayed_refs(trans, root, 256);
btrfs_end_transaction(trans, root);
continue;
}
goto sleep; goto sleep;
} }
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
trans = btrfs_start_transaction(root, 1); trans = btrfs_start_transaction(root, 1);
ret = btrfs_commit_transaction(trans, root); ret = btrfs_commit_transaction(trans, root);
sleep: sleep:
wake_up_process(root->fs_info->cleaner_kthread); wake_up_process(root->fs_info->cleaner_kthread);
mutex_unlock(&root->fs_info->transaction_kthread_mutex); mutex_unlock(&root->fs_info->transaction_kthread_mutex);
@ -1611,10 +1623,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
extent_io_tree_init(&fs_info->pinned_extents, extent_io_tree_init(&fs_info->pinned_extents,
fs_info->btree_inode->i_mapping, GFP_NOFS); fs_info->btree_inode->i_mapping, GFP_NOFS);
extent_io_tree_init(&fs_info->pending_del,
fs_info->btree_inode->i_mapping, GFP_NOFS);
extent_io_tree_init(&fs_info->extent_ins,
fs_info->btree_inode->i_mapping, GFP_NOFS);
fs_info->do_barriers = 1; fs_info->do_barriers = 1;
INIT_LIST_HEAD(&fs_info->dead_reloc_roots); INIT_LIST_HEAD(&fs_info->dead_reloc_roots);
@ -1629,7 +1637,6 @@ struct btrfs_root *open_ctree(struct super_block *sb,
mutex_init(&fs_info->trans_mutex); mutex_init(&fs_info->trans_mutex);
mutex_init(&fs_info->tree_log_mutex); mutex_init(&fs_info->tree_log_mutex);
mutex_init(&fs_info->drop_mutex); mutex_init(&fs_info->drop_mutex);
mutex_init(&fs_info->extent_ins_mutex);
mutex_init(&fs_info->pinned_mutex); mutex_init(&fs_info->pinned_mutex);
mutex_init(&fs_info->chunk_mutex); mutex_init(&fs_info->chunk_mutex);
mutex_init(&fs_info->transaction_kthread_mutex); mutex_init(&fs_info->transaction_kthread_mutex);

File diff suppressed because it is too large Load diff

View file

@ -643,7 +643,9 @@ next_slot:
if (disk_bytenr != 0) { if (disk_bytenr != 0) {
ret = btrfs_update_extent_ref(trans, root, ret = btrfs_update_extent_ref(trans, root,
disk_bytenr, orig_parent, disk_bytenr,
le64_to_cpu(old.disk_num_bytes),
orig_parent,
leaf->start, leaf->start,
root->root_key.objectid, root->root_key.objectid,
trans->transid, ins.objectid); trans->transid, ins.objectid);
@ -912,7 +914,7 @@ again:
btrfs_set_file_extent_other_encoding(leaf, fi, 0); btrfs_set_file_extent_other_encoding(leaf, fi, 0);
if (orig_parent != leaf->start) { if (orig_parent != leaf->start) {
ret = btrfs_update_extent_ref(trans, root, bytenr, ret = btrfs_update_extent_ref(trans, root, bytenr, num_bytes,
orig_parent, leaf->start, orig_parent, leaf->start,
root->root_key.objectid, root->root_key.objectid,
trans->transid, inode->i_ino); trans->transid, inode->i_ino);

View file

@ -65,6 +65,12 @@ static noinline int join_transaction(struct btrfs_root *root)
cur_trans->use_count = 1; cur_trans->use_count = 1;
cur_trans->commit_done = 0; cur_trans->commit_done = 0;
cur_trans->start_time = get_seconds(); cur_trans->start_time = get_seconds();
cur_trans->delayed_refs.root.rb_node = NULL;
cur_trans->delayed_refs.num_entries = 0;
cur_trans->delayed_refs.flushing = 0;
spin_lock_init(&cur_trans->delayed_refs.lock);
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);
extent_io_tree_init(&cur_trans->dirty_pages, extent_io_tree_init(&cur_trans->dirty_pages,
@ -182,6 +188,7 @@ static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
h->block_group = 0; h->block_group = 0;
h->alloc_exclude_nr = 0; h->alloc_exclude_nr = 0;
h->alloc_exclude_start = 0; h->alloc_exclude_start = 0;
h->delayed_ref_updates = 0;
root->fs_info->running_transaction->use_count++; root->fs_info->running_transaction->use_count++;
mutex_unlock(&root->fs_info->trans_mutex); mutex_unlock(&root->fs_info->trans_mutex);
return h; return h;
@ -281,6 +288,14 @@ static int __btrfs_end_transaction(struct btrfs_trans_handle *trans,
struct btrfs_transaction *cur_trans; struct btrfs_transaction *cur_trans;
struct btrfs_fs_info *info = root->fs_info; struct btrfs_fs_info *info = root->fs_info;
if (trans->delayed_ref_updates &&
(trans->transaction->delayed_refs.flushing ||
trans->transaction->delayed_refs.num_entries > 16384)) {
btrfs_run_delayed_refs(trans, root, trans->delayed_ref_updates);
} else if (trans->transaction->delayed_refs.num_entries > 64) {
wake_up_process(root->fs_info->transaction_kthread);
}
mutex_lock(&info->trans_mutex); mutex_lock(&info->trans_mutex);
cur_trans = info->running_transaction; cur_trans = info->running_transaction;
WARN_ON(cur_trans != trans->transaction); WARN_ON(cur_trans != trans->transaction);
@ -424,9 +439,10 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
u64 old_root_bytenr; u64 old_root_bytenr;
struct btrfs_root *tree_root = root->fs_info->tree_root; struct btrfs_root *tree_root = root->fs_info->tree_root;
btrfs_extent_post_op(trans, root);
btrfs_write_dirty_block_groups(trans, root); btrfs_write_dirty_block_groups(trans, root);
btrfs_extent_post_op(trans, root);
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
while (1) { while (1) {
old_root_bytenr = btrfs_root_bytenr(&root->root_item); old_root_bytenr = btrfs_root_bytenr(&root->root_item);
@ -438,14 +454,14 @@ static int update_cowonly_root(struct btrfs_trans_handle *trans,
btrfs_header_level(root->node)); btrfs_header_level(root->node));
btrfs_set_root_generation(&root->root_item, trans->transid); btrfs_set_root_generation(&root->root_item, trans->transid);
btrfs_extent_post_op(trans, root);
ret = btrfs_update_root(trans, tree_root, ret = btrfs_update_root(trans, tree_root,
&root->root_key, &root->root_key,
&root->root_item); &root->root_item);
BUG_ON(ret); BUG_ON(ret);
btrfs_write_dirty_block_groups(trans, root); btrfs_write_dirty_block_groups(trans, root);
btrfs_extent_post_op(trans, root);
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
} }
return 0; return 0;
} }
@ -459,15 +475,18 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
struct btrfs_fs_info *fs_info = root->fs_info; struct btrfs_fs_info *fs_info = root->fs_info;
struct list_head *next; struct list_head *next;
struct extent_buffer *eb; struct extent_buffer *eb;
int ret;
btrfs_extent_post_op(trans, fs_info->tree_root); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
eb = btrfs_lock_root_node(fs_info->tree_root); eb = btrfs_lock_root_node(fs_info->tree_root);
btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb); btrfs_cow_block(trans, fs_info->tree_root, eb, NULL, 0, &eb);
btrfs_tree_unlock(eb); btrfs_tree_unlock(eb);
free_extent_buffer(eb); free_extent_buffer(eb);
btrfs_extent_post_op(trans, fs_info->tree_root); ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
while (!list_empty(&fs_info->dirty_cowonly_roots)) { while (!list_empty(&fs_info->dirty_cowonly_roots)) {
next = fs_info->dirty_cowonly_roots.next; next = fs_info->dirty_cowonly_roots.next;
@ -475,6 +494,9 @@ int btrfs_commit_tree_roots(struct btrfs_trans_handle *trans,
root = list_entry(next, struct btrfs_root, dirty_list); root = list_entry(next, struct btrfs_root, dirty_list);
update_cowonly_root(trans, root); update_cowonly_root(trans, root);
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
} }
return 0; return 0;
} }
@ -895,6 +917,21 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
int ret; int ret;
/* make a pass through all the delayed refs we have so far
* any runnings procs may add more while we are here
*/
ret = btrfs_run_delayed_refs(trans, root, 0);
BUG_ON(ret);
/*
* set the flushing flag so procs in this transaction have to
* start sending their work down.
*/
trans->transaction->delayed_refs.flushing = 1;
ret = btrfs_run_delayed_refs(trans, root, (u64)-1);
BUG_ON(ret);
INIT_LIST_HEAD(&dirty_fs_roots); INIT_LIST_HEAD(&dirty_fs_roots);
mutex_lock(&root->fs_info->trans_mutex); mutex_lock(&root->fs_info->trans_mutex);
if (trans->transaction->in_commit) { if (trans->transaction->in_commit) {
@ -969,6 +1006,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
ret = create_pending_snapshots(trans, root->fs_info); ret = create_pending_snapshots(trans, root->fs_info);
BUG_ON(ret); BUG_ON(ret);
ret = btrfs_run_delayed_refs(trans, root, (unsigned long)-1);
BUG_ON(ret);
WARN_ON(cur_trans != trans->transaction); WARN_ON(cur_trans != trans->transaction);
/* btrfs_commit_tree_roots is responsible for getting the /* btrfs_commit_tree_roots is responsible for getting the

View file

@ -19,6 +19,7 @@
#ifndef __BTRFS_TRANSACTION__ #ifndef __BTRFS_TRANSACTION__
#define __BTRFS_TRANSACTION__ #define __BTRFS_TRANSACTION__
#include "btrfs_inode.h" #include "btrfs_inode.h"
#include "delayed-ref.h"
struct btrfs_transaction { struct btrfs_transaction {
u64 transid; u64 transid;
@ -34,6 +35,7 @@ struct btrfs_transaction {
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;
struct btrfs_delayed_ref_root delayed_refs;
}; };
struct btrfs_trans_handle { struct btrfs_trans_handle {
@ -44,6 +46,7 @@ struct btrfs_trans_handle {
u64 block_group; u64 block_group;
u64 alloc_exclude_start; u64 alloc_exclude_start;
u64 alloc_exclude_nr; u64 alloc_exclude_nr;
unsigned long delayed_ref_updates;
}; };
struct btrfs_pending_snapshot { struct btrfs_pending_snapshot {

View file

@ -124,8 +124,6 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
} }
btrfs_release_path(root, path); btrfs_release_path(root, path);
if (is_extent)
btrfs_extent_post_op(trans, root);
out: out:
if (path) if (path)
btrfs_free_path(path); btrfs_free_path(path);