提交 c226fd65 编写于 作者: L Linus Torvalds

Merge git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable

* git://git.kernel.org/pub/scm/linux/kernel/git/mason/btrfs-unstable:
  Btrfs: try to free metadata pages when we free btree blocks
  Btrfs: add extra flushing for renames and truncates
  Btrfs: make sure btrfs_update_delayed_ref doesn't increase ref_mod
  Btrfs: optimize fsyncs on old files
  Btrfs: tree logging unlink/rename fixes
  Btrfs: Make sure i_nlink doesn't hit zero too soon during log replay
  Btrfs: limit balancing work while flushing delayed refs
  Btrfs: readahead checksums during btrfs_finish_ordered_io
  Btrfs: leave btree locks spinning more often
  Btrfs: Only let very young transactions grow during commit
  Btrfs: Check for a blocking lock before taking the spin
  Btrfs: reduce stack in cow_file_range
  Btrfs: reduce stalls during transaction commit
  Btrfs: process the delayed reference queue in clusters
  Btrfs: try to cleanup delayed refs while freeing extents
  Btrfs: reduce stack usage in some crucial tree balancing functions
  Btrfs: do extent allocation and reference count updates in the background
  Btrfs: don't preallocate metadata blocks during btrfs_search_slot
...@@ -8,7 +8,7 @@ btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \ ...@@ -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
......
...@@ -66,6 +66,12 @@ struct btrfs_inode { ...@@ -66,6 +66,12 @@ struct btrfs_inode {
*/ */
struct list_head delalloc_inodes; struct list_head delalloc_inodes;
/*
* list for tracking inodes that must be sent to disk before a
* rename or truncate commit
*/
struct list_head ordered_operations;
/* the space_info for where this inode's data allocations are done */ /* the space_info for where this inode's data allocations are done */
struct btrfs_space_info *space_info; struct btrfs_space_info *space_info;
...@@ -86,12 +92,6 @@ struct btrfs_inode { ...@@ -86,12 +92,6 @@ struct btrfs_inode {
*/ */
u64 logged_trans; u64 logged_trans;
/*
* trans that last made a change that should be fully fsync'd. This
* gets reset to zero each time the inode is logged
*/
u64 log_dirty_trans;
/* total number of bytes pending delalloc, used by stat to calc the /* total number of bytes pending delalloc, used by stat to calc the
* real block usage of the file * real block usage of the file
*/ */
...@@ -121,6 +121,25 @@ struct btrfs_inode { ...@@ -121,6 +121,25 @@ struct btrfs_inode {
/* the start of block group preferred for allocations. */ /* the start of block group preferred for allocations. */
u64 block_group; u64 block_group;
/* the fsync log has some corner cases that mean we have to check
* directories to see if any unlinks have been done before
* the directory was logged. See tree-log.c for all the
* details
*/
u64 last_unlink_trans;
/*
* ordered_data_close is set by truncate when a file that used
* to have good data has been truncated to zero. When it is set
* the btrfs file release call will add this inode to the
* ordered operations list so that we make sure to flush out any
* new data the application may have written before commit.
*
* yes, its silly to have a single bitflag, but we might grow more
* of these.
*/
unsigned ordered_data_close:1;
struct inode vfs_inode; struct inode vfs_inode;
}; };
......
此差异已折叠。
...@@ -45,6 +45,13 @@ struct btrfs_ordered_sum; ...@@ -45,6 +45,13 @@ struct btrfs_ordered_sum;
#define BTRFS_MAX_LEVEL 8 #define BTRFS_MAX_LEVEL 8
/*
* files bigger than this get some pre-flushing when they are added
* to the ordered operations list. That way we limit the total
* work done by the commit
*/
#define BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT (8 * 1024 * 1024)
/* holds pointers to all of the tree roots */ /* holds pointers to all of the tree roots */
#define BTRFS_ROOT_TREE_OBJECTID 1ULL #define BTRFS_ROOT_TREE_OBJECTID 1ULL
...@@ -401,15 +408,16 @@ struct btrfs_path { ...@@ -401,15 +408,16 @@ struct btrfs_path {
int locks[BTRFS_MAX_LEVEL]; int locks[BTRFS_MAX_LEVEL];
int reada; int reada;
/* keep some upper locks as we walk down */ /* keep some upper locks as we walk down */
int keep_locks;
int skip_locking;
int lowest_level; int lowest_level;
/* /*
* set by btrfs_split_item, tells search_slot to keep all locks * set by btrfs_split_item, tells search_slot to keep all locks
* and to force calls to keep space in the nodes * and to force calls to keep space in the nodes
*/ */
int search_for_split; unsigned int search_for_split:1;
unsigned int keep_locks:1;
unsigned int skip_locking:1;
unsigned int leave_spinning:1;
}; };
/* /*
...@@ -688,15 +696,18 @@ struct btrfs_fs_info { ...@@ -688,15 +696,18 @@ 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;
u64 generation; u64 generation;
u64 last_trans_committed; u64 last_trans_committed;
u64 last_trans_new_blockgroup;
/*
* this is updated to the current trans every time a full commit
* is required instead of the faster short fsync log commits
*/
u64 last_trans_log_full_commit;
u64 open_ioctl_trans; u64 open_ioctl_trans;
unsigned long mount_opt; unsigned long mount_opt;
u64 max_extent; u64 max_extent;
...@@ -717,12 +728,21 @@ struct btrfs_fs_info { ...@@ -717,12 +728,21 @@ 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;
struct mutex volume_mutex; struct mutex volume_mutex;
struct mutex tree_reloc_mutex; struct mutex tree_reloc_mutex;
/*
* this protects the ordered operations list only while we are
* processing all of the entries on it. This way we make
* sure the commit code doesn't find the list temporarily empty
* because another function happens to be doing non-waiting preflush
* before jumping into the main commit.
*/
struct mutex ordered_operations_mutex;
struct list_head trans_list; struct list_head trans_list;
struct list_head hashers; struct list_head hashers;
struct list_head dead_roots; struct list_head dead_roots;
...@@ -737,9 +757,28 @@ struct btrfs_fs_info { ...@@ -737,9 +757,28 @@ struct btrfs_fs_info {
* ordered extents * ordered extents
*/ */
spinlock_t ordered_extent_lock; spinlock_t ordered_extent_lock;
/*
* all of the data=ordered extents pending writeback
* these can span multiple transactions and basically include
* every dirty data page that isn't from nodatacow
*/
struct list_head ordered_extents; struct list_head ordered_extents;
/*
* all of the inodes that have delalloc bytes. It is possible for
* this list to be empty even when there is still dirty data=ordered
* extents waiting to finish IO.
*/
struct list_head delalloc_inodes; struct list_head delalloc_inodes;
/*
* special rename and truncate targets that must be on disk before
* we're allowed to commit. This is basically the ext3 style
* data=ordered list.
*/
struct list_head ordered_operations;
/* /*
* there is a pool of worker threads for checksumming during writes * there is a pool of worker threads for checksumming during writes
* and a pool for checksumming after reads. This is because readers * and a pool for checksumming after reads. This is because readers
...@@ -781,6 +820,11 @@ struct btrfs_fs_info { ...@@ -781,6 +820,11 @@ struct btrfs_fs_info {
atomic_t throttle_gen; atomic_t throttle_gen;
u64 total_pinned; u64 total_pinned;
/* protected by the delalloc lock, used to keep from writing
* metadata until there is a nice batch
*/
u64 dirty_metadata_bytes;
struct list_head dirty_cowonly_roots; struct list_head dirty_cowonly_roots;
struct btrfs_fs_devices *fs_devices; struct btrfs_fs_devices *fs_devices;
...@@ -1704,18 +1748,15 @@ static inline struct dentry *fdentry(struct file *file) ...@@ -1704,18 +1748,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 +1818,7 @@ int btrfs_inc_extent_ref(struct btrfs_trans_handle *trans, ...@@ -1777,7 +1818,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);
...@@ -1838,7 +1879,7 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key, ...@@ -1838,7 +1879,7 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
int btrfs_cow_block(struct btrfs_trans_handle *trans, int btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf, struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot, struct extent_buffer *parent, int parent_slot,
struct extent_buffer **cow_ret, u64 prealloc_dest); struct extent_buffer **cow_ret);
int btrfs_copy_root(struct btrfs_trans_handle *trans, int btrfs_copy_root(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
struct extent_buffer *buf, struct extent_buffer *buf,
......
/*
* 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 btrfs_delayed_ref_node **last)
{
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);
if (last)
*last = entry;
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;
}
int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_head *head)
{
struct btrfs_delayed_ref_root *delayed_refs;
delayed_refs = &trans->transaction->delayed_refs;
assert_spin_locked(&delayed_refs->lock);
if (mutex_trylock(&head->mutex))
return 0;
atomic_inc(&head->node.refs);
spin_unlock(&delayed_refs->lock);
mutex_lock(&head->mutex);
spin_lock(&delayed_refs->lock);
if (!head->node.in_tree) {
mutex_unlock(&head->mutex);
btrfs_put_delayed_ref(&head->node);
return -EAGAIN;
}
btrfs_put_delayed_ref(&head->node);
return 0;
}
int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
struct list_head *cluster, u64 start)
{
int count = 0;
struct btrfs_delayed_ref_root *delayed_refs;
struct rb_node *node;
struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_head *head;
delayed_refs = &trans->transaction->delayed_refs;
if (start == 0) {
node = rb_first(&delayed_refs->root);
} else {
ref = NULL;
tree_search(&delayed_refs->root, start, (u64)-1, &ref);
if (ref) {
struct btrfs_delayed_ref_node *tmp;
node = rb_prev(&ref->rb_node);
while (node) {
tmp = rb_entry(node,
struct btrfs_delayed_ref_node,
rb_node);
if (tmp->bytenr < start)
break;
ref = tmp;
node = rb_prev(&ref->rb_node);
}
node = &ref->rb_node;
} else
node = rb_first(&delayed_refs->root);
}
again:
while (node && count < 32) {
ref = rb_entry(node, struct btrfs_delayed_ref_node, rb_node);
if (btrfs_delayed_ref_is_head(ref)) {
head = btrfs_delayed_node_to_head(ref);
if (list_empty(&head->cluster)) {
list_add_tail(&head->cluster, cluster);
delayed_refs->run_delayed_start =
head->node.bytenr;
count++;
WARN_ON(delayed_refs->num_heads_ready == 0);
delayed_refs->num_heads_ready--;
} else if (count) {
/* the goal of the clustering is to find extents
* that are likely to end up in the same extent
* leaf on disk. So, we don't want them spread
* all over the tree. Stop now if we've hit
* a head that was already in use
*/
break;
}
}
node = rb_next(node);
}
if (count) {
return 0;
} else if (start) {
/*
* we've gone to the end of the rbtree without finding any
* clusters. start from the beginning and try again
*/
start = 0;
node = rb_first(&delayed_refs->root);
goto again;
}
return 1;
}
/*
* 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, NULL);
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, NULL);
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 = NULL;
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) {
if (action == BTRFS_DROP_DELAYED_REF)
count_mod = -1;
else if (action == BTRFS_UPDATE_DELAYED_HEAD)
count_mod = 0;
}
/*
* 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;
INIT_LIST_HEAD(&head_ref->cluster);
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 {
if (btrfs_delayed_ref_is_head(ref)) {
delayed_refs->num_heads++;
delayed_refs->num_heads_ready++;
}
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;
}
/*
* this does a simple search for the head node for a given extent.
* It must be called with the delayed ref spinlock held, and it returns
* the head node if any where found, or NULL if not.
*/
struct btrfs_delayed_ref_head *
btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr)
{
struct btrfs_delayed_ref_node *ref;
struct btrfs_delayed_ref_root *delayed_refs;
delayed_refs = &trans->transaction->delayed_refs;
ref = tree_search(&delayed_refs->root, bytenr, (u64)-1, NULL);
if (ref)
return btrfs_delayed_node_to_head(ref);
return NULL;
}
/*
* 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_UPDATE_DELAYED_HEAD, 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;
}
/*
* 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 */
#define BTRFS_UPDATE_DELAYED_HEAD 4 /* not changing ref count on head ref */
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;
struct list_head cluster;
/*
* 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;
/* total number of head nodes in tree */
unsigned long num_heads;
/* total number of head nodes ready for processing */
unsigned long num_heads_ready;
/*
* 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;
u64 run_delayed_start;
};
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_head *
btrfs_find_delayed_ref_head(struct btrfs_trans_handle *trans, u64 bytenr);
int btrfs_delayed_ref_pending(struct btrfs_trans_handle *trans, u64 bytenr);
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);
int btrfs_delayed_ref_lock(struct btrfs_trans_handle *trans,
struct btrfs_delayed_ref_head *head);
int btrfs_find_ref_cluster(struct btrfs_trans_handle *trans,
struct list_head *cluster, u64 search_start);
/*
* 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
...@@ -145,7 +145,10 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root ...@@ -145,7 +145,10 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
key.objectid = dir; key.objectid = dir;
btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY); btrfs_set_key_type(&key, BTRFS_DIR_ITEM_KEY);
key.offset = btrfs_name_hash(name, name_len); key.offset = btrfs_name_hash(name, name_len);
path = btrfs_alloc_path(); path = btrfs_alloc_path();
path->leave_spinning = 1;
data_size = sizeof(*dir_item) + name_len; data_size = sizeof(*dir_item) + name_len;
dir_item = insert_with_overflow(trans, root, path, &key, data_size, dir_item = insert_with_overflow(trans, root, path, &key, data_size,
name, name_len); name, name_len);
......
...@@ -668,14 +668,31 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio, ...@@ -668,14 +668,31 @@ static int btree_submit_bio_hook(struct inode *inode, int rw, struct bio *bio,
static int btree_writepage(struct page *page, struct writeback_control *wbc) static int btree_writepage(struct page *page, struct writeback_control *wbc)
{ {
struct extent_io_tree *tree; struct extent_io_tree *tree;
struct btrfs_root *root = BTRFS_I(page->mapping->host)->root;
struct extent_buffer *eb;
int was_dirty;
tree = &BTRFS_I(page->mapping->host)->io_tree; tree = &BTRFS_I(page->mapping->host)->io_tree;
if (!(current->flags & PF_MEMALLOC)) {
return extent_write_full_page(tree, page,
btree_get_extent, wbc);
}
if (current->flags & PF_MEMALLOC) { redirty_page_for_writepage(wbc, page);
redirty_page_for_writepage(wbc, page); eb = btrfs_find_tree_block(root, page_offset(page),
unlock_page(page); PAGE_CACHE_SIZE);
return 0; WARN_ON(!eb);
was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
if (!was_dirty) {
spin_lock(&root->fs_info->delalloc_lock);
root->fs_info->dirty_metadata_bytes += PAGE_CACHE_SIZE;
spin_unlock(&root->fs_info->delalloc_lock);
} }
return extent_write_full_page(tree, page, btree_get_extent, wbc); free_extent_buffer(eb);
unlock_page(page);
return 0;
} }
static int btree_writepages(struct address_space *mapping, static int btree_writepages(struct address_space *mapping,
...@@ -684,15 +701,15 @@ static int btree_writepages(struct address_space *mapping, ...@@ -684,15 +701,15 @@ static int btree_writepages(struct address_space *mapping,
struct extent_io_tree *tree; struct extent_io_tree *tree;
tree = &BTRFS_I(mapping->host)->io_tree; tree = &BTRFS_I(mapping->host)->io_tree;
if (wbc->sync_mode == WB_SYNC_NONE) { if (wbc->sync_mode == WB_SYNC_NONE) {
struct btrfs_root *root = BTRFS_I(mapping->host)->root;
u64 num_dirty; u64 num_dirty;
u64 start = 0;
unsigned long thresh = 32 * 1024 * 1024; unsigned long thresh = 32 * 1024 * 1024;
if (wbc->for_kupdate) if (wbc->for_kupdate)
return 0; return 0;
num_dirty = count_range_bits(tree, &start, (u64)-1, /* this is a bit racy, but that's ok */
thresh, EXTENT_DIRTY); num_dirty = root->fs_info->dirty_metadata_bytes;
if (num_dirty < thresh) if (num_dirty < thresh)
return 0; return 0;
} }
...@@ -859,9 +876,17 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root, ...@@ -859,9 +876,17 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
root->fs_info->running_transaction->transid) { root->fs_info->running_transaction->transid) {
btrfs_assert_tree_locked(buf); btrfs_assert_tree_locked(buf);
/* ugh, clear_extent_buffer_dirty can be expensive */ if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &buf->bflags)) {
btrfs_set_lock_blocking(buf); spin_lock(&root->fs_info->delalloc_lock);
if (root->fs_info->dirty_metadata_bytes >= buf->len)
root->fs_info->dirty_metadata_bytes -= buf->len;
else
WARN_ON(1);
spin_unlock(&root->fs_info->delalloc_lock);
}
/* ugh, clear_extent_buffer_dirty needs to lock the page */
btrfs_set_lock_blocking(buf);
clear_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, clear_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree,
buf); buf);
} }
...@@ -1471,12 +1496,6 @@ static int transaction_kthread(void *arg) ...@@ -1471,12 +1496,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) {
...@@ -1493,6 +1512,7 @@ static int transaction_kthread(void *arg) ...@@ -1493,6 +1512,7 @@ static int transaction_kthread(void *arg)
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);
...@@ -1552,6 +1572,7 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1552,6 +1572,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
INIT_LIST_HEAD(&fs_info->dead_roots); INIT_LIST_HEAD(&fs_info->dead_roots);
INIT_LIST_HEAD(&fs_info->hashers); INIT_LIST_HEAD(&fs_info->hashers);
INIT_LIST_HEAD(&fs_info->delalloc_inodes); INIT_LIST_HEAD(&fs_info->delalloc_inodes);
INIT_LIST_HEAD(&fs_info->ordered_operations);
spin_lock_init(&fs_info->delalloc_lock); spin_lock_init(&fs_info->delalloc_lock);
spin_lock_init(&fs_info->new_trans_lock); spin_lock_init(&fs_info->new_trans_lock);
spin_lock_init(&fs_info->ref_cache_lock); spin_lock_init(&fs_info->ref_cache_lock);
...@@ -1611,10 +1632,6 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1611,10 +1632,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);
...@@ -1627,9 +1644,9 @@ struct btrfs_root *open_ctree(struct super_block *sb, ...@@ -1627,9 +1644,9 @@ struct btrfs_root *open_ctree(struct super_block *sb,
insert_inode_hash(fs_info->btree_inode); insert_inode_hash(fs_info->btree_inode);
mutex_init(&fs_info->trans_mutex); mutex_init(&fs_info->trans_mutex);
mutex_init(&fs_info->ordered_operations_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);
...@@ -2358,8 +2375,7 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf) ...@@ -2358,8 +2375,7 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root; struct btrfs_root *root = BTRFS_I(buf->first_page->mapping->host)->root;
u64 transid = btrfs_header_generation(buf); u64 transid = btrfs_header_generation(buf);
struct inode *btree_inode = root->fs_info->btree_inode; struct inode *btree_inode = root->fs_info->btree_inode;
int was_dirty;
btrfs_set_lock_blocking(buf);
btrfs_assert_tree_locked(buf); btrfs_assert_tree_locked(buf);
if (transid != root->fs_info->generation) { if (transid != root->fs_info->generation) {
...@@ -2370,7 +2386,13 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf) ...@@ -2370,7 +2386,13 @@ void btrfs_mark_buffer_dirty(struct extent_buffer *buf)
(unsigned long long)root->fs_info->generation); (unsigned long long)root->fs_info->generation);
WARN_ON(1); WARN_ON(1);
} }
set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree, buf); was_dirty = set_extent_buffer_dirty(&BTRFS_I(btree_inode)->io_tree,
buf);
if (!was_dirty) {
spin_lock(&root->fs_info->delalloc_lock);
root->fs_info->dirty_metadata_bytes += buf->len;
spin_unlock(&root->fs_info->delalloc_lock);
}
} }
void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr) void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr)
...@@ -2410,6 +2432,7 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid) ...@@ -2410,6 +2432,7 @@ int btrfs_read_buffer(struct extent_buffer *buf, u64 parent_transid)
int btree_lock_page_hook(struct page *page) int btree_lock_page_hook(struct page *page)
{ {
struct inode *inode = page->mapping->host; struct inode *inode = page->mapping->host;
struct btrfs_root *root = BTRFS_I(inode)->root;
struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree; struct extent_io_tree *io_tree = &BTRFS_I(inode)->io_tree;
struct extent_buffer *eb; struct extent_buffer *eb;
unsigned long len; unsigned long len;
...@@ -2425,6 +2448,16 @@ int btree_lock_page_hook(struct page *page) ...@@ -2425,6 +2448,16 @@ int btree_lock_page_hook(struct page *page)
btrfs_tree_lock(eb); btrfs_tree_lock(eb);
btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN); btrfs_set_header_flag(eb, BTRFS_HEADER_FLAG_WRITTEN);
if (test_and_clear_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
spin_lock(&root->fs_info->delalloc_lock);
if (root->fs_info->dirty_metadata_bytes >= eb->len)
root->fs_info->dirty_metadata_bytes -= eb->len;
else
WARN_ON(1);
spin_unlock(&root->fs_info->delalloc_lock);
}
btrfs_tree_unlock(eb); btrfs_tree_unlock(eb);
free_extent_buffer(eb); free_extent_buffer(eb);
out: out:
......
...@@ -72,6 +72,7 @@ int btrfs_insert_dev_radix(struct btrfs_root *root, ...@@ -72,6 +72,7 @@ int btrfs_insert_dev_radix(struct btrfs_root *root,
void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr); void btrfs_btree_balance_dirty(struct btrfs_root *root, unsigned long nr);
int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root); int btrfs_free_fs_root(struct btrfs_fs_info *fs_info, struct btrfs_root *root);
void btrfs_mark_buffer_dirty(struct extent_buffer *buf); void btrfs_mark_buffer_dirty(struct extent_buffer *buf);
void btrfs_mark_buffer_dirty_nonblocking(struct extent_buffer *buf);
int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid); int btrfs_buffer_uptodate(struct extent_buffer *buf, u64 parent_transid);
int btrfs_set_buffer_uptodate(struct extent_buffer *buf); int btrfs_set_buffer_uptodate(struct extent_buffer *buf);
int wait_on_tree_block_writeback(struct btrfs_root *root, int wait_on_tree_block_writeback(struct btrfs_root *root,
......
此差异已折叠。
...@@ -3124,20 +3124,15 @@ void free_extent_buffer(struct extent_buffer *eb) ...@@ -3124,20 +3124,15 @@ void free_extent_buffer(struct extent_buffer *eb)
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;
unsigned long i; unsigned long i;
unsigned long num_pages; unsigned long num_pages;
struct page *page; struct page *page;
u64 start = eb->start;
u64 end = start + eb->len - 1;
set = clear_extent_dirty(tree, start, end, GFP_NOFS);
num_pages = num_extent_pages(eb->start, eb->len); num_pages = num_extent_pages(eb->start, eb->len);
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++) {
page = extent_buffer_page(eb, i); page = extent_buffer_page(eb, i);
if (!set && !PageDirty(page)) if (!PageDirty(page))
continue; continue;
lock_page(page); lock_page(page);
...@@ -3146,22 +3141,6 @@ int clear_extent_buffer_dirty(struct extent_io_tree *tree, ...@@ -3146,22 +3141,6 @@ int clear_extent_buffer_dirty(struct extent_io_tree *tree,
else else
set_page_private(page, EXTENT_PAGE_PRIVATE); set_page_private(page, EXTENT_PAGE_PRIVATE);
/*
* if we're on the last page or the first page and the
* block isn't aligned on a page boundary, do extra checks
* to make sure we don't clean page that is partially dirty
*/
if ((i == 0 && (eb->start & (PAGE_CACHE_SIZE - 1))) ||
((i == num_pages - 1) &&
((eb->start + eb->len) & (PAGE_CACHE_SIZE - 1)))) {
start = (u64)page->index << PAGE_CACHE_SHIFT;
end = start + PAGE_CACHE_SIZE - 1;
if (test_range_bit(tree, start, end,
EXTENT_DIRTY, 0)) {
unlock_page(page);
continue;
}
}
clear_page_dirty_for_io(page); clear_page_dirty_for_io(page);
spin_lock_irq(&page->mapping->tree_lock); spin_lock_irq(&page->mapping->tree_lock);
if (!PageDirty(page)) { if (!PageDirty(page)) {
...@@ -3187,29 +3166,13 @@ int set_extent_buffer_dirty(struct extent_io_tree *tree, ...@@ -3187,29 +3166,13 @@ int set_extent_buffer_dirty(struct extent_io_tree *tree,
{ {
unsigned long i; unsigned long i;
unsigned long num_pages; unsigned long num_pages;
int was_dirty = 0;
was_dirty = test_and_set_bit(EXTENT_BUFFER_DIRTY, &eb->bflags);
num_pages = num_extent_pages(eb->start, eb->len); num_pages = num_extent_pages(eb->start, eb->len);
for (i = 0; i < num_pages; i++) { for (i = 0; i < num_pages; i++)
struct page *page = extent_buffer_page(eb, i);
/* writepage may need to do something special for the
* first page, we have to make sure page->private is
* properly set. releasepage may drop page->private
* on us if the page isn't already dirty.
*/
lock_page(page);
if (i == 0) {
set_page_extent_head(page, eb->len);
} else if (PagePrivate(page) &&
page->private != EXTENT_PAGE_PRIVATE) {
set_page_extent_mapped(page);
}
__set_page_dirty_nobuffers(extent_buffer_page(eb, i)); __set_page_dirty_nobuffers(extent_buffer_page(eb, i));
set_extent_dirty(tree, page_offset(page), return was_dirty;
page_offset(page) + PAGE_CACHE_SIZE - 1,
GFP_NOFS);
unlock_page(page);
}
return 0;
} }
int clear_extent_buffer_uptodate(struct extent_io_tree *tree, int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
...@@ -3789,6 +3752,10 @@ int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page) ...@@ -3789,6 +3752,10 @@ int try_release_extent_buffer(struct extent_io_tree *tree, struct page *page)
ret = 0; ret = 0;
goto out; goto out;
} }
if (test_bit(EXTENT_BUFFER_DIRTY, &eb->bflags)) {
ret = 0;
goto out;
}
/* at this point we can safely release the extent buffer */ /* at this point we can safely release the extent buffer */
num_pages = num_extent_pages(eb->start, eb->len); num_pages = num_extent_pages(eb->start, eb->len);
for (i = 0; i < num_pages; i++) for (i = 0; i < num_pages; i++)
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
/* these are bit numbers for test/set bit */ /* these are bit numbers for test/set bit */
#define EXTENT_BUFFER_UPTODATE 0 #define EXTENT_BUFFER_UPTODATE 0
#define EXTENT_BUFFER_BLOCKING 1 #define EXTENT_BUFFER_BLOCKING 1
#define EXTENT_BUFFER_DIRTY 2
/* /*
* page->private values. Every page that is controlled by the extent * page->private values. Every page that is controlled by the extent
...@@ -254,6 +255,8 @@ int clear_extent_buffer_dirty(struct extent_io_tree *tree, ...@@ -254,6 +255,8 @@ 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,
struct extent_buffer *eb); struct extent_buffer *eb);
int test_extent_buffer_dirty(struct extent_io_tree *tree,
struct extent_buffer *eb);
int set_extent_buffer_uptodate(struct extent_io_tree *tree, int set_extent_buffer_uptodate(struct extent_io_tree *tree,
struct extent_buffer *eb); struct extent_buffer *eb);
int clear_extent_buffer_uptodate(struct extent_io_tree *tree, int clear_extent_buffer_uptodate(struct extent_io_tree *tree,
......
...@@ -52,6 +52,7 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans, ...@@ -52,6 +52,7 @@ int btrfs_insert_file_extent(struct btrfs_trans_handle *trans,
file_key.offset = pos; file_key.offset = pos;
btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY); btrfs_set_key_type(&file_key, BTRFS_EXTENT_DATA_KEY);
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &file_key, ret = btrfs_insert_empty_item(trans, root, path, &file_key,
sizeof(*item)); sizeof(*item));
if (ret < 0) if (ret < 0)
...@@ -523,6 +524,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans, ...@@ -523,6 +524,7 @@ int btrfs_del_csums(struct btrfs_trans_handle *trans,
key.offset = end_byte - 1; key.offset = end_byte - 1;
key.type = BTRFS_EXTENT_CSUM_KEY; key.type = BTRFS_EXTENT_CSUM_KEY;
path->leave_spinning = 1;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1); ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret > 0) { if (ret > 0) {
if (path->slots[0] == 0) if (path->slots[0] == 0)
...@@ -757,8 +759,10 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -757,8 +759,10 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans,
} else { } else {
ins_size = csum_size; ins_size = csum_size;
} }
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &file_key, ret = btrfs_insert_empty_item(trans, root, path, &file_key,
ins_size); ins_size);
path->leave_spinning = 0;
if (ret < 0) if (ret < 0)
goto fail_unlock; goto fail_unlock;
if (ret != 0) { if (ret != 0) {
...@@ -776,7 +780,6 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -776,7 +780,6 @@ 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;
cond_resched();
next_sector: next_sector:
if (!eb_token || if (!eb_token ||
...@@ -817,9 +820,9 @@ int btrfs_csum_file_blocks(struct btrfs_trans_handle *trans, ...@@ -817,9 +820,9 @@ 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]);
cond_resched();
if (total_bytes < sums->len) { if (total_bytes < sums->len) {
btrfs_release_path(root, path); btrfs_release_path(root, path);
cond_resched();
goto again; goto again;
} }
out: out:
......
...@@ -606,6 +606,7 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -606,6 +606,7 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
btrfs_set_key_type(&ins, BTRFS_EXTENT_DATA_KEY); btrfs_set_key_type(&ins, BTRFS_EXTENT_DATA_KEY);
btrfs_release_path(root, path); btrfs_release_path(root, path);
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &ins, ret = btrfs_insert_empty_item(trans, root, path, &ins,
sizeof(*extent)); sizeof(*extent));
BUG_ON(ret); BUG_ON(ret);
...@@ -639,17 +640,22 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans, ...@@ -639,17 +640,22 @@ noinline int btrfs_drop_extents(struct btrfs_trans_handle *trans,
ram_bytes); ram_bytes);
btrfs_set_file_extent_type(leaf, extent, found_type); btrfs_set_file_extent_type(leaf, extent, found_type);
btrfs_unlock_up_safe(path, 1);
btrfs_mark_buffer_dirty(path->nodes[0]); btrfs_mark_buffer_dirty(path->nodes[0]);
btrfs_set_lock_blocking(path->nodes[0]);
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);
BUG_ON(ret); BUG_ON(ret);
} }
path->leave_spinning = 0;
btrfs_release_path(root, path); btrfs_release_path(root, path);
if (disk_bytenr != 0) if (disk_bytenr != 0)
inode_add_bytes(inode, extent_end - end); inode_add_bytes(inode, extent_end - end);
...@@ -912,7 +918,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans, ...@@ -912,7 +918,7 @@ int btrfs_mark_extent_written(struct btrfs_trans_handle *trans,
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);
...@@ -1155,6 +1161,20 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1155,6 +1161,20 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
page_cache_release(pinned[1]); page_cache_release(pinned[1]);
*ppos = pos; *ppos = pos;
/*
* we want to make sure fsync finds this change
* but we haven't joined a transaction running right now.
*
* Later on, someone is sure to update the inode and get the
* real transid recorded.
*
* We set last_trans now to the fs_info generation + 1,
* this will either be one more than the running transaction
* or the generation used for the next transaction if there isn't
* one running right now.
*/
BTRFS_I(inode)->last_trans = root->fs_info->generation + 1;
if (num_written > 0 && will_write) { if (num_written > 0 && will_write) {
struct btrfs_trans_handle *trans; struct btrfs_trans_handle *trans;
...@@ -1167,8 +1187,11 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1167,8 +1187,11 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
ret = btrfs_log_dentry_safe(trans, root, ret = btrfs_log_dentry_safe(trans, root,
file->f_dentry); file->f_dentry);
if (ret == 0) { if (ret == 0) {
btrfs_sync_log(trans, root); ret = btrfs_sync_log(trans, root);
btrfs_end_transaction(trans, root); if (ret == 0)
btrfs_end_transaction(trans, root);
else
btrfs_commit_transaction(trans, root);
} else { } else {
btrfs_commit_transaction(trans, root); btrfs_commit_transaction(trans, root);
} }
...@@ -1185,6 +1208,18 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf, ...@@ -1185,6 +1208,18 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
int btrfs_release_file(struct inode *inode, struct file *filp) int btrfs_release_file(struct inode *inode, struct file *filp)
{ {
/*
* ordered_data_close is set by settattr when we are about to truncate
* a file from a non-zero size to a zero size. This tries to
* flush down new bytes that may have been written if the
* application were using truncate to replace a file in place.
*/
if (BTRFS_I(inode)->ordered_data_close) {
BTRFS_I(inode)->ordered_data_close = 0;
btrfs_add_ordered_operation(NULL, BTRFS_I(inode)->root, inode);
if (inode->i_size > BTRFS_ORDERED_OPERATIONS_FLUSH_LIMIT)
filemap_flush(inode->i_mapping);
}
if (filp->private_data) if (filp->private_data)
btrfs_ioctl_trans_end(filp); btrfs_ioctl_trans_end(filp);
return 0; return 0;
...@@ -1260,8 +1295,11 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync) ...@@ -1260,8 +1295,11 @@ int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
if (ret > 0) { if (ret > 0) {
ret = btrfs_commit_transaction(trans, root); ret = btrfs_commit_transaction(trans, root);
} else { } else {
btrfs_sync_log(trans, root); ret = btrfs_sync_log(trans, root);
ret = btrfs_end_transaction(trans, root); if (ret == 0)
ret = btrfs_end_transaction(trans, root);
else
ret = btrfs_commit_transaction(trans, root);
} }
mutex_lock(&dentry->d_inode->i_mutex); mutex_lock(&dentry->d_inode->i_mutex);
out: out:
......
...@@ -73,6 +73,8 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans, ...@@ -73,6 +73,8 @@ int btrfs_del_inode_ref(struct btrfs_trans_handle *trans,
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
path->leave_spinning = 1;
ret = btrfs_search_slot(trans, root, &key, path, -1, 1); ret = btrfs_search_slot(trans, root, &key, path, -1, 1);
if (ret > 0) { if (ret > 0) {
ret = -ENOENT; ret = -ENOENT;
...@@ -127,6 +129,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans, ...@@ -127,6 +129,7 @@ int btrfs_insert_inode_ref(struct btrfs_trans_handle *trans,
if (!path) if (!path)
return -ENOMEM; return -ENOMEM;
path->leave_spinning = 1;
ret = btrfs_insert_empty_item(trans, root, path, &key, ret = btrfs_insert_empty_item(trans, root, path, &key,
ins_len); ins_len);
if (ret == -EEXIST) { if (ret == -EEXIST) {
......
此差异已折叠。
...@@ -71,12 +71,13 @@ void btrfs_clear_lock_blocking(struct extent_buffer *eb) ...@@ -71,12 +71,13 @@ void btrfs_clear_lock_blocking(struct extent_buffer *eb)
static int btrfs_spin_on_block(struct extent_buffer *eb) static int btrfs_spin_on_block(struct extent_buffer *eb)
{ {
int i; int i;
for (i = 0; i < 512; i++) { for (i = 0; i < 512; i++) {
cpu_relax();
if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags)) if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
return 1; return 1;
if (need_resched()) if (need_resched())
break; break;
cpu_relax();
} }
return 0; return 0;
} }
...@@ -95,13 +96,15 @@ int btrfs_try_spin_lock(struct extent_buffer *eb) ...@@ -95,13 +96,15 @@ int btrfs_try_spin_lock(struct extent_buffer *eb)
{ {
int i; int i;
spin_nested(eb); if (btrfs_spin_on_block(eb)) {
if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags)) spin_nested(eb);
return 1; if (!test_bit(EXTENT_BUFFER_BLOCKING, &eb->bflags))
spin_unlock(&eb->lock); return 1;
spin_unlock(&eb->lock);
}
/* spin for a bit on the BLOCKING flag */ /* spin for a bit on the BLOCKING flag */
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
cpu_relax();
if (!btrfs_spin_on_block(eb)) if (!btrfs_spin_on_block(eb))
break; break;
...@@ -148,6 +151,9 @@ int btrfs_tree_lock(struct extent_buffer *eb) ...@@ -148,6 +151,9 @@ int btrfs_tree_lock(struct extent_buffer *eb)
DEFINE_WAIT(wait); DEFINE_WAIT(wait);
wait.func = btrfs_wake_function; wait.func = btrfs_wake_function;
if (!btrfs_spin_on_block(eb))
goto sleep;
while(1) { while(1) {
spin_nested(eb); spin_nested(eb);
...@@ -165,9 +171,10 @@ int btrfs_tree_lock(struct extent_buffer *eb) ...@@ -165,9 +171,10 @@ int btrfs_tree_lock(struct extent_buffer *eb)
* spin for a bit, and if the blocking flag goes away, * spin for a bit, and if the blocking flag goes away,
* loop around * loop around
*/ */
cpu_relax();
if (btrfs_spin_on_block(eb)) if (btrfs_spin_on_block(eb))
continue; continue;
sleep:
prepare_to_wait_exclusive(&eb->lock_wq, &wait, prepare_to_wait_exclusive(&eb->lock_wq, &wait,
TASK_UNINTERRUPTIBLE); TASK_UNINTERRUPTIBLE);
......
...@@ -310,6 +310,16 @@ int btrfs_remove_ordered_extent(struct inode *inode, ...@@ -310,6 +310,16 @@ int btrfs_remove_ordered_extent(struct inode *inode,
spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock); spin_lock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
list_del_init(&entry->root_extent_list); list_del_init(&entry->root_extent_list);
/*
* we have no more ordered extents for this inode and
* no dirty pages. We can safely remove it from the
* list of ordered extents
*/
if (RB_EMPTY_ROOT(&tree->tree) &&
!mapping_tagged(inode->i_mapping, PAGECACHE_TAG_DIRTY)) {
list_del_init(&BTRFS_I(inode)->ordered_operations);
}
spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock); spin_unlock(&BTRFS_I(inode)->root->fs_info->ordered_extent_lock);
mutex_unlock(&tree->mutex); mutex_unlock(&tree->mutex);
...@@ -369,6 +379,68 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only) ...@@ -369,6 +379,68 @@ int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only)
return 0; return 0;
} }
/*
* this is used during transaction commit to write all the inodes
* added to the ordered operation list. These files must be fully on
* disk before the transaction commits.
*
* we have two modes here, one is to just start the IO via filemap_flush
* and the other is to wait for all the io. When we wait, we have an
* extra check to make sure the ordered operation list really is empty
* before we return
*/
int btrfs_run_ordered_operations(struct btrfs_root *root, int wait)
{
struct btrfs_inode *btrfs_inode;
struct inode *inode;
struct list_head splice;
INIT_LIST_HEAD(&splice);
mutex_lock(&root->fs_info->ordered_operations_mutex);
spin_lock(&root->fs_info->ordered_extent_lock);
again:
list_splice_init(&root->fs_info->ordered_operations, &splice);
while (!list_empty(&splice)) {
btrfs_inode = list_entry(splice.next, struct btrfs_inode,
ordered_operations);
inode = &btrfs_inode->vfs_inode;
list_del_init(&btrfs_inode->ordered_operations);
/*
* the inode may be getting freed (in sys_unlink path).
*/
inode = igrab(inode);
if (!wait && inode) {
list_add_tail(&BTRFS_I(inode)->ordered_operations,
&root->fs_info->ordered_operations);
}
spin_unlock(&root->fs_info->ordered_extent_lock);
if (inode) {
if (wait)
btrfs_wait_ordered_range(inode, 0, (u64)-1);
else
filemap_flush(inode->i_mapping);
iput(inode);
}
cond_resched();
spin_lock(&root->fs_info->ordered_extent_lock);
}
if (wait && !list_empty(&root->fs_info->ordered_operations))
goto again;
spin_unlock(&root->fs_info->ordered_extent_lock);
mutex_unlock(&root->fs_info->ordered_operations_mutex);
return 0;
}
/* /*
* Used to start IO or wait for a given ordered extent to finish. * Used to start IO or wait for a given ordered extent to finish.
* *
...@@ -726,3 +798,49 @@ int btrfs_wait_on_page_writeback_range(struct address_space *mapping, ...@@ -726,3 +798,49 @@ int btrfs_wait_on_page_writeback_range(struct address_space *mapping,
return ret; return ret;
} }
/*
* add a given inode to the list of inodes that must be fully on
* disk before a transaction commit finishes.
*
* This basically gives us the ext3 style data=ordered mode, and it is mostly
* used to make sure renamed files are fully on disk.
*
* It is a noop if the inode is already fully on disk.
*
* If trans is not null, we'll do a friendly check for a transaction that
* is already flushing things and force the IO down ourselves.
*/
int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct inode *inode)
{
u64 last_mod;
last_mod = max(BTRFS_I(inode)->generation, BTRFS_I(inode)->last_trans);
/*
* if this file hasn't been changed since the last transaction
* commit, we can safely return without doing anything
*/
if (last_mod < root->fs_info->last_trans_committed)
return 0;
/*
* the transaction is already committing. Just start the IO and
* don't bother with all of this list nonsense
*/
if (trans && root->fs_info->running_transaction->blocked) {
btrfs_wait_ordered_range(inode, 0, (u64)-1);
return 0;
}
spin_lock(&root->fs_info->ordered_extent_lock);
if (list_empty(&BTRFS_I(inode)->ordered_operations)) {
list_add_tail(&BTRFS_I(inode)->ordered_operations,
&root->fs_info->ordered_operations);
}
spin_unlock(&root->fs_info->ordered_extent_lock);
return 0;
}
...@@ -155,4 +155,8 @@ int btrfs_wait_on_page_writeback_range(struct address_space *mapping, ...@@ -155,4 +155,8 @@ int btrfs_wait_on_page_writeback_range(struct address_space *mapping,
int btrfs_fdatawrite_range(struct address_space *mapping, loff_t start, int btrfs_fdatawrite_range(struct address_space *mapping, loff_t start,
loff_t end, int sync_mode); loff_t end, int sync_mode);
int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only); int btrfs_wait_ordered_extents(struct btrfs_root *root, int nocow_only);
int btrfs_run_ordered_operations(struct btrfs_root *root, int wait);
int btrfs_add_ordered_operation(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct inode *inode);
#endif #endif
此差异已折叠。
...@@ -19,10 +19,16 @@ ...@@ -19,10 +19,16 @@
#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;
/*
* total writers in this transaction, it must be zero before the
* transaction can end
*/
unsigned long num_writers; unsigned long num_writers;
unsigned long num_joined; unsigned long num_joined;
int in_commit; int in_commit;
int use_count; int use_count;
...@@ -34,6 +40,7 @@ struct btrfs_transaction { ...@@ -34,6 +40,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 +51,7 @@ struct btrfs_trans_handle { ...@@ -44,6 +51,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 {
......
...@@ -124,8 +124,6 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans, ...@@ -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);
......
此差异已折叠。
...@@ -22,14 +22,9 @@ ...@@ -22,14 +22,9 @@
int btrfs_sync_log(struct btrfs_trans_handle *trans, int btrfs_sync_log(struct btrfs_trans_handle *trans,
struct btrfs_root *root); struct btrfs_root *root);
int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root); int btrfs_free_log(struct btrfs_trans_handle *trans, struct btrfs_root *root);
int btrfs_log_dentry(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct dentry *dentry);
int btrfs_recover_log_trees(struct btrfs_root *tree_root); int btrfs_recover_log_trees(struct btrfs_root *tree_root);
int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans, int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct dentry *dentry); struct btrfs_root *root, struct dentry *dentry);
int btrfs_log_inode(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
int inode_only);
int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans, int btrfs_del_dir_entries_in_log(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
const char *name, int name_len, const char *name, int name_len,
...@@ -38,4 +33,16 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans, ...@@ -38,4 +33,16 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct btrfs_root *root,
const char *name, int name_len, const char *name, int name_len,
struct inode *inode, u64 dirid); struct inode *inode, u64 dirid);
int btrfs_join_running_log_trans(struct btrfs_root *root);
int btrfs_end_log_trans(struct btrfs_root *root);
int btrfs_pin_log_trans(struct btrfs_root *root);
int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct inode *inode,
struct dentry *parent, int exists_only);
void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,
struct inode *dir, struct inode *inode,
int for_rename);
int btrfs_log_new_name(struct btrfs_trans_handle *trans,
struct inode *inode, struct inode *old_dir,
struct dentry *parent);
#endif #endif
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册