aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Mason <chris.mason@oracle.com>2008-09-05 16:13:11 -0400
committerChris Mason <chris.mason@oracle.com>2008-09-25 11:04:07 -0400
commite02119d5a7b4396c5a872582fddc8bd6d305a70a (patch)
tree825efe2a79dbca8d61256183f3526a5b5dc40dc6
parenta1b32a5932cfac7c38b442582285f3da2a09dfd8 (diff)
Btrfs: Add a write ahead tree log to optimize synchronous operations
File syncs and directory syncs are optimized by copying their items into a special (copy-on-write) log tree. There is one log tree per subvolume and the btrfs super block points to a tree of log tree roots. After a crash, items are copied out of the log tree and back into the subvolume. See tree-log.c for all the details. Signed-off-by: Chris Mason <chris.mason@oracle.com>
-rw-r--r--fs/btrfs/Makefile3
-rw-r--r--fs/btrfs/btrfs_inode.h8
-rw-r--r--fs/btrfs/compat.h15
-rw-r--r--fs/btrfs/ctree.c73
-rw-r--r--fs/btrfs/ctree.h72
-rw-r--r--fs/btrfs/dir-item.c3
-rw-r--r--fs/btrfs/disk-io.c138
-rw-r--r--fs/btrfs/disk-io.h8
-rw-r--r--fs/btrfs/extent-tree.c93
-rw-r--r--fs/btrfs/file.c39
-rw-r--r--fs/btrfs/inode.c261
-rw-r--r--fs/btrfs/root-tree.c5
-rw-r--r--fs/btrfs/transaction.c45
-rw-r--r--fs/btrfs/transaction.h1
-rw-r--r--fs/btrfs/tree-defrag.c4
-rw-r--r--fs/btrfs/tree-log.c2804
-rw-r--r--fs/btrfs/tree-log.h41
17 files changed, 3408 insertions, 205 deletions
diff --git a/fs/btrfs/Makefile b/fs/btrfs/Makefile
index 3a01065d4ef..b7addbfd8c2 100644
--- a/fs/btrfs/Makefile
+++ b/fs/btrfs/Makefile
@@ -7,8 +7,7 @@ btrfs-y := super.o ctree.o extent-tree.o print-tree.o root-tree.o dir-item.o \
transaction.o bit-radix.o inode.o file.o tree-defrag.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 \
- ref-cache.o acl.o export.o
-
+ ref-cache.o export.o tree-log.o acl.o
else
# Normal Makefile
diff --git a/fs/btrfs/btrfs_inode.h b/fs/btrfs/btrfs_inode.h
index 111f90524ae..fcc8cf27e90 100644
--- a/fs/btrfs/btrfs_inode.h
+++ b/fs/btrfs/btrfs_inode.h
@@ -33,6 +33,7 @@ struct btrfs_inode {
struct extent_io_tree io_failure_tree;
struct mutex csum_mutex;
struct mutex extent_mutex;
+ struct mutex log_mutex;
struct inode vfs_inode;
struct btrfs_ordered_inode_tree ordered_tree;
@@ -44,10 +45,17 @@ struct btrfs_inode {
struct list_head delalloc_inodes;
+ /* full 64 bit generation number */
+ u64 generation;
+
/*
* transid of the trans_handle that last modified this inode
*/
u64 last_trans;
+ /*
+ * transid that last logged this inode
+ */
+ u64 logged_trans;
u64 delalloc_bytes;
u64 disk_i_size;
u32 flags;
diff --git a/fs/btrfs/compat.h b/fs/btrfs/compat.h
index d45fb37887b..b0ed1887d9b 100644
--- a/fs/btrfs/compat.h
+++ b/fs/btrfs/compat.h
@@ -22,6 +22,21 @@ static inline struct dentry *d_obtain_alias(struct inode *inode)
}
#endif
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
+static inline void btrfs_drop_nlink(struct inode *inode)
+{
+ inode->i_nlink--;
+}
+
+static inline void btrfs_inc_nlink(struct inode *inode)
+{
+ inode->i_nlink++;
+}
+#else
+# define btrfs_drop_nlink(inode) drop_nlink(inode)
+# define btrfs_inc_nlink(inode) inc_nlink(inode)
+#endif
+
/*
* Even if AppArmor isn't enabled, it still has different prototypes.
* Add more distro/version pairs here to declare which has AppArmor applied.
diff --git a/fs/btrfs/ctree.c b/fs/btrfs/ctree.c
index 7114faafa9d..579124043d9 100644
--- a/fs/btrfs/ctree.c
+++ b/fs/btrfs/ctree.c
@@ -60,7 +60,7 @@ void btrfs_free_path(struct btrfs_path *p)
kmem_cache_free(btrfs_path_cachep, p);
}
-void btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p)
+void noinline btrfs_release_path(struct btrfs_root *root, struct btrfs_path *p)
{
int i;
@@ -176,7 +176,7 @@ int btrfs_copy_root(struct btrfs_trans_handle *trans,
return 0;
}
-int __btrfs_cow_block(struct btrfs_trans_handle *trans,
+int noinline __btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot,
@@ -294,7 +294,7 @@ int __btrfs_cow_block(struct btrfs_trans_handle *trans,
return 0;
}
-int btrfs_cow_block(struct btrfs_trans_handle *trans,
+int noinline btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot,
struct extent_buffer **cow_ret, u64 prealloc_dest)
@@ -677,9 +677,10 @@ static int noinline check_block(struct btrfs_root *root,
*
* slot may point to max if the key is bigger than all of the keys
*/
-static int generic_bin_search(struct extent_buffer *eb, unsigned long p,
- int item_size, struct btrfs_key *key,
- int max, int *slot)
+static noinline int generic_bin_search(struct extent_buffer *eb,
+ unsigned long p,
+ int item_size, struct btrfs_key *key,
+ int max, int *slot)
{
int low = 0;
int high = max;
@@ -765,7 +766,7 @@ static int bin_search(struct extent_buffer *eb, struct btrfs_key *key,
return -1;
}
-static struct extent_buffer *read_node_slot(struct btrfs_root *root,
+static noinline struct extent_buffer *read_node_slot(struct btrfs_root *root,
struct extent_buffer *parent, int slot)
{
int level = btrfs_header_level(parent);
@@ -781,7 +782,7 @@ static struct extent_buffer *read_node_slot(struct btrfs_root *root,
btrfs_node_ptr_generation(parent, slot));
}
-static int balance_level(struct btrfs_trans_handle *trans,
+static noinline int balance_level(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, int level)
{
@@ -1128,8 +1129,9 @@ static int noinline push_nodes_for_insert(struct btrfs_trans_handle *trans,
/*
* readahead one full node of leaves
*/
-static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
- int level, int slot, u64 objectid)
+static noinline void reada_for_search(struct btrfs_root *root,
+ struct btrfs_path *path,
+ int level, int slot, u64 objectid)
{
struct extent_buffer *node;
struct btrfs_disk_key disk_key;
@@ -1201,7 +1203,8 @@ static void reada_for_search(struct btrfs_root *root, struct btrfs_path *path,
}
}
-static void unlock_up(struct btrfs_path *path, int level, int lowest_unlock)
+static noinline void unlock_up(struct btrfs_path *path, int level,
+ int lowest_unlock)
{
int i;
int skip_level = level;
@@ -1759,8 +1762,9 @@ static int insert_ptr(struct btrfs_trans_handle *trans, struct btrfs_root
*
* returns 0 on success and < 0 on failure
*/
-static int split_node(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, struct btrfs_path *path, int level)
+static noinline int split_node(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_path *path, int level)
{
u64 root_gen;
struct extent_buffer *c;
@@ -1874,7 +1878,8 @@ static int leaf_space_used(struct extent_buffer *l, int start, int nr)
* the start of the leaf data. IOW, how much room
* the leaf has left for both items and data
*/
-int btrfs_leaf_free_space(struct btrfs_root *root, struct extent_buffer *leaf)
+int noinline btrfs_leaf_free_space(struct btrfs_root *root,
+ struct extent_buffer *leaf)
{
int nritems = btrfs_header_nritems(leaf);
int ret;
@@ -2283,9 +2288,11 @@ out:
*
* returns 0 if all went well and < 0 on failure.
*/
-static int split_leaf(struct btrfs_trans_handle *trans, struct btrfs_root
- *root, struct btrfs_key *ins_key,
- struct btrfs_path *path, int data_size, int extend)
+static noinline int split_leaf(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct btrfs_key *ins_key,
+ struct btrfs_path *path, int data_size,
+ int extend)
{
u64 root_gen;
struct extent_buffer *l;
@@ -3079,6 +3086,7 @@ int btrfs_prev_leaf(struct btrfs_root *root, struct btrfs_path *path)
* was nothing in the tree that matched the search criteria.
*/
int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
+ struct btrfs_key *max_key,
struct btrfs_path *path, int cache_only,
u64 min_trans)
{
@@ -3093,6 +3101,7 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
again:
cur = btrfs_lock_root_node(root);
level = btrfs_header_level(cur);
+ WARN_ON(path->nodes[level]);
path->nodes[level] = cur;
path->locks[level] = 1;
@@ -3107,6 +3116,8 @@ again:
/* at level = 0, we're done, setup the path and exit */
if (level == 0) {
+ if (slot >= nritems)
+ goto find_next_key;
ret = 0;
path->slots[level] = slot;
btrfs_item_key_to_cpu(cur, &found_key, slot);
@@ -3123,6 +3134,8 @@ again:
u64 blockptr;
u64 gen;
struct extent_buffer *tmp;
+ struct btrfs_disk_key disk_key;
+
blockptr = btrfs_node_blockptr(cur, slot);
gen = btrfs_node_ptr_generation(cur, slot);
if (gen < min_trans) {
@@ -3132,6 +3145,14 @@ again:
if (!cache_only)
break;
+ if (max_key) {
+ btrfs_node_key(cur, &disk_key, slot);
+ if (comp_keys(&disk_key, max_key) >= 0) {
+ ret = 1;
+ goto out;
+ }
+ }
+
tmp = btrfs_find_tree_block(root, blockptr,
btrfs_level_size(root, level - 1));
@@ -3143,14 +3164,16 @@ again:
free_extent_buffer(tmp);
slot++;
}
+find_next_key:
/*
* we didn't find a candidate key in this node, walk forward
* and find another one
*/
if (slot >= nritems) {
- ret = btrfs_find_next_key(root, path, min_key, level,
+ path->slots[level] = slot;
+ sret = btrfs_find_next_key(root, path, min_key, level,
cache_only, min_trans);
- if (ret == 0) {
+ if (sret == 0) {
btrfs_release_path(root, path);
goto again;
} else {
@@ -3351,6 +3374,7 @@ int btrfs_previous_item(struct btrfs_root *root,
{
struct btrfs_key found_key;
struct extent_buffer *leaf;
+ u32 nritems;
int ret;
while(1) {
@@ -3362,9 +3386,20 @@ int btrfs_previous_item(struct btrfs_root *root,
path->slots[0]--;
}
leaf = path->nodes[0];
+ nritems = btrfs_header_nritems(leaf);
+ if (nritems == 0)
+ return 1;
+ if (path->slots[0] == nritems)
+ path->slots[0]--;
+
btrfs_item_key_to_cpu(leaf, &found_key, path->slots[0]);
if (found_key.type == type)
return 0;
+ if (found_key.objectid < min_objectid)
+ break;
+ if (found_key.objectid == min_objectid &&
+ found_key.type < type)
+ break;
}
return 1;
}
diff --git a/fs/btrfs/ctree.h b/fs/btrfs/ctree.h
index b305ae7e10b..6532b60683e 100644
--- a/fs/btrfs/ctree.h
+++ b/fs/btrfs/ctree.h
@@ -77,6 +77,10 @@ struct btrfs_ordered_sum;
/* orhpan objectid for tracking unlinked/truncated files */
#define BTRFS_ORPHAN_OBJECTID -5ULL
+/* does write ahead logging to speed up fsyncs */
+#define BTRFS_TREE_LOG_OBJECTID -6ULL
+#define BTRFS_TREE_LOG_FIXUP_OBJECTID -7ULL
+
/*
* All files have objectids higher than this.
*/
@@ -276,6 +280,7 @@ struct btrfs_super_block {
__le64 generation;
__le64 root;
__le64 chunk_root;
+ __le64 log_root;
__le64 total_bytes;
__le64 bytes_used;
__le64 root_dir_objectid;
@@ -287,6 +292,7 @@ struct btrfs_super_block {
__le32 sys_chunk_array_size;
u8 root_level;
u8 chunk_root_level;
+ u8 log_root_level;
struct btrfs_dev_item dev_item;
char label[BTRFS_LABEL_SIZE];
u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
@@ -392,7 +398,10 @@ struct btrfs_timespec {
* make a new item type
*/
struct btrfs_inode_item {
+ /* nfs style generation number */
__le64 generation;
+ /* transid that last touched this inode */
+ __le64 transid;
__le64 size;
__le64 nblocks;
__le64 block_group;
@@ -409,8 +418,13 @@ struct btrfs_inode_item {
struct btrfs_timespec otime;
} __attribute__ ((__packed__));
+struct btrfs_dir_log_item {
+ __le64 end;
+} __attribute__ ((__packed__));
+
struct btrfs_dir_item {
struct btrfs_disk_key location;
+ __le64 transid;
__le16 data_len;
__le16 name_len;
u8 type;
@@ -505,6 +519,9 @@ struct btrfs_fs_info {
struct btrfs_root *tree_root;
struct btrfs_root *chunk_root;
struct btrfs_root *dev_root;
+
+ /* the log root tree is a directory of all the other log roots */
+ struct btrfs_root *log_root_tree;
struct radix_tree_root fs_roots_radix;
struct extent_io_tree free_space_cache;
@@ -518,6 +535,7 @@ struct btrfs_fs_info {
u64 generation;
u64 last_trans_committed;
+ u64 last_trans_new_blockgroup;
u64 open_ioctl_trans;
unsigned long mount_opt;
u64 max_extent;
@@ -527,6 +545,9 @@ struct btrfs_fs_info {
wait_queue_head_t transaction_throttle;
wait_queue_head_t transaction_wait;
wait_queue_head_t async_submit_wait;
+
+ wait_queue_head_t tree_log_wait;
+
struct btrfs_super_block super_copy;
struct btrfs_super_block super_for_commit;
struct block_device *__bdev;
@@ -535,6 +556,7 @@ struct btrfs_fs_info {
struct backing_dev_info bdi;
spinlock_t hash_lock;
struct mutex trans_mutex;
+ struct mutex tree_log_mutex;
struct mutex transaction_kthread_mutex;
struct mutex cleaner_mutex;
struct mutex alloc_mutex;
@@ -544,8 +566,13 @@ struct btrfs_fs_info {
struct list_head trans_list;
struct list_head hashers;
struct list_head dead_roots;
+
atomic_t nr_async_submits;
atomic_t nr_async_bios;
+ atomic_t tree_log_writers;
+ atomic_t tree_log_commit;
+ unsigned long tree_log_batch;
+ u64 tree_log_transid;
/*
* this is used by the balancing code to wait for all the pending
@@ -583,6 +610,7 @@ struct btrfs_fs_info {
struct completion kobj_unregister;
int do_barriers;
int closing;
+ int log_root_recovering;
atomic_t throttles;
atomic_t throttle_gen;
@@ -596,6 +624,7 @@ struct btrfs_fs_info {
u64 delalloc_bytes;
u64 last_alloc;
u64 last_data_alloc;
+ u64 last_log_alloc;
spinlock_t ref_cache_lock;
u64 total_ref_cache_size;
@@ -632,6 +661,7 @@ struct btrfs_root {
struct btrfs_leaf_ref_tree *ref_tree;
struct btrfs_leaf_ref_tree ref_tree_struct;
struct btrfs_dirty_root *dirty_root;
+ struct btrfs_root *log_root;
struct btrfs_root_item root_item;
struct btrfs_key root_key;
@@ -640,6 +670,7 @@ struct btrfs_root {
struct kobject root_kobj;
struct completion kobj_unregister;
struct mutex objectid_mutex;
+ struct mutex log_mutex;
u64 objectid;
u64 last_trans;
@@ -692,6 +723,8 @@ struct btrfs_root {
* dir items are the name -> inode pointers in a directory. There is one
* for every name in a directory.
*/
+#define BTRFS_DIR_LOG_ITEM_KEY 14
+#define BTRFS_DIR_LOG_INDEX_KEY 15
#define BTRFS_DIR_ITEM_KEY 16
#define BTRFS_DIR_INDEX_KEY 17
/*
@@ -703,7 +736,8 @@ struct btrfs_root {
*/
#define BTRFS_CSUM_ITEM_KEY 19
-/* reserve 20-31 for other file stuff */
+
+/* reserve 21-31 for other file/dir stuff */
/*
* root items point to tree roots. There are typically in the root
@@ -938,6 +972,7 @@ BTRFS_SETGET_FUNCS(inode_ref_index, struct btrfs_inode_ref, index, 64);
/* struct btrfs_inode_item */
BTRFS_SETGET_FUNCS(inode_generation, struct btrfs_inode_item, generation, 64);
+BTRFS_SETGET_FUNCS(inode_transid, struct btrfs_inode_item, transid, 64);
BTRFS_SETGET_FUNCS(inode_size, struct btrfs_inode_item, size, 64);
BTRFS_SETGET_FUNCS(inode_nblocks, struct btrfs_inode_item, nblocks, 64);
BTRFS_SETGET_FUNCS(inode_block_group, struct btrfs_inode_item, block_group, 64);
@@ -1126,10 +1161,13 @@ static inline void btrfs_set_item_key(struct extent_buffer *eb,
write_eb_member(eb, item, struct btrfs_item, key, disk_key);
}
+BTRFS_SETGET_FUNCS(dir_log_end, struct btrfs_dir_log_item, end, 64);
+
/* struct btrfs_dir_item */
BTRFS_SETGET_FUNCS(dir_data_len, struct btrfs_dir_item, data_len, 16);
BTRFS_SETGET_FUNCS(dir_type, struct btrfs_dir_item, type, 8);
BTRFS_SETGET_FUNCS(dir_name_len, struct btrfs_dir_item, name_len, 16);
+BTRFS_SETGET_FUNCS(dir_transid, struct btrfs_dir_item, transid, 64);
static inline void btrfs_dir_item_key(struct extent_buffer *eb,
struct btrfs_dir_item *item,
@@ -1301,7 +1339,11 @@ BTRFS_SETGET_STACK_FUNCS(super_root_level, struct btrfs_super_block,
BTRFS_SETGET_STACK_FUNCS(super_chunk_root, struct btrfs_super_block,
chunk_root, 64);
BTRFS_SETGET_STACK_FUNCS(super_chunk_root_level, struct btrfs_super_block,
- chunk_root_level, 64);
+ chunk_root_level, 8);
+BTRFS_SETGET_STACK_FUNCS(super_log_root, struct btrfs_super_block,
+ log_root, 64);
+BTRFS_SETGET_STACK_FUNCS(super_log_root_level, struct btrfs_super_block,
+ log_root_level, 8);
BTRFS_SETGET_STACK_FUNCS(super_total_bytes, struct btrfs_super_block,
total_bytes, 64);
BTRFS_SETGET_STACK_FUNCS(super_bytes_used, struct btrfs_super_block,
@@ -1405,6 +1447,12 @@ static inline struct dentry *fdentry(struct file *file) {
}
/* extent-tree.c */
+int btrfs_lookup_extent(struct btrfs_root *root, struct btrfs_path *path,
+ u64 start, u64 len);
+int btrfs_update_pinned_extents(struct btrfs_root *root,
+ u64 bytenr, u64 num, int pin);
+int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root, struct extent_buffer *leaf);
int btrfs_cross_ref_exists(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_key *key, u64 bytenr);
@@ -1448,6 +1496,11 @@ int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
u64 root_objectid, u64 ref_generation,
u64 owner, u64 owner_offset,
struct btrfs_key *ins);
+int btrfs_alloc_logged_extent(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ u64 root_objectid, u64 ref_generation,
+ u64 owner, u64 owner_offset,
+ struct btrfs_key *ins);
int btrfs_reserve_extent(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
u64 num_bytes, u64 min_alloc_size,
@@ -1488,9 +1541,9 @@ int btrfs_find_next_key(struct btrfs_root *root, struct btrfs_path *path,
struct btrfs_key *key, int lowest_level,
int cache_only, u64 min_trans);
int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
+ struct btrfs_key *max_key,
struct btrfs_path *path, int cache_only,
u64 min_trans);
-
int btrfs_cow_block(struct btrfs_trans_handle *trans,
struct btrfs_root *root, struct extent_buffer *buf,
struct extent_buffer *parent, int parent_slot,
@@ -1656,6 +1709,18 @@ int btrfs_csum_truncate(struct btrfs_trans_handle *trans,
#define PageChecked PageFsMisc
#endif
+int btrfs_unlink_inode(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode *dir, struct inode *inode,
+ const char *name, int name_len);
+int btrfs_add_link(struct btrfs_trans_handle *trans,
+ struct inode *parent_inode, struct inode *inode,
+ const char *name, int name_len, int add_backref, u64 index);
+int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
+ struct btrfs_root *root,
+ struct inode *inode, u64 new_size,
+ u32 min_type);
+
int btrfs_start_delalloc_inodes(struct btrfs_root *root);
int btrfs_set_extent_delalloc(struct inode *inode, u64 start, u64 end);
int btrfs_writepages(struct address_space *mapping,
@@ -1715,6 +1780,7 @@ int btrfs_update_inode(struct btrfs_trans_handle *trans,
long btrfs_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
/* file.c */
+int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync);
int btrfs_drop_extent_cache(struct inode *inode, u64 start, u64 end);
int btrfs_check_file(struct btrfs_root *root, struct inode *inode);
extern struct file_operations btrfs_file_operations;
diff --git a/fs/btrfs/dir-item.c b/fs/btrfs/dir-item.c
index 125094617fe..e4f30090d64 100644
--- a/fs/btrfs/dir-item.c
+++ b/fs/btrfs/dir-item.c
@@ -96,6 +96,7 @@ int btrfs_insert_xattr_item(struct btrfs_trans_handle *trans,
btrfs_set_dir_item_key(leaf, dir_item, &disk_key);
btrfs_set_dir_type(leaf, dir_item, BTRFS_FT_XATTR);
btrfs_set_dir_name_len(leaf, dir_item, name_len);
+ btrfs_set_dir_transid(leaf, dir_item, trans->transid);
btrfs_set_dir_data_len(leaf, dir_item, data_len);
name_ptr = (unsigned long)(dir_item + 1);
data_ptr = (unsigned long)((char *)name_ptr + name_len);
@@ -142,6 +143,7 @@ int btrfs_insert_dir_item(struct btrfs_trans_handle *trans, struct btrfs_root
btrfs_set_dir_type(leaf, dir_item, type);
btrfs_set_dir_data_len(leaf, dir_item, 0);
btrfs_set_dir_name_len(leaf, dir_item, name_len);
+ btrfs_set_dir_transid(leaf, dir_item, trans->transid);
name_ptr = (unsigned long)(dir_item + 1);
write_extent_buffer(leaf, name, name_ptr, name_len);
@@ -169,6 +171,7 @@ second_insert:
btrfs_set_dir_type(leaf, dir_item, type);
btrfs_set_dir_data_len(leaf, dir_item, 0);
btrfs_set_dir_name_len(leaf, dir_item, name_len);
+ btrfs_set_dir_transid(leaf, dir_item, trans->transid);
name_ptr = (unsigned long)(dir_item + 1);
write_extent_buffer(leaf, name, name_ptr, name_len);
btrfs_mark_buffer_dirty(leaf);
diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 8e7a938bfbc..a4373db5967 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -41,6 +41,7 @@
#include "async-thread.h"
#include "locking.h"
#include "ref-cache.h"
+#include "tree-log.h"
#if 0
static int check_tree_block(struct btrfs_root *root, struct extent_buffer *buf)
@@ -694,6 +695,18 @@ struct extent_buffer *btrfs_find_create_tree_block(struct btrfs_root *root,
}
+int btrfs_write_tree_block(struct extent_buffer *buf)
+{
+ return btrfs_fdatawrite_range(buf->first_page->mapping, buf->start,
+ buf->start + buf->len - 1, WB_SYNC_NONE);
+}
+
+int btrfs_wait_tree_block_writeback(struct extent_buffer *buf)
+{
+ return btrfs_wait_on_page_writeback_range(buf->first_page->mapping,
+ buf->start, buf->start + buf->len -1);
+}
+
struct extent_buffer *read_tree_block(struct btrfs_root *root, u64 bytenr,
u32 blocksize, u64 parent_transid)
{
@@ -732,15 +745,6 @@ int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
return 0;
}
-int wait_on_tree_block_writeback(struct btrfs_root *root,
- struct extent_buffer *buf)
-{
- struct inode *btree_inode = root->fs_info->btree_inode;
- wait_on_extent_buffer_writeback(&BTRFS_I(btree_inode)->io_tree,
- buf);
- return 0;
-}
-
static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
u32 stripesize, struct btrfs_root *root,
struct btrfs_fs_info *fs_info,
@@ -771,6 +775,7 @@ static int __setup_root(u32 nodesize, u32 leafsize, u32 sectorsize,
spin_lock_init(&root->node_lock);
spin_lock_init(&root->list_lock);
mutex_init(&root->objectid_mutex);
+ mutex_init(&root->log_mutex);
btrfs_leaf_ref_tree_init(&root->ref_tree_struct);
root->ref_tree = &root->ref_tree_struct;
@@ -809,11 +814,74 @@ static int find_and_setup_root(struct btrfs_root *tree_root,
return 0;
}
-struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
- struct btrfs_key *location)
+int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info)
+{
+ struct extent_buffer *eb;
+ int ret;
+
+ if (!fs_info->log_root_tree)
+ return 0;
+
+ eb = fs_info->log_root_tree->node;
+
+ WARN_ON(btrfs_header_level(eb) != 0);
+ WARN_ON(btrfs_header_nritems(eb) != 0);
+
+ ret = btrfs_free_extent(trans, fs_info->tree_root,
+ eb->start, eb->len,
+ BTRFS_TREE_LOG_OBJECTID, 0, 0, 0, 1);
+ BUG_ON(ret);
+
+ free_extent_buffer(eb);
+ kfree(fs_info->log_root_tree);
+ fs_info->log_root_tree = NULL;
+ return 0;
+}
+
+int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info)
{
struct btrfs_root *root;
struct btrfs_root *tree_root = fs_info->tree_root;
+
+ root = kzalloc(sizeof(*root), GFP_NOFS);
+ if (!root)
+ return -ENOMEM;
+
+ __setup_root(tree_root->nodesize, tree_root->leafsize,
+ tree_root->sectorsize, tree_root->stripesize,
+ root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+
+ root->root_key.objectid = BTRFS_TREE_LOG_OBJECTID;
+ root->root_key.type = BTRFS_ROOT_ITEM_KEY;
+ root->root_key.offset = BTRFS_TREE_LOG_OBJECTID;
+ root->ref_cows = 0;
+
+ root->node = btrfs_alloc_free_block(trans, root, root->leafsize,
+ BTRFS_TREE_LOG_OBJECTID,
+ 0, 0, 0, 0, 0);
+
+ btrfs_set_header_nritems(root->node, 0);
+ btrfs_set_header_level(root->node, 0);
+ btrfs_set_header_bytenr(root->node, root->node->start);
+ btrfs_set_header_generation(root->node, trans->transid);
+ btrfs_set_header_owner(root->node, BTRFS_TREE_LOG_OBJECTID);
+
+ write_extent_buffer(root->node, root->fs_info->fsid,
+ (unsigned long)btrfs_header_fsid(root->node),
+ BTRFS_FSID_SIZE);
+ btrfs_mark_buffer_dirty(root->node);
+ btrfs_tree_unlock(root->node);
+ fs_info->log_root_tree = root;
+ return 0;
+}
+
+struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
+ struct btrfs_key *location)
+{
+ struct btrfs_root *root;
+ struct btrfs_fs_info *fs_info = tree_root->fs_info;
struct btrfs_path *path;
struct extent_buffer *l;
u64 highest_inode;
@@ -863,11 +931,13 @@ out:
blocksize, 0);
BUG_ON(!root->node);
insert:
- root->ref_cows = 1;
- ret = btrfs_find_highest_inode(root, &highest_inode);
- if (ret == 0) {
- root->highest_inode = highest_inode;
- root->last_inode_alloc = highest_inode;
+ if (location->objectid != BTRFS_TREE_LOG_OBJECTID) {
+ root->ref_cows = 1;
+ ret = btrfs_find_highest_inode(root, &highest_inode);
+ if (ret == 0) {
+ root->highest_inode = highest_inode;
+ root->last_inode_alloc = highest_inode;
+ }
}
return root;
}
@@ -907,7 +977,7 @@ struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
if (root)
return root;
- root = btrfs_read_fs_root_no_radix(fs_info, location);
+ root = btrfs_read_fs_root_no_radix(fs_info->tree_root, location);
if (IS_ERR(root))
return root;
ret = radix_tree_insert(&fs_info->fs_roots_radix,
@@ -1250,16 +1320,18 @@ struct btrfs_root *open_ctree(struct super_block *sb,
u32 blocksize;
u32 stripesize;
struct buffer_head *bh;
- struct btrfs_root *extent_root = kmalloc(sizeof(struct btrfs_root),
+ struct btrfs_root *extent_root = kzalloc(sizeof(struct btrfs_root),
GFP_NOFS);
- struct btrfs_root *tree_root = kmalloc(sizeof(struct btrfs_root),
+ struct btrfs_root *tree_root = kzalloc(sizeof(struct btrfs_root),
GFP_NOFS);
struct btrfs_fs_info *fs_info = kzalloc(sizeof(*fs_info),
GFP_NOFS);
- struct btrfs_root *chunk_root = kmalloc(sizeof(struct btrfs_root),
+ struct btrfs_root *chunk_root = kzalloc(sizeof(struct btrfs_root),
GFP_NOFS);
- struct btrfs_root *dev_root = kmalloc(sizeof(struct btrfs_root),
+ struct btrfs_root *dev_root = kzalloc(sizeof(struct btrfs_root),
GFP_NOFS);
+ struct btrfs_root *log_tree_root;
+
int ret;
int err = -EINVAL;
@@ -1343,6 +1415,7 @@ struct btrfs_root *open_ctree(struct super_block *sb,
mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS);
mutex_init(&fs_info->trans_mutex);
+ mutex_init(&fs_info->tree_log_mutex);
mutex_init(&fs_info->drop_mutex);
mutex_init(&fs_info->alloc_mutex);
mutex_init(&fs_info->chunk_mutex);
@@ -1352,6 +1425,10 @@ struct btrfs_root *open_ctree(struct super_block *sb,
init_waitqueue_head(&fs_info->transaction_throttle);
init_waitqueue_head(&fs_info->transaction_wait);
init_waitqueue_head(&fs_info->async_submit_wait);
+ init_waitqueue_head(&fs_info->tree_log_wait);
+ atomic_set(&fs_info->tree_log_commit, 0);
+ atomic_set(&fs_info->tree_log_writers, 0);
+ fs_info->tree_log_transid = 0;
#if 0
ret = add_hasher(fs_info, "crc32c");
@@ -1532,7 +1609,26 @@ struct btrfs_root *open_ctree(struct super_block *sb,
if (!fs_info->transaction_kthread)
goto fail_cleaner;
+ if (btrfs_super_log_root(disk_super) != 0) {
+ u32 blocksize;
+ u64 bytenr = btrfs_super_log_root(disk_super);
+
+ blocksize =
+ btrfs_level_size(tree_root,
+ btrfs_super_log_root_level(disk_super));
+ log_tree_root = kzalloc(sizeof(struct btrfs_root),
+ GFP_NOFS);
+
+ __setup_root(nodesize, leafsize, sectorsize, stripesize,
+ log_tree_root, fs_info, BTRFS_TREE_LOG_OBJECTID);
+
+ log_tree_root->node = read_tree_block(tree_root, bytenr,
+ blocksize, 0);
+ ret = btrfs_recover_log_trees(log_tree_root);
+ BUG_ON(ret);
+ }
+ fs_info->last_trans_committed = btrfs_super_generation(disk_super);
return tree_root;
fail_cleaner:
diff --git a/fs/btrfs/disk-io.h b/fs/btrfs/disk-io.h
index 2562a273ae1..6b6fdc697f3 100644
--- a/fs/btrfs/disk-io.h
+++ b/fs/btrfs/disk-io.h
@@ -45,7 +45,7 @@ struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
struct btrfs_root *btrfs_read_fs_root(struct btrfs_fs_info *fs_info,
struct btrfs_key *location,
const char *name, int namelen);
-struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
+struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_root *tree_root,
struct btrfs_key *location);
struct btrfs_root *btrfs_read_fs_root_no_name(struct btrfs_fs_info *fs_info,
struct btrfs_key *location);
@@ -74,4 +74,10 @@ int btrfs_wq_submit_bio(struct btrfs_fs_info *fs_info, struct inode *inode,
extent_submit_bio_hook_t *submit_bio_hook);
int btrfs_congested_async(struct btrfs_fs_info *info, int iodone);
unsigned long btrfs_async_submit_limit(struct btrfs_fs_info *info);
+int btrfs_write_tree_block(struct extent_buffer *buf);
+int btrfs_wait_tree_block_writeback(struct extent_buffer *buf);
+int btrfs_free_log_root_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info);
+int btrfs_init_log_root_tree(struct btrfs_trans_handle *trans,
+ struct btrfs_fs_info *fs_info);
#endif
diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index e63b3b4bed7..646b9148ca2 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -496,6 +496,23 @@ static int match_extent_ref(struct extent_buffer *leaf,
return ret == 0;
}
+/* simple helper to search for an existing extent at a given offset */
+int btrfs_lookup_extent(struct btrfs_root *root, struct btrfs_path *path,
+ u64 start, u64 len)
+{
+ int ret;
+ struct btrfs_key key;
+
+ maybe_lock_mutex(root);
+ key.objectid = start;
+ key.offset = len;
+ btrfs_set_key_type(&key, BTRFS_EXTENT_ITEM_KEY);
+ ret = btrfs_search_slot(NULL, root->fs_info->extent_root, &key, path,
+ 0, 0);
+ maybe_unlock_mutex(root);
+ return ret;
+}
+
static int noinline lookup_extent_backref(struct btrfs_trans_handle *trans,
struct btrfs_root *root,
struct btrfs_path *path, u64 bytenr,
@@ -1409,7 +1426,7 @@ static u64 first_logical_byte(struct btrfs_root *root, u64 search_start)
}
-static int update_pinned_extents(struct btrfs_root *root,
+int btrfs_update_pinned_extents(struct btrfs_root *root,
u64 bytenr, u64 num, int pin)
{
u64 len;
@@ -1492,7 +1509,7 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans,
EXTENT_DIRTY);
if (ret)
break;
- update_pinned_extents(root, start, end + 1 - start, 0);
+ btrfs_update_pinned_extents(root, start, end + 1 - start, 0);
clear_extent_dirty(unpin, start, end, GFP_NOFS);
set_extent_dirty(free_space_cache, start, end, GFP_NOFS);
if (need_resched()) {
@@ -1538,14 +1555,11 @@ static int finish_current_insert(struct btrfs_trans_handle *trans,
clear_extent_bits(&info->extent_ins, start, end, EXTENT_LOCKED,
GFP_NOFS);
- eb = btrfs_find_tree_block(extent_root, ins.objectid,
+ eb = btrfs_find_create_tree_block(extent_root, ins.objectid,
ins.offset);
- if (!btrfs_buffer_uptodate(eb, trans->transid)) {
- mutex_unlock(&extent_root->fs_info->alloc_mutex);
+ if (!btrfs_buffer_uptodate(eb, trans->transid))
btrfs_read_buffer(eb, trans->transid);
- mutex_lock(&extent_root->fs_info->alloc_mutex);
- }
btrfs_tree_lock(eb);
level = btrfs_header_level(eb);
@@ -1585,13 +1599,20 @@ static int pin_down_bytes(struct btrfs_root *root, u64 bytenr, u32 num_bytes,
struct extent_buffer *buf;
buf = btrfs_find_tree_block(root, bytenr, num_bytes);
if (buf) {
+ /* we can reuse a block if it hasn't been written
+ * and it is from this transaction. We can't
+ * reuse anything from the tree log root because
+ * it has tiny sub-transactions.
+ */
if (btrfs_buffer_uptodate(buf, 0) &&