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>
This commit is contained in:
parent
a1b32a5932
commit
e02119d5a7
17 changed files with 3408 additions and 205 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 @@ 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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 @@ 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);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
|
|
|
@ -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 @@ struct btrfs_root *btrfs_read_fs_root_no_radix(struct btrfs_fs_info *fs_info,
|
|||
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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) &&
|
||||
btrfs_try_tree_lock(buf)) {
|
||||
u64 transid =
|
||||
root->fs_info->running_transaction->transid;
|
||||
u64 header_transid =
|
||||
btrfs_header_generation(buf);
|
||||
if (header_transid == transid &&
|
||||
if (btrfs_header_owner(buf) !=
|
||||
BTRFS_TREE_LOG_OBJECTID &&
|
||||
header_transid == transid &&
|
||||
!btrfs_header_flag(buf,
|
||||
BTRFS_HEADER_FLAG_WRITTEN)) {
|
||||
clean_tree_block(NULL, root, buf);
|
||||
|
@ -1603,7 +1624,7 @@ static int pin_down_bytes(struct btrfs_root *root, u64 bytenr, u32 num_bytes,
|
|||
}
|
||||
free_extent_buffer(buf);
|
||||
}
|
||||
update_pinned_extents(root, bytenr, num_bytes, 1);
|
||||
btrfs_update_pinned_extents(root, bytenr, num_bytes, 1);
|
||||
} else {
|
||||
set_extent_bits(&root->fs_info->pending_del,
|
||||
bytenr, bytenr + num_bytes - 1,
|
||||
|
@ -1801,7 +1822,7 @@ static int del_pending_extents(struct btrfs_trans_handle *trans, struct
|
|||
GFP_NOFS);
|
||||
if (!test_range_bit(&extent_root->fs_info->extent_ins,
|
||||
start, end, EXTENT_LOCKED, 0)) {
|
||||
update_pinned_extents(extent_root, start,
|
||||
btrfs_update_pinned_extents(extent_root, start,
|
||||
end + 1 - start, 1);
|
||||
ret = __free_extent(trans, extent_root,
|
||||
start, end + 1 - start,
|
||||
|
@ -1919,6 +1940,12 @@ static int noinline find_free_extent(struct btrfs_trans_handle *trans,
|
|||
if ((data & BTRFS_BLOCK_GROUP_DATA) && btrfs_test_opt(root, SSD)) {
|
||||
last_ptr = &root->fs_info->last_data_alloc;
|
||||
}
|
||||
if (root->root_key.objectid == BTRFS_TREE_LOG_OBJECTID) {
|
||||
last_ptr = &root->fs_info->last_log_alloc;
|
||||
if (!last_ptr == 0 && root->fs_info->last_alloc) {
|
||||
*last_ptr = root->fs_info->last_alloc + empty_cluster;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_ptr) {
|
||||
if (*last_ptr)
|
||||
|
@ -2268,6 +2295,35 @@ int btrfs_alloc_reserved_extent(struct btrfs_trans_handle *trans,
|
|||
maybe_unlock_mutex(root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* this is used by the tree logging recovery code. It records that
|
||||
* an extent has been allocated and makes sure to clear the free
|
||||
* space cache bits as well
|
||||
*/
|
||||
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 ret;
|
||||
struct btrfs_block_group_cache *block_group;
|
||||
|
||||
maybe_lock_mutex(root);
|
||||
block_group = btrfs_lookup_block_group(root->fs_info, ins->objectid);
|
||||
cache_block_group(root, block_group);
|
||||
|
||||
clear_extent_dirty(&root->fs_info->free_space_cache,
|
||||
ins->objectid, ins->objectid + ins->offset - 1,
|
||||
GFP_NOFS);
|
||||
ret = __btrfs_alloc_reserved_extent(trans, root, root_objectid,
|
||||
ref_generation, owner,
|
||||
owner_offset, ins);
|
||||
maybe_unlock_mutex(root);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* finds a free extent and does all the dirty work required for allocation
|
||||
* returns the key for the extent through ins, and a tree buffer for
|
||||
|
@ -2350,9 +2406,8 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans,
|
|||
return buf;
|
||||
}
|
||||
|
||||
static int noinline drop_leaf_ref_no_cache(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct extent_buffer *leaf)
|
||||
int btrfs_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root, struct extent_buffer *leaf)
|
||||
{
|
||||
u64 leaf_owner;
|
||||
u64 leaf_generation;
|
||||
|
@ -2402,9 +2457,9 @@ static int noinline drop_leaf_ref_no_cache(struct btrfs_trans_handle *trans,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int noinline drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_leaf_ref *ref)
|
||||
static int noinline cache_drop_leaf_ref(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct btrfs_leaf_ref *ref)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
|
@ -2512,7 +2567,7 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
|
|||
btrfs_header_nritems(cur))
|
||||
break;
|
||||
if (*level == 0) {
|
||||
ret = drop_leaf_ref_no_cache(trans, root, cur);
|
||||
ret = btrfs_drop_leaf_ref(trans, root, cur);
|
||||
BUG_ON(ret);
|
||||
break;
|
||||
}
|
||||
|
@ -2552,7 +2607,7 @@ static int noinline walk_down_tree(struct btrfs_trans_handle *trans,
|
|||
btrfs_node_key_to_cpu(cur, &key, path->slots[*level]);
|
||||
ref = btrfs_lookup_leaf_ref(root, bytenr);
|
||||
if (ref) {
|
||||
ret = drop_leaf_ref(trans, root, ref);
|
||||
ret = cache_drop_leaf_ref(trans, root, ref);
|
||||
BUG_ON(ret);
|
||||
btrfs_remove_leaf_ref(root, ref);
|
||||
btrfs_free_leaf_ref(root, ref);
|
||||
|
@ -3628,6 +3683,8 @@ int btrfs_make_block_group(struct btrfs_trans_handle *trans,
|
|||
extent_root = root->fs_info->extent_root;
|
||||
block_group_cache = &root->fs_info->block_group_cache;
|
||||
|
||||
root->fs_info->last_trans_new_blockgroup = trans->transid;
|
||||
|
||||
cache = kzalloc(sizeof(*cache), GFP_NOFS);
|
||||
BUG_ON(!cache);
|
||||
cache->key.objectid = chunk_offset;
|
||||
|
|
|
@ -36,6 +36,8 @@
|
|||
#include "btrfs_inode.h"
|
||||
#include "ioctl.h"
|
||||
#include "print-tree.h"
|
||||
#include "tree-log.h"
|
||||
#include "locking.h"
|
||||
#include "compat.h"
|
||||
|
||||
|
||||
|
@ -988,10 +990,27 @@ static ssize_t btrfs_file_write(struct file *file, const char __user *buf,
|
|||
*ppos = pos;
|
||||
|
||||
if (num_written > 0 && ((file->f_flags & O_SYNC) || IS_SYNC(inode))) {
|
||||
err = sync_page_range(inode, inode->i_mapping,
|
||||
start_pos, num_written);
|
||||
struct btrfs_trans_handle *trans;
|
||||
|
||||
err = btrfs_fdatawrite_range(inode->i_mapping, start_pos,
|
||||
start_pos + num_written -1,
|
||||
WB_SYNC_NONE);
|
||||
if (err < 0)
|
||||
num_written = err;
|
||||
|
||||
err = btrfs_wait_on_page_writeback_range(inode->i_mapping,
|
||||
start_pos, start_pos + num_written - 1);
|
||||
if (err < 0)
|
||||
num_written = err;
|
||||
|
||||
trans = btrfs_start_transaction(root, 1);
|
||||
ret = btrfs_log_dentry_safe(trans, root, file->f_dentry);
|
||||
if (ret == 0) {
|
||||
btrfs_sync_log(trans, root);
|
||||
btrfs_end_transaction(trans, root);
|
||||
} else {
|
||||
btrfs_commit_transaction(trans, root);
|
||||
}
|
||||
} else if (num_written > 0 && (file->f_flags & O_DIRECT)) {
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,22)
|
||||
do_sync_file_range(file, start_pos,
|
||||
|
@ -1019,8 +1038,7 @@ int btrfs_release_file(struct inode * inode, struct file * filp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_sync_file(struct file *file,
|
||||
struct dentry *dentry, int datasync)
|
||||
int btrfs_sync_file(struct file *file, struct dentry *dentry, int datasync)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct btrfs_root *root = BTRFS_I(inode)->root;
|
||||
|
@ -1043,6 +1061,8 @@ static int btrfs_sync_file(struct file *file,
|
|||
}
|
||||
mutex_unlock(&root->fs_info->trans_mutex);
|
||||
|
||||
filemap_fdatawait(inode->i_mapping);
|
||||
|
||||
/*
|
||||
* ok we haven't committed the transaction yet, lets do a commit
|
||||
*/
|
||||
|
@ -1054,7 +1074,16 @@ static int btrfs_sync_file(struct file *file,
|
|||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
ret = btrfs_commit_transaction(trans, root);
|
||||
|
||||
ret = btrfs_log_dentry_safe(trans, root, file->f_dentry);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret > 0) {
|
||||
ret = btrfs_commit_transaction(trans, root);
|
||||
} else {
|
||||
btrfs_sync_log(trans, root);
|
||||
ret = btrfs_end_transaction(trans, root);
|
||||
}
|
||||
out:
|
||||
return ret > 0 ? EIO : ret;
|
||||
}
|
||||
|
|
261
fs/btrfs/inode.c
261
fs/btrfs/inode.c
|
@ -46,6 +46,8 @@
|
|||
#include "volumes.h"
|
||||
#include "ordered-data.h"
|
||||
#include "xattr.h"
|
||||
#include "compat.h"
|
||||
#include "tree-log.h"
|
||||
|
||||
struct btrfs_iget_args {
|
||||
u64 ino;
|
||||
|
@ -586,6 +588,7 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
|
|||
&ordered_extent->list);
|
||||
|
||||
btrfs_ordered_update_i_size(inode, ordered_extent);
|
||||
btrfs_update_inode(trans, root, inode);
|
||||
btrfs_remove_ordered_extent(inode, ordered_extent);
|
||||
|
||||
/* once for us */
|
||||
|
@ -593,7 +596,6 @@ static int btrfs_finish_ordered_io(struct inode *inode, u64 start, u64 end)
|
|||
/* once for the tree */
|
||||
btrfs_put_ordered_extent(ordered_extent);
|
||||
|
||||
btrfs_update_inode(trans, root, inode);
|
||||
btrfs_end_transaction(trans, root);
|
||||
return 0;
|
||||
}
|
||||
|
@ -1007,7 +1009,8 @@ void btrfs_read_locked_inode(struct inode *inode)
|
|||
inode->i_ctime.tv_nsec = btrfs_timespec_nsec(leaf, tspec);
|
||||
|
||||
inode->i_blocks = btrfs_inode_nblocks(leaf, inode_item);
|
||||
inode->i_generation = btrfs_inode_generation(leaf, inode_item);
|
||||
BTRFS_I(inode)->generation = btrfs_inode_generation(leaf, inode_item);
|
||||
inode->i_generation = BTRFS_I(inode)->generation;
|
||||
inode->i_rdev = 0;
|
||||
rdev = btrfs_inode_rdev(leaf, inode_item);
|
||||
|
||||
|
@ -1056,7 +1059,8 @@ void btrfs_read_locked_inode(struct inode *inode)
|
|||
make_bad_inode(inode);
|
||||
}
|
||||
|
||||
static void fill_inode_item(struct extent_buffer *leaf,
|
||||
static void fill_inode_item(struct btrfs_trans_handle *trans,
|
||||
struct extent_buffer *leaf,
|
||||
struct btrfs_inode_item *item,
|
||||
struct inode *inode)
|
||||
{
|
||||
|
@ -1082,7 +1086,8 @@ static void fill_inode_item(struct extent_buffer *leaf,
|
|||
inode->i_ctime.tv_nsec);
|
||||
|
||||
btrfs_set_inode_nblocks(leaf, item, inode->i_blocks);
|
||||
btrfs_set_inode_generation(leaf, item, inode->i_generation);
|
||||
btrfs_set_inode_generation(leaf, item, BTRFS_I(inode)->generation);
|
||||
btrfs_set_inode_transid(leaf, item, trans->transid);
|
||||
btrfs_set_inode_rdev(leaf, item, inode->i_rdev);
|
||||
btrfs_set_inode_flags(leaf, item, BTRFS_I(inode)->flags);
|
||||
btrfs_set_inode_block_group(leaf, item,
|
||||
|
@ -1112,7 +1117,7 @@ int noinline btrfs_update_inode(struct btrfs_trans_handle *trans,
|
|||
inode_item = btrfs_item_ptr(leaf, path->slots[0],
|
||||
struct btrfs_inode_item);
|
||||
|
||||
fill_inode_item(leaf, inode_item, inode);
|
||||
fill_inode_item(trans, leaf, inode_item, inode);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
btrfs_set_inode_last_trans(trans, inode);
|
||||
ret = 0;
|
||||
|
@ -1122,14 +1127,12 @@ int noinline btrfs_update_inode(struct btrfs_trans_handle *trans,
|
|||
}
|
||||
|
||||
|
||||
static int btrfs_unlink_trans(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *dir,
|
||||
struct dentry *dentry)
|
||||
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)
|
||||
{
|
||||
struct btrfs_path *path;
|
||||
const char *name = dentry->d_name.name;
|
||||
int name_len = dentry->d_name.len;
|
||||
int ret = 0;
|
||||
struct extent_buffer *leaf;
|
||||
struct btrfs_dir_item *di;
|
||||
|
@ -1160,13 +1163,12 @@ static int btrfs_unlink_trans(struct btrfs_trans_handle *trans,
|
|||
btrfs_release_path(root, path);
|
||||
|
||||
ret = btrfs_del_inode_ref(trans, root, name, name_len,
|
||||
dentry->d_inode->i_ino,
|
||||
dentry->d_parent->d_inode->i_ino, &index);
|
||||
inode->i_ino,
|
||||
dir->i_ino, &index);
|
||||
if (ret) {
|
||||
printk("failed to delete reference to %.*s, "
|
||||
"inode %lu parent %lu\n", name_len, name,
|
||||
dentry->d_inode->i_ino,
|
||||
dentry->d_parent->d_inode->i_ino);
|
||||
inode->i_ino, dir->i_ino);
|
||||
goto err;
|
||||
}
|
||||
|
||||
|
@ -1183,21 +1185,25 @@ static int btrfs_unlink_trans(struct btrfs_trans_handle *trans,
|
|||
ret = btrfs_delete_one_dir_name(trans, root, path, di);
|
||||
btrfs_release_path(root, path);
|
||||
|
||||
dentry->d_inode->i_ctime = dir->i_ctime;
|
||||
ret = btrfs_del_inode_ref_in_log(trans, root, name, name_len,
|
||||
inode, dir->i_ino);
|
||||
BUG_ON(ret);
|
||||
|
||||
ret = btrfs_del_dir_entries_in_log(trans, root, name, name_len,
|
||||
dir, index);
|
||||
BUG_ON(ret);
|
||||
err:
|
||||
btrfs_free_path(path);
|
||||
if (!ret) {
|
||||
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
||||
dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
btrfs_update_inode(trans, root, dir);
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
|
||||
dentry->d_inode->i_nlink--;
|
||||
#else
|
||||
drop_nlink(dentry->d_inode);
|
||||
#endif
|
||||
ret = btrfs_update_inode(trans, root, dentry->d_inode);
|
||||
dir->i_sb->s_dirt = 1;
|
||||
}
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
btrfs_i_size_write(dir, dir->i_size - name_len * 2);
|
||||
inode->i_ctime = dir->i_mtime = dir->i_ctime = CURRENT_TIME;
|
||||
btrfs_update_inode(trans, root, dir);
|
||||
btrfs_drop_nlink(inode);
|
||||
ret = btrfs_update_inode(trans, root, inode);
|
||||
dir->i_sb->s_dirt = 1;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1218,7 +1224,8 @@ static int btrfs_unlink(struct inode *dir, struct dentry *dentry)
|
|||
trans = btrfs_start_transaction(root, 1);
|
||||
|
||||
btrfs_set_trans_block_group(trans, dir);
|
||||
ret = btrfs_unlink_trans(trans, root, dir, dentry);
|
||||
ret = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
|
||||
dentry->d_name.name, dentry->d_name.len);
|
||||
|
||||
if (inode->i_nlink == 0)
|
||||
ret = btrfs_orphan_add(trans, inode);
|
||||
|
@ -1256,7 +1263,8 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
goto fail_trans;
|
||||
|
||||
/* now the directory is empty */
|
||||
err = btrfs_unlink_trans(trans, root, dir, dentry);
|
||||
err = btrfs_unlink_inode(trans, root, dir, dentry->d_inode,
|
||||
dentry->d_name.name, dentry->d_name.len);
|
||||
if (!err) {
|
||||
btrfs_i_size_write(inode, 0);
|
||||
}
|
||||
|
@ -1283,10 +1291,10 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry)
|
|||
* min_type is the minimum key type to truncate down to. If set to 0, this
|
||||
* will kill all the items on this inode, including the INODE_ITEM_KEY.
|
||||
*/
|
||||
static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *inode,
|
||||
u32 min_type)
|
||||
noinline int btrfs_truncate_inode_items(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
struct inode *inode,
|
||||
u64 new_size, u32 min_type)
|
||||
{
|
||||
int ret;
|
||||
struct btrfs_path *path;
|
||||
|
@ -1307,7 +1315,9 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
int extent_type = -1;
|
||||
u64 mask = root->sectorsize - 1;
|
||||
|
||||
btrfs_drop_extent_cache(inode, inode->i_size & (~mask), (u64)-1);
|
||||
if (root->ref_cows)
|
||||
btrfs_drop_extent_cache(inode,
|
||||
new_size & (~mask), (u64)-1);
|
||||
path = btrfs_alloc_path();
|
||||
path->reada = -1;
|
||||
BUG_ON(!path);
|
||||
|
@ -1324,7 +1334,13 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
goto error;
|
||||
}
|
||||
if (ret > 0) {
|
||||
BUG_ON(path->slots[0] == 0);
|
||||
/* there are no items in the tree for us to truncate, we're
|
||||
* done
|
||||
*/
|
||||
if (path->slots[0] == 0) {
|
||||
ret = 0;
|
||||
goto error;
|
||||
}
|
||||
path->slots[0]--;
|
||||
}
|
||||
|
||||
|
@ -1358,10 +1374,10 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
}
|
||||
if (found_type == BTRFS_CSUM_ITEM_KEY) {
|
||||
ret = btrfs_csum_truncate(trans, root, path,
|
||||
inode->i_size);
|
||||
new_size);
|
||||
BUG_ON(ret);
|
||||
}
|
||||
if (item_end < inode->i_size) {
|
||||
if (item_end < new_size) {
|
||||
if (found_type == BTRFS_DIR_ITEM_KEY) {
|
||||
found_type = BTRFS_INODE_ITEM_KEY;
|
||||
} else if (found_type == BTRFS_EXTENT_ITEM_KEY) {
|
||||
|
@ -1378,7 +1394,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
btrfs_set_key_type(&key, found_type);
|
||||
goto next;
|
||||
}
|
||||
if (found_key.offset >= inode->i_size)
|
||||
if (found_key.offset >= new_size)
|
||||
del_item = 1;
|
||||
else
|
||||
del_item = 0;
|
||||
|
@ -1394,7 +1410,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
if (!del_item) {
|
||||
u64 orig_num_bytes =
|
||||
btrfs_file_extent_num_bytes(leaf, fi);
|
||||
extent_num_bytes = inode->i_size -
|
||||
extent_num_bytes = new_size -
|
||||
found_key.offset + root->sectorsize - 1;
|
||||
extent_num_bytes = extent_num_bytes &
|
||||
~((u64)root->sectorsize - 1);
|
||||
|
@ -1402,7 +1418,7 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
extent_num_bytes);
|
||||
num_dec = (orig_num_bytes -
|
||||
extent_num_bytes);
|
||||
if (extent_start != 0)
|
||||
if (root->ref_cows && extent_start != 0)
|
||||
dec_i_blocks(inode, num_dec);
|
||||
btrfs_mark_buffer_dirty(leaf);
|
||||
} else {
|
||||
|
@ -1413,22 +1429,29 @@ static int btrfs_truncate_in_trans(struct btrfs_trans_handle *trans,
|
|||
num_dec = btrfs_file_extent_num_bytes(leaf, fi);
|
||||
if (extent_start != 0) {
|
||||
found_extent = 1;
|
||||
dec_i_blocks(inode, num_dec);
|
||||
if (root->ref_cows)
|
||||
dec_i_blocks(inode, num_dec);
|
||||
}
|
||||
if (root->ref_cows) {
|
||||
root_gen =
|
||||
btrfs_header_generation(leaf);
|
||||
}
|
||||
root_gen = btrfs_header_generation(leaf);
|
||||
root_owner = btrfs_header_owner(leaf);
|
||||
}
|
||||
} else if (extent_type == BTRFS_FILE_EXTENT_INLINE) {
|
||||
if (!del_item) {
|
||||
u32 newsize = inode->i_size - found_key.offset;
|
||||
dec_i_blocks(inode, item_end + 1 -
|
||||
found_key.offset - newsize);
|
||||
newsize =
|
||||
btrfs_file_extent_calc_inline_size(newsize);
|
||||
u32 size = new_size - found_key.offset;
|
||||
|
||||
if (root->ref_cows) {
|
||||
dec_i_blocks(inode, item_end + 1 -
|
||||
found_key.offset - size);
|
||||
}
|
||||
size =
|
||||
btrfs_file_extent_calc_inline_size(size);
|
||||
ret = btrfs_truncate_item(trans, root, path,
|
||||
newsize, 1);
|
||||
size, 1);
|
||||
BUG_ON(ret);
|
||||
} else {
|
||||
} else if (root->ref_cows) {
|
||||
dec_i_blocks(inode, item_end + 1 -
|
||||
found_key.offset);
|
||||
}
|
||||
|
@ -1666,7 +1689,7 @@ void btrfs_delete_inode(struct inode *inode)
|
|||
trans = btrfs_start_transaction(root, 1);
|
||||
|
||||
btrfs_set_trans_block_group(trans, inode);
|
||||
ret = btrfs_truncate_in_trans(trans, root, inode, 0);
|
||||
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size, 0);
|
||||
if (ret) {
|
||||
btrfs_orphan_del(NULL, inode);
|
||||
goto no_delete_lock;
|
||||
|
@ -1753,15 +1776,20 @@ static int fixup_tree_root_location(struct btrfs_root *root,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btrfs_init_locked_inode(struct inode *inode, void *p)
|
||||
static noinline void init_btrfs_i(struct inode *inode)
|
||||
{
|
||||
struct btrfs_iget_args *args = p;
|
||||
inode->i_ino = args->ino;
|
||||
BTRFS_I(inode)->root = args->root;
|
||||
BTRFS_I(inode)->delalloc_bytes = 0;
|
||||
inode->i_mapping->writeback_index = 0;
|
||||
BTRFS_I(inode)->disk_i_size = 0;
|
||||
BTRFS_I(inode)->index_cnt = (u64)-1;
|
||||
struct btrfs_inode *bi = BTRFS_I(inode);
|
||||
|
||||
bi->i_acl = NULL;
|
||||
bi->i_default_acl = NULL;
|
||||
|
||||
bi->generation = 0;
|
||||
bi->last_trans = 0;
|
||||
bi->logged_trans = 0;
|
||||
bi->delalloc_bytes = 0;
|
||||
bi->disk_i_size = 0;
|
||||
bi->flags = 0;
|
||||
bi->index_cnt = (u64)-1;
|
||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
|
@ -1771,6 +1799,15 @@ static int btrfs_init_locked_inode(struct inode *inode, void *p)
|
|||
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
||||
mutex_init(&BTRFS_I(inode)->csum_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->log_mutex);
|
||||
}
|
||||
|
||||
static int btrfs_init_locked_inode(struct inode *inode, void *p)
|
||||
{
|
||||
struct btrfs_iget_args *args = p;
|
||||
inode->i_ino = args->ino;
|
||||
init_btrfs_i(inode);
|
||||
BTRFS_I(inode)->root = args->root;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2263,21 +2300,10 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
* btrfs_get_inode_index_count has an explanation for the magic
|
||||
* number
|
||||
*/
|
||||
init_btrfs_i(inode);
|
||||
BTRFS_I(inode)->index_cnt = 2;
|
||||
|
||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
||||
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
|
||||
mutex_init(&BTRFS_I(inode)->csum_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
||||
BTRFS_I(inode)->delalloc_bytes = 0;
|
||||
inode->i_mapping->writeback_index = 0;
|
||||
BTRFS_I(inode)->disk_i_size = 0;
|
||||
BTRFS_I(inode)->root = root;
|
||||
BTRFS_I(inode)->generation = trans->transid;
|
||||
|
||||
if (mode & S_IFDIR)
|
||||
owner = 0;
|
||||
|
@ -2290,7 +2316,6 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
new_inode_group = group;
|
||||
}
|
||||
BTRFS_I(inode)->block_group = new_inode_group;
|
||||
BTRFS_I(inode)->flags = 0;
|
||||
|
||||
key[0].objectid = objectid;
|
||||
btrfs_set_key_type(&key[0], BTRFS_INODE_ITEM_KEY);
|
||||
|
@ -2318,7 +2343,7 @@ static struct inode *btrfs_new_inode(struct btrfs_trans_handle *trans,
|
|||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
|
||||
inode_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
|
||||
struct btrfs_inode_item);
|
||||
fill_inode_item(path->nodes[0], inode_item, inode);
|
||||
fill_inode_item(trans, path->nodes[0], inode_item, inode);
|
||||
|
||||
ref = btrfs_item_ptr(path->nodes[0], path->slots[0] + 1,
|
||||
struct btrfs_inode_ref);
|
||||
|
@ -2349,38 +2374,34 @@ static inline u8 btrfs_inode_type(struct inode *inode)
|
|||
return btrfs_type_by_mode[(inode->i_mode & S_IFMT) >> S_SHIFT];
|
||||
}
|
||||
|
||||
static int btrfs_add_link(struct btrfs_trans_handle *trans,
|
||||
struct dentry *dentry, struct inode *inode,
|
||||
int add_backref, u64 index)
|
||||
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 ret;
|
||||
struct btrfs_key key;
|
||||
struct btrfs_root *root = BTRFS_I(dentry->d_parent->d_inode)->root;
|
||||
struct inode *parent_inode = dentry->d_parent->d_inode;
|
||||
struct btrfs_root *root = BTRFS_I(parent_inode)->root;
|
||||
|
||||
key.objectid = inode->i_ino;
|
||||
btrfs_set_key_type(&key, BTRFS_INODE_ITEM_KEY);
|
||||
key.offset = 0;
|
||||
|
||||
ret = btrfs_insert_dir_item(trans, root,
|
||||
dentry->d_name.name, dentry->d_name.len,
|
||||
dentry->d_parent->d_inode->i_ino,
|
||||
ret = btrfs_insert_dir_item(trans, root, name, name_len,
|
||||
parent_inode->i_ino,
|
||||
&key, btrfs_inode_type(inode),
|
||||
index);
|
||||
if (ret == 0) {
|
||||
if (add_backref) {
|
||||
ret = btrfs_insert_inode_ref(trans, root,
|
||||
dentry->d_name.name,
|
||||
dentry->d_name.len,
|
||||
inode->i_ino,
|
||||
parent_inode->i_ino,
|
||||
index);
|
||||
name, name_len,
|
||||
inode->i_ino,
|
||||
parent_inode->i_ino,
|
||||
index);
|
||||
}
|
||||
btrfs_i_size_write(parent_inode, parent_inode->i_size +
|
||||
dentry->d_name.len * 2);
|
||||
name_len * 2);
|
||||
parent_inode->i_mtime = parent_inode->i_ctime = CURRENT_TIME;
|
||||
ret = btrfs_update_inode(trans, root,
|
||||
dentry->d_parent->d_inode);
|
||||
ret = btrfs_update_inode(trans, root, parent_inode);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
@ -2389,7 +2410,9 @@ static int btrfs_add_nondir(struct btrfs_trans_handle *trans,
|
|||
struct dentry *dentry, struct inode *inode,
|
||||
int backref, u64 index)
|
||||
{
|
||||
int err = btrfs_add_link(trans, dentry, inode, backref, index);
|
||||
int err = btrfs_add_link(trans, dentry->d_parent->d_inode,
|
||||
inode, dentry->d_name.name,
|
||||
dentry->d_name.len, backref, index);
|
||||
if (!err) {
|
||||
d_instantiate(dentry, inode);
|
||||
return 0;
|
||||
|
@ -2513,19 +2536,7 @@ static int btrfs_create(struct inode *dir, struct dentry *dentry,
|
|||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
inode->i_fop = &btrfs_file_operations;
|
||||
inode->i_op = &btrfs_file_inode_operations;
|
||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
|
||||
mutex_init(&BTRFS_I(inode)->csum_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
||||
BTRFS_I(inode)->delalloc_bytes = 0;
|
||||
BTRFS_I(inode)->disk_i_size = 0;
|
||||
inode->i_mapping->writeback_index = 0;
|
||||
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
||||
}
|
||||
dir->i_sb->s_dirt = 1;
|
||||
btrfs_update_inode_block_group(trans, inode);
|
||||
|
@ -2556,11 +2567,7 @@ static int btrfs_link(struct dentry *old_dentry, struct inode *dir,
|
|||
if (inode->i_nlink == 0)
|
||||
return -ENOENT;
|
||||
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,18)
|
||||
inode->i_nlink++;
|
||||
#else
|
||||
inc_nlink(inode);
|
||||
#endif
|
||||
btrfs_inc_nlink(inode);
|
||||
err = btrfs_check_free_space(root, 1, 0);
|
||||
if (err)
|
||||
goto fail;
|
||||
|
@ -2650,7 +2657,9 @@ static int btrfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|||
if (err)
|
||||
goto out_fail;
|
||||
|
||||
err = btrfs_add_link(trans, dentry, inode, 0, index);
|
||||
err = btrfs_add_link(trans, dentry->d_parent->d_inode,
|
||||
inode, dentry->d_name.name,
|
||||
dentry->d_name.len, 0, index);
|
||||
if (err)
|
||||
goto out_fail;
|
||||
|
||||
|
@ -3221,7 +3230,7 @@ static void btrfs_truncate(struct inode *inode)
|
|||
if (ret)
|
||||
goto out;
|
||||
/* FIXME, add redo link to tree so we don't leak on crash */
|
||||
ret = btrfs_truncate_in_trans(trans, root, inode,
|
||||
ret = btrfs_truncate_inode_items(trans, root, inode, inode->i_size,
|
||||
BTRFS_EXTENT_DATA_KEY);
|
||||
btrfs_update_inode(trans, root, inode);
|
||||
|
||||
|
@ -3304,6 +3313,7 @@ struct inode *btrfs_alloc_inode(struct super_block *sb)
|
|||
if (!ei)
|
||||
return NULL;
|
||||
ei->last_trans = 0;
|
||||
ei->logged_trans = 0;
|
||||
btrfs_ordered_inode_tree_init(&ei->ordered_tree);
|
||||
ei->i_acl = BTRFS_ACL_NOT_CACHED;
|
||||
ei->i_default_acl = BTRFS_ACL_NOT_CACHED;
|
||||
|
@ -3463,31 +3473,39 @@ static int btrfs_rename(struct inode * old_dir, struct dentry *old_dentry,
|
|||
|
||||
btrfs_set_trans_block_group(trans, new_dir);
|
||||
|
||||
old_dentry->d_inode->i_nlink++;
|
||||
btrfs_inc_nlink(old_dentry->d_inode);
|
||||
old_dir->i_ctime = old_dir->i_mtime = ctime;
|
||||
new_dir->i_ctime = new_dir->i_mtime = ctime;
|
||||
old_inode->i_ctime = ctime;
|
||||
|
||||
ret = btrfs_unlink_trans(trans, root, old_dir, old_dentry);
|
||||
ret = btrfs_unlink_inode(trans, root, old_dir, old_dentry->d_inode,
|
||||
old_dentry->d_name.name,
|
||||
old_dentry->d_name.len);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
|
||||
if (new_inode) {
|
||||
new_inode->i_ctime = CURRENT_TIME;
|
||||
ret = btrfs_unlink_trans(trans, root, new_dir, new_dentry);
|
||||
ret = btrfs_unlink_inode(trans, root, new_dir,
|
||||
new_dentry->d_inode,
|
||||
new_dentry->d_name.name,
|
||||
new_dentry->d_name.len);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
if (new_inode->i_nlink == 0) {
|
||||
ret = btrfs_orphan_add(trans, new_inode);
|
||||
ret = btrfs_orphan_add(trans, new_dentry->d_inode);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
}
|
||||
|
||||
}
|
||||
ret = btrfs_set_inode_index(new_dir, old_inode, &index);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
|
||||
ret = btrfs_add_link(trans, new_dentry, old_inode, 1, index);
|
||||
ret = btrfs_add_link(trans, new_dentry->d_parent->d_inode,
|
||||
old_inode, new_dentry->d_name.name,
|
||||
new_dentry->d_name.len, 1, index);
|
||||
if (ret)
|
||||
goto out_fail;
|
||||
|
||||
|
@ -3577,19 +3595,7 @@ static int btrfs_symlink(struct inode *dir, struct dentry *dentry,
|
|||
inode->i_mapping->backing_dev_info = &root->fs_info->bdi;
|
||||
inode->i_fop = &btrfs_file_operations;
|
||||
inode->i_op = &btrfs_file_inode_operations;
|
||||
extent_map_tree_init(&BTRFS_I(inode)->extent_tree, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
extent_io_tree_init(&BTRFS_I(inode)->io_failure_tree,
|
||||
inode->i_mapping, GFP_NOFS);
|
||||
INIT_LIST_HEAD(&BTRFS_I(inode)->delalloc_inodes);
|
||||
mutex_init(&BTRFS_I(inode)->csum_mutex);
|
||||
mutex_init(&BTRFS_I(inode)->extent_mutex);
|
||||
BTRFS_I(inode)->delalloc_bytes = 0;
|
||||
BTRFS_I(inode)->disk_i_size = 0;
|
||||
inode->i_mapping->writeback_index = 0;
|
||||
BTRFS_I(inode)->io_tree.ops = &btrfs_extent_io_ops;
|
||||
btrfs_ordered_inode_tree_init(&BTRFS_I(inode)->ordered_tree);
|
||||
}
|
||||
dir->i_sb->s_dirt = 1;
|
||||
btrfs_update_inode_block_group(trans, inode);
|
||||
|
@ -3691,6 +3697,7 @@ static struct file_operations btrfs_dir_file_operations = {
|
|||
.compat_ioctl = btrfs_ioctl,
|
||||
#endif
|
||||
.release = btrfs_release_file,
|
||||
.fsync = btrfs_sync_file,
|
||||
};
|
||||
|
||||
static struct extent_io_ops btrfs_extent_io_ops = {
|
||||
|
|
|
@ -202,8 +202,9 @@ int btrfs_find_dead_roots(struct btrfs_root *root, u64 objectid,
|
|||
memcpy(&found_key, &key, sizeof(key));
|
||||
key.offset++;
|
||||
btrfs_release_path(root, path);
|
||||
dead_root = btrfs_read_fs_root_no_radix(root->fs_info,
|
||||
&found_key);
|
||||
dead_root =
|
||||
btrfs_read_fs_root_no_radix(root->fs_info->tree_root,
|
||||
&found_key);
|
||||
if (IS_ERR(dead_root)) {
|
||||
ret = PTR_ERR(dead_root);
|
||||
goto err;
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "transaction.h"
|
||||
#include "locking.h"
|
||||
#include "ref-cache.h"
|
||||
#include "tree-log.h"
|
||||
|
||||
static int total_trans = 0;
|
||||
extern struct kmem_cache *btrfs_trans_handle_cachep;
|
||||
|
@ -57,6 +58,7 @@ static noinline int join_transaction(struct btrfs_root *root)
|
|||
root->fs_info->generation++;
|
||||
root->fs_info->last_alloc = 0;
|
||||
root->fs_info->last_data_alloc = 0;
|
||||
root->fs_info->last_log_alloc = 0;
|
||||
cur_trans->num_writers = 1;
|
||||
cur_trans->num_joined = 0;
|
||||
cur_trans->transid = root->fs_info->generation;
|
||||
|
@ -83,7 +85,7 @@ static noinline int join_transaction(struct btrfs_root *root)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static noinline int record_root_in_trans(struct btrfs_root *root)
|
||||
noinline int btrfs_record_root_in_trans(struct btrfs_root *root)
|
||||
{
|
||||
struct btrfs_dirty_root *dirty;
|
||||
u64 running_trans_id = root->fs_info->running_transaction->transid;
|
||||
|
@ -151,7 +153,7 @@ static void wait_current_trans(struct btrfs_root *root)
|
|||
}
|
||||
}
|
||||
|
||||
struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
|
||||
static struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
|
||||
int num_blocks, int wait)
|
||||
{
|
||||
struct btrfs_trans_handle *h =
|
||||
|
@ -164,7 +166,7 @@ struct btrfs_trans_handle *start_transaction(struct btrfs_root *root,
|
|||
ret = join_transaction(root);
|
||||
BUG_ON(ret);
|
||||
|
||||
record_root_in_trans(root);
|
||||
btrfs_record_root_in_trans(root);
|
||||
h->transid = root->fs_info->running_transaction->transid;
|
||||
h->transaction = root->fs_info->running_transaction;
|
||||
h->blocks_reserved = num_blocks;
|
||||
|
@ -456,6 +458,8 @@ static noinline int add_dirty_roots(struct btrfs_trans_handle *trans,
|
|||
BUG_ON(!root->ref_tree);
|
||||
dirty = root->dirty_root;
|
||||
|
||||
btrfs_free_log(trans, root);
|
||||
|
||||
if (root->commit_root == root->node) {
|
||||
WARN_ON(root->node->start !=
|
||||
btrfs_root_bytenr(&root->root_item));
|
||||
|
@ -600,7 +604,7 @@ static noinline int drop_dirty_roots(struct btrfs_root *tree_root,
|
|||
num_bytes -= btrfs_root_used(&dirty->root->root_item);
|
||||
bytes_used = btrfs_root_used(&root->root_item);
|
||||
if (num_bytes) {
|
||||
record_root_in_trans(root);
|
||||
btrfs_record_root_in_trans(root);
|
||||
btrfs_set_root_used(&root->root_item,
|
||||
bytes_used - num_bytes);
|
||||
}
|
||||
|
@ -745,7 +749,6 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
int ret;
|
||||
|
||||
INIT_LIST_HEAD(&dirty_fs_roots);
|
||||
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
if (trans->transaction->in_commit) {
|
||||
cur_trans = trans->transaction;
|
||||
|
@ -821,10 +824,30 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
|
||||
WARN_ON(cur_trans != trans->transaction);
|
||||
|
||||
/* btrfs_commit_tree_roots is responsible for getting the
|
||||
* various roots consistent with each other. Every pointer
|
||||
* in the tree of tree roots has to point to the most up to date
|
||||
* root for every subvolume and other tree. So, we have to keep
|
||||
* the tree logging code from jumping in and changing any
|
||||
* of the trees.
|
||||
*
|
||||
* At this point in the commit, there can't be any tree-log
|
||||
* writers, but a little lower down we drop the trans mutex
|
||||
* and let new people in. By holding the tree_log_mutex
|
||||
* from now until after the super is written, we avoid races
|
||||
* with the tree-log code.
|
||||
*/
|
||||
mutex_lock(&root->fs_info->tree_log_mutex);
|
||||
|
||||
ret = add_dirty_roots(trans, &root->fs_info->fs_roots_radix,
|
||||
&dirty_fs_roots);
|
||||
BUG_ON(ret);
|
||||
|
||||
/* add_dirty_roots gets rid of all the tree log roots, it is now
|
||||
* safe to free the root of tree log roots
|
||||
*/
|
||||
btrfs_free_log_root_tree(trans, root->fs_info);
|
||||
|
||||
ret = btrfs_commit_tree_roots(trans, root);
|
||||
BUG_ON(ret);
|
||||
|
||||
|
@ -843,6 +866,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
chunk_root->node->start);
|
||||
btrfs_set_super_chunk_root_level(&root->fs_info->super_copy,
|
||||
btrfs_header_level(chunk_root->node));
|
||||
|
||||
if (!root->fs_info->log_root_recovering) {
|
||||
btrfs_set_super_log_root(&root->fs_info->super_copy, 0);
|
||||
btrfs_set_super_log_root_level(&root->fs_info->super_copy, 0);
|
||||
}
|
||||
|
||||
memcpy(&root->fs_info->super_for_commit, &root->fs_info->super_copy,
|
||||
sizeof(root->fs_info->super_copy));
|
||||
|
||||
|
@ -857,6 +886,12 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
BUG_ON(ret);
|
||||
write_ctree_super(trans, root);
|
||||
|
||||
/*
|
||||
* the super is written, we can safely allow the tree-loggers
|
||||
* to go about their business
|
||||
*/
|
||||
mutex_unlock(&root->fs_info->tree_log_mutex);
|
||||
|
||||
btrfs_finish_extent_commit(trans, root, pinned_copy);
|
||||
mutex_lock(&root->fs_info->trans_mutex);
|
||||
|
||||
|
|
|
@ -98,4 +98,5 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
|
|||
int btrfs_end_transaction_throttle(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root);
|
||||
void btrfs_throttle(struct btrfs_root *root);
|
||||
int btrfs_record_root_in_trans(struct btrfs_root *root);
|
||||
#endif
|
||||
|
|
|
@ -81,12 +81,12 @@ int btrfs_defrag_leaves(struct btrfs_trans_handle *trans,
|
|||
memcpy(&key, &root->defrag_progress, sizeof(key));
|
||||
}
|
||||
|
||||
path->lowest_level = 1;
|
||||
path->keep_locks = 1;
|
||||
if (cache_only)
|
||||
min_trans = root->defrag_trans_start;
|
||||
|
||||
ret = btrfs_search_forward(root, &key, path, cache_only, min_trans);
|
||||
ret = btrfs_search_forward(root, &key, NULL, path,
|
||||
cache_only, min_trans);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
if (ret > 0) {
|
||||
|
|
2804
fs/btrfs/tree-log.c
Normal file
2804
fs/btrfs/tree-log.c
Normal file
File diff suppressed because it is too large
Load diff
41
fs/btrfs/tree-log.h
Normal file
41
fs/btrfs/tree-log.h
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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 __TREE_LOG_
|
||||
#define __TREE_LOG_
|
||||
|
||||
int btrfs_sync_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_log_dentry_safe(struct btrfs_trans_handle *trans,
|
||||
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,
|
||||
struct btrfs_root *root,
|
||||
const char *name, int name_len,
|
||||
struct inode *dir, u64 index);
|
||||
int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,
|
||||
struct btrfs_root *root,
|
||||
const char *name, int name_len,
|
||||
struct inode *inode, u64 dirid);
|
||||
#endif
|
Loading…
Reference in a new issue