Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e02119d5 authored by Chris Mason's avatar Chris Mason
Browse files

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: default avatarChris Mason <chris.mason@oracle.com>
parent a1b32a59
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -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
+8 −0
Original line number Diff line number Diff line
@@ -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;
+15 −0
Original line number Diff line number Diff line
@@ -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.
+54 −19
Original line number Diff line number Diff line
@@ -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,7 +677,8 @@ 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,
static noinline int generic_bin_search(struct extent_buffer *eb,
				       unsigned long p,
				       int item_size, struct btrfs_key *key,
				       int max, int *slot)
{
@@ -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,7 +1129,8 @@ 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,
static noinline void reada_for_search(struct btrfs_root *root,
				      struct btrfs_path *path,
				      int level, int slot, u64 objectid)
{
	struct extent_buffer *node;
@@ -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 @@ static int push_leaf_left(struct btrfs_trans_handle *trans, struct btrfs_root
 *
 * 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 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,

		/* 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 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
			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 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
			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 @@ int btrfs_search_forward(struct btrfs_root *root, struct btrfs_key *min_key,
				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;
}
+69 −3
Original line number Diff line number Diff line
@@ -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;
Loading