diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 667 | 
1 files changed, 482 insertions, 185 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 0d9613c3f5e..9e1f2cd5e67 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -20,14 +20,11 @@  #include <linux/slab.h>  #include <linux/blkdev.h>  #include <linux/list_sort.h> -#include "ctree.h" -#include "transaction.h" +#include "tree-log.h"  #include "disk-io.h"  #include "locking.h"  #include "print-tree.h"  #include "backref.h" -#include "compat.h" -#include "tree-log.h"  #include "hash.h"  /* magic values for the inode_only field in btrfs_log_inode: @@ -93,7 +90,8 @@   */  #define LOG_WALK_PIN_ONLY 0  #define LOG_WALK_REPLAY_INODES 1 -#define LOG_WALK_REPLAY_ALL 2 +#define LOG_WALK_REPLAY_DIR_INDEX 2 +#define LOG_WALK_REPLAY_ALL 3  static int btrfs_log_inode(struct btrfs_trans_handle *trans,  			     struct btrfs_root *root, struct inode *inode, @@ -136,43 +134,61 @@ static noinline int replay_dir_deletes(struct btrfs_trans_handle *trans,   * syncing the tree wait for us to finish   */  static int start_log_trans(struct btrfs_trans_handle *trans, -			   struct btrfs_root *root) +			   struct btrfs_root *root, +			   struct btrfs_log_ctx *ctx)  { +	int index;  	int ret; -	int err = 0;  	mutex_lock(&root->log_mutex);  	if (root->log_root) { +		if (btrfs_need_log_full_commit(root->fs_info, trans)) { +			ret = -EAGAIN; +			goto out; +		}  		if (!root->log_start_pid) {  			root->log_start_pid = current->pid; -			root->log_multiple_pids = false; +			clear_bit(BTRFS_ROOT_MULTI_LOG_TASKS, &root->state);  		} else if (root->log_start_pid != current->pid) { -			root->log_multiple_pids = true; +			set_bit(BTRFS_ROOT_MULTI_LOG_TASKS, &root->state);  		}  		atomic_inc(&root->log_batch);  		atomic_inc(&root->log_writers); +		if (ctx) { +			index = root->log_transid % 2; +			list_add_tail(&ctx->list, &root->log_ctxs[index]); +			ctx->log_transid = root->log_transid; +		}  		mutex_unlock(&root->log_mutex);  		return 0;  	} -	root->log_multiple_pids = false; -	root->log_start_pid = current->pid; + +	ret = 0;  	mutex_lock(&root->fs_info->tree_log_mutex); -	if (!root->fs_info->log_root_tree) { +	if (!root->fs_info->log_root_tree)  		ret = btrfs_init_log_root_tree(trans, root->fs_info); -		if (ret) -			err = ret; -	} -	if (err == 0 && !root->log_root) { +	mutex_unlock(&root->fs_info->tree_log_mutex); +	if (ret) +		goto out; + +	if (!root->log_root) {  		ret = btrfs_add_log_tree(trans, root);  		if (ret) -			err = ret; +			goto out;  	} -	mutex_unlock(&root->fs_info->tree_log_mutex); +	clear_bit(BTRFS_ROOT_MULTI_LOG_TASKS, &root->state); +	root->log_start_pid = current->pid;  	atomic_inc(&root->log_batch);  	atomic_inc(&root->log_writers); +	if (ctx) { +		index = root->log_transid % 2; +		list_add_tail(&ctx->list, &root->log_ctxs[index]); +		ctx->log_transid = root->log_transid; +	} +out:  	mutex_unlock(&root->log_mutex); -	return err; +	return ret;  }  /* @@ -393,6 +409,7 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  		if (inode_item) {  			struct btrfs_inode_item *item;  			u64 nbytes; +			u32 mode;  			item = btrfs_item_ptr(path->nodes[0], path->slots[0],  					      struct btrfs_inode_item); @@ -400,9 +417,19 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  			item = btrfs_item_ptr(eb, slot,  					      struct btrfs_inode_item);  			btrfs_set_inode_nbytes(eb, item, nbytes); + +			/* +			 * If this is a directory we need to reset the i_size to +			 * 0 so that we can set it up properly when replaying +			 * the rest of the items in this log. +			 */ +			mode = btrfs_inode_mode(eb, item); +			if (S_ISDIR(mode)) +				btrfs_set_inode_size(eb, item, 0);  		}  	} else if (inode_item) {  		struct btrfs_inode_item *item; +		u32 mode;  		/*  		 * New inode, set nbytes to 0 so that the nbytes comes out @@ -410,6 +437,15 @@ static noinline int overwrite_item(struct btrfs_trans_handle *trans,  		 */  		item = btrfs_item_ptr(eb, slot, struct btrfs_inode_item);  		btrfs_set_inode_nbytes(eb, item, 0); + +		/* +		 * If this is a directory we need to reset the i_size to 0 so +		 * that we can set it up properly when replaying the rest of +		 * the items in this log. +		 */ +		mode = btrfs_inode_mode(eb, item); +		if (S_ISDIR(mode)) +			btrfs_set_inode_size(eb, item, 0);  	}  insert:  	btrfs_release_path(path); @@ -550,7 +586,7 @@ static noinline int replay_one_extent(struct btrfs_trans_handle *trans,  		if (btrfs_file_extent_disk_bytenr(eb, item) == 0)  			nbytes = 0;  	} else if (found_type == BTRFS_FILE_EXTENT_INLINE) { -		size = btrfs_file_extent_inline_len(eb, item); +		size = btrfs_file_extent_inline_len(eb, slot, item);  		nbytes = btrfs_file_extent_ram_bytes(eb, item);  		extent_end = ALIGN(start + size, root->sectorsize);  	} else { @@ -915,7 +951,7 @@ again:  					    parent_objectid,  					    victim_name,  					    victim_name_len)) { -				btrfs_inc_nlink(inode); +				inc_nlink(inode);  				btrfs_release_path(path);  				ret = btrfs_unlink_inode(trans, root, dir, @@ -985,7 +1021,7 @@ again:  				victim_parent = read_one_inode(root,  							       parent_objectid);  				if (victim_parent) { -					btrfs_inc_nlink(inode); +					inc_nlink(inode);  					btrfs_release_path(path);  					ret = btrfs_unlink_inode(trans, root, @@ -1092,11 +1128,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  				  struct extent_buffer *eb, int slot,  				  struct btrfs_key *key)  { -	struct inode *dir; -	struct inode *inode; +	struct inode *dir = NULL; +	struct inode *inode = NULL;  	unsigned long ref_ptr;  	unsigned long ref_end; -	char *name; +	char *name = NULL;  	int namelen;  	int ret;  	int search_done = 0; @@ -1129,13 +1165,15 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  	 * care of the rest  	 */  	dir = read_one_inode(root, parent_objectid); -	if (!dir) -		return -ENOENT; +	if (!dir) { +		ret = -ENOENT; +		goto out; +	}  	inode = read_one_inode(root, inode_objectid);  	if (!inode) { -		iput(dir); -		return -EIO; +		ret = -EIO; +		goto out;  	}  	while (ref_ptr < ref_end) { @@ -1148,14 +1186,16 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  			 */  			if (!dir)  				dir = read_one_inode(root, parent_objectid); -			if (!dir) -				return -ENOENT; +			if (!dir) { +				ret = -ENOENT; +				goto out; +			}  		} else {  			ret = ref_get_fields(eb, ref_ptr, &namelen, &name,  					     &ref_index);  		}  		if (ret) -			return ret; +			goto out;  		/* if we already have a perfect match, we're done */  		if (!inode_in_dir(root, path, btrfs_ino(dir), btrfs_ino(inode), @@ -1175,12 +1215,11 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  						      parent_objectid,  						      ref_index, name, namelen,  						      &search_done); -				if (ret == 1) { -					ret = 0; +				if (ret) { +					if (ret == 1) +						ret = 0;  					goto out;  				} -				if (ret) -					goto out;  			}  			/* insert our name */ @@ -1194,6 +1233,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  		ref_ptr = (unsigned long)(ref_ptr + ref_struct_size) + namelen;  		kfree(name); +		name = NULL;  		if (log_ref_ver) {  			iput(dir);  			dir = NULL; @@ -1204,6 +1244,7 @@ static noinline int add_inode_ref(struct btrfs_trans_handle *trans,  	ret = overwrite_item(trans, root, path, eb, slot, key);  out:  	btrfs_release_path(path); +	kfree(name);  	iput(dir);  	iput(inode);  	return ret; @@ -1213,7 +1254,8 @@ static int insert_orphan_item(struct btrfs_trans_handle *trans,  			      struct btrfs_root *root, u64 offset)  {  	int ret; -	ret = btrfs_find_orphan_item(root, offset); +	ret = btrfs_find_item(root, NULL, BTRFS_ORPHAN_OBJECTID, +			offset, BTRFS_ORPHAN_ITEM_KEY, NULL);  	if (ret > 0)  		ret = btrfs_insert_orphan_item(trans, root, offset);  	return ret; @@ -1286,6 +1328,7 @@ static int count_inode_refs(struct btrfs_root *root,  				break;  			path->slots[0]--;  		} +process_slot:  		btrfs_item_key_to_cpu(path->nodes[0], &key,  				      path->slots[0]);  		if (key.objectid != ino || @@ -1306,6 +1349,10 @@ static int count_inode_refs(struct btrfs_root *root,  		if (key.offset == 0)  			break; +		if (path->slots[0] > 0) { +			path->slots[0]--; +			goto process_slot; +		}  		key.offset--;  		btrfs_release_path(path);  	} @@ -1459,7 +1506,7 @@ static noinline int link_to_fixup_dir(struct btrfs_trans_handle *trans,  		if (!inode->i_nlink)  			set_nlink(inode, 1);  		else -			btrfs_inc_nlink(inode); +			inc_nlink(inode);  		ret = btrfs_update_inode(trans, root, inode);  	} else if (ret == -EEXIST) {  		ret = 0; @@ -1496,6 +1543,7 @@ static noinline int insert_one_name(struct btrfs_trans_handle *trans,  		iput(inode);  		return -EIO;  	} +  	ret = btrfs_add_link(trans, dir, inode, name, name_len, 1, index);  	/* FIXME, put inode into FIXUP list */ @@ -1534,6 +1582,7 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,  	u8 log_type;  	int exists;  	int ret = 0; +	bool update_size = (key->type == BTRFS_DIR_INDEX_KEY);  	dir = read_one_inode(root, key->objectid);  	if (!dir) @@ -1604,6 +1653,10 @@ static noinline int replay_one_name(struct btrfs_trans_handle *trans,  		goto insert;  out:  	btrfs_release_path(path); +	if (!ret && update_size) { +		btrfs_i_size_write(dir, dir->i_size + name_len * 2); +		ret = btrfs_update_inode(trans, root, dir); +	}  	kfree(name);  	iput(dir);  	return ret; @@ -1614,6 +1667,7 @@ insert:  			      name, name_len, log_type, &log_key);  	if (ret && ret != -ENOENT)  		goto out; +	update_size = false;  	ret = 0;  	goto out;  } @@ -1795,7 +1849,7 @@ again:  						     dir_key->offset,  						     name, name_len, 0);  		} -		if (IS_ERR_OR_NULL(log_di)) { +		if (!log_di || (IS_ERR(log_di) && PTR_ERR(log_di) == -ENOENT)) {  			btrfs_dir_item_key_to_cpu(eb, di, &location);  			btrfs_release_path(path);  			btrfs_release_path(log_path); @@ -1813,7 +1867,7 @@ again:  				goto out;  			} -			btrfs_inc_nlink(inode); +			inc_nlink(inode);  			ret = btrfs_unlink_inode(trans, root, dir, inode,  						 name, name_len);  			if (!ret) @@ -1832,6 +1886,9 @@ again:  				goto again;  			ret = 0;  			goto out; +		} else if (IS_ERR(log_di)) { +			kfree(name); +			return PTR_ERR(log_di);  		}  		btrfs_release_path(log_path);  		kfree(name); @@ -2027,6 +2084,15 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,  			if (ret)  				break;  		} + +		if (key.type == BTRFS_DIR_INDEX_KEY && +		    wc->stage == LOG_WALK_REPLAY_DIR_INDEX) { +			ret = replay_one_dir_item(wc->trans, root, path, +						  eb, i, &key); +			if (ret) +				break; +		} +  		if (wc->stage < LOG_WALK_REPLAY_ALL)  			continue; @@ -2048,8 +2114,7 @@ static int replay_one_buffer(struct btrfs_root *log, struct extent_buffer *eb,  						eb, i, &key);  			if (ret)  				break; -		} else if (key.type == BTRFS_DIR_ITEM_KEY || -			   key.type == BTRFS_DIR_INDEX_KEY) { +		} else if (key.type == BTRFS_DIR_ITEM_KEY) {  			ret = replay_one_dir_item(wc->trans, root, path,  						  eb, i, &key);  			if (ret) @@ -2082,8 +2147,7 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,  		WARN_ON(*level >= BTRFS_MAX_LEVEL);  		cur = path->nodes[*level]; -		if (btrfs_header_level(cur) != *level) -			WARN_ON(1); +		WARN_ON(btrfs_header_level(cur) != *level);  		if (path->slots[*level] >=  		    btrfs_header_nritems(cur)) @@ -2115,11 +2179,13 @@ static noinline int walk_down_log_tree(struct btrfs_trans_handle *trans,  					return ret;  				} -				btrfs_tree_lock(next); -				btrfs_set_lock_blocking(next); -				clean_tree_block(trans, root, next); -				btrfs_wait_tree_block_writeback(next); -				btrfs_tree_unlock(next); +				if (trans) { +					btrfs_tree_lock(next); +					btrfs_set_lock_blocking(next); +					clean_tree_block(trans, root, next); +					btrfs_wait_tree_block_writeback(next); +					btrfs_tree_unlock(next); +				}  				WARN_ON(root_owner !=  					BTRFS_TREE_LOG_OBJECTID); @@ -2191,11 +2257,13 @@ static noinline int walk_up_log_tree(struct btrfs_trans_handle *trans,  				next = path->nodes[*level]; -				btrfs_tree_lock(next); -				btrfs_set_lock_blocking(next); -				clean_tree_block(trans, root, next); -				btrfs_wait_tree_block_writeback(next); -				btrfs_tree_unlock(next); +				if (trans) { +					btrfs_tree_lock(next); +					btrfs_set_lock_blocking(next); +					clean_tree_block(trans, root, next); +					btrfs_wait_tree_block_writeback(next); +					btrfs_tree_unlock(next); +				}  				WARN_ON(root_owner != BTRFS_TREE_LOG_OBJECTID);  				ret = btrfs_free_and_pin_reserved_extent(root, @@ -2265,11 +2333,13 @@ static int walk_log_tree(struct btrfs_trans_handle *trans,  			next = path->nodes[orig_level]; -			btrfs_tree_lock(next); -			btrfs_set_lock_blocking(next); -			clean_tree_block(trans, log, next); -			btrfs_wait_tree_block_writeback(next); -			btrfs_tree_unlock(next); +			if (trans) { +				btrfs_tree_lock(next); +				btrfs_set_lock_blocking(next); +				clean_tree_block(trans, log, next); +				btrfs_wait_tree_block_writeback(next); +				btrfs_tree_unlock(next); +			}  			WARN_ON(log->root_key.objectid !=  				BTRFS_TREE_LOG_OBJECTID); @@ -2305,8 +2375,8 @@ static int update_log_root(struct btrfs_trans_handle *trans,  	return ret;  } -static int wait_log_commit(struct btrfs_trans_handle *trans, -			   struct btrfs_root *root, unsigned long transid) +static void wait_log_commit(struct btrfs_trans_handle *trans, +			    struct btrfs_root *root, int transid)  {  	DEFINE_WAIT(wait);  	int index = transid % 2; @@ -2321,36 +2391,63 @@ static int wait_log_commit(struct btrfs_trans_handle *trans,  				&wait, TASK_UNINTERRUPTIBLE);  		mutex_unlock(&root->log_mutex); -		if (root->fs_info->last_trans_log_full_commit != -		    trans->transid && root->log_transid < transid + 2 && +		if (root->log_transid_committed < transid &&  		    atomic_read(&root->log_commit[index]))  			schedule();  		finish_wait(&root->log_commit_wait[index], &wait);  		mutex_lock(&root->log_mutex); -	} while (root->fs_info->last_trans_log_full_commit != -		 trans->transid && root->log_transid < transid + 2 && +	} while (root->log_transid_committed < transid &&  		 atomic_read(&root->log_commit[index])); -	return 0;  }  static void wait_for_writer(struct btrfs_trans_handle *trans,  			    struct btrfs_root *root)  {  	DEFINE_WAIT(wait); -	while (root->fs_info->last_trans_log_full_commit != -	       trans->transid && atomic_read(&root->log_writers)) { + +	while (atomic_read(&root->log_writers)) {  		prepare_to_wait(&root->log_writer_wait,  				&wait, TASK_UNINTERRUPTIBLE);  		mutex_unlock(&root->log_mutex); -		if (root->fs_info->last_trans_log_full_commit != -		    trans->transid && atomic_read(&root->log_writers)) +		if (atomic_read(&root->log_writers))  			schedule();  		mutex_lock(&root->log_mutex);  		finish_wait(&root->log_writer_wait, &wait);  	}  } +static inline void btrfs_remove_log_ctx(struct btrfs_root *root, +					struct btrfs_log_ctx *ctx) +{ +	if (!ctx) +		return; + +	mutex_lock(&root->log_mutex); +	list_del_init(&ctx->list); +	mutex_unlock(&root->log_mutex); +} + +/*  + * Invoked in log mutex context, or be sure there is no other task which + * can access the list. + */ +static inline void btrfs_remove_all_log_ctxs(struct btrfs_root *root, +					     int index, int error) +{ +	struct btrfs_log_ctx *ctx; + +	if (!error) { +		INIT_LIST_HEAD(&root->log_ctxs[index]); +		return; +	} + +	list_for_each_entry(ctx, &root->log_ctxs[index], list) +		ctx->log_ret = error; + +	INIT_LIST_HEAD(&root->log_ctxs[index]); +} +  /*   * btrfs_sync_log does sends a given tree log down to the disk and   * updates the super blocks to record it.  When this call is done, @@ -2364,7 +2461,7 @@ static void wait_for_writer(struct btrfs_trans_handle *trans,   * that has happened.   */  int btrfs_sync_log(struct btrfs_trans_handle *trans, -		   struct btrfs_root *root) +		   struct btrfs_root *root, struct btrfs_log_ctx *ctx)  {  	int index1;  	int index2; @@ -2372,26 +2469,35 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	int ret;  	struct btrfs_root *log = root->log_root;  	struct btrfs_root *log_root_tree = root->fs_info->log_root_tree; -	unsigned long log_transid = 0; +	int log_transid = 0; +	struct btrfs_log_ctx root_log_ctx;  	struct blk_plug plug;  	mutex_lock(&root->log_mutex); -	log_transid = root->log_transid; -	index1 = root->log_transid % 2; +	log_transid = ctx->log_transid; +	if (root->log_transid_committed >= log_transid) { +		mutex_unlock(&root->log_mutex); +		return ctx->log_ret; +	} + +	index1 = log_transid % 2;  	if (atomic_read(&root->log_commit[index1])) { -		wait_log_commit(trans, root, root->log_transid); +		wait_log_commit(trans, root, log_transid);  		mutex_unlock(&root->log_mutex); -		return 0; +		return ctx->log_ret;  	} +	ASSERT(log_transid == root->log_transid);  	atomic_set(&root->log_commit[index1], 1);  	/* wait for previous tree log sync to complete */  	if (atomic_read(&root->log_commit[(index1 + 1) % 2])) -		wait_log_commit(trans, root, root->log_transid - 1); +		wait_log_commit(trans, root, log_transid - 1); +  	while (1) {  		int batch = atomic_read(&root->log_batch);  		/* when we're on an ssd, just kick the log commit out */ -		if (!btrfs_test_opt(root, SSD) && root->log_multiple_pids) { +		if (!btrfs_test_opt(root, SSD) && +		    test_bit(BTRFS_ROOT_MULTI_LOG_TASKS, &root->state)) {  			mutex_unlock(&root->log_mutex);  			schedule_timeout_uninterruptible(1);  			mutex_lock(&root->log_mutex); @@ -2402,7 +2508,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	}  	/* bail out if we need to do a full commit */ -	if (root->fs_info->last_trans_log_full_commit == trans->transid) { +	if (btrfs_need_log_full_commit(root->fs_info, trans)) {  		ret = -EAGAIN;  		btrfs_free_logged_extents(log, log_transid);  		mutex_unlock(&root->log_mutex); @@ -2423,6 +2529,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  		blk_finish_plug(&plug);  		btrfs_abort_transaction(trans, root, ret);  		btrfs_free_logged_extents(log, log_transid); +		btrfs_set_log_full_commit(root->fs_info, trans);  		mutex_unlock(&root->log_mutex);  		goto out;  	} @@ -2432,7 +2539,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	root->log_transid++;  	log->log_transid = root->log_transid;  	root->log_start_pid = 0; -	smp_mb();  	/*  	 * IO has been started, blocks of the log tree have WRITTEN flag set  	 * in their headers. new modifications of the log will be written to @@ -2440,9 +2546,16 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	 */  	mutex_unlock(&root->log_mutex); +	btrfs_init_log_ctx(&root_log_ctx); +  	mutex_lock(&log_root_tree->log_mutex);  	atomic_inc(&log_root_tree->log_batch);  	atomic_inc(&log_root_tree->log_writers); + +	index2 = log_root_tree->log_transid % 2; +	list_add_tail(&root_log_ctx.list, &log_root_tree->log_ctxs[index2]); +	root_log_ctx.log_transid = log_root_tree->log_transid; +  	mutex_unlock(&log_root_tree->log_mutex);  	ret = update_log_root(trans, log); @@ -2455,13 +2568,17 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	}  	if (ret) { +		if (!list_empty(&root_log_ctx.list)) +			list_del_init(&root_log_ctx.list); +  		blk_finish_plug(&plug); +		btrfs_set_log_full_commit(root->fs_info, trans); +  		if (ret != -ENOSPC) {  			btrfs_abort_transaction(trans, root, ret);  			mutex_unlock(&log_root_tree->log_mutex);  			goto out;  		} -		root->fs_info->last_trans_log_full_commit = trans->transid;  		btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);  		btrfs_free_logged_extents(log, log_transid);  		mutex_unlock(&log_root_tree->log_mutex); @@ -2469,22 +2586,29 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  		goto out;  	} -	index2 = log_root_tree->log_transid % 2; +	if (log_root_tree->log_transid_committed >= root_log_ctx.log_transid) { +		mutex_unlock(&log_root_tree->log_mutex); +		ret = root_log_ctx.log_ret; +		goto out; +	} + +	index2 = root_log_ctx.log_transid % 2;  	if (atomic_read(&log_root_tree->log_commit[index2])) {  		blk_finish_plug(&plug);  		btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);  		wait_log_commit(trans, log_root_tree, -				log_root_tree->log_transid); +				root_log_ctx.log_transid);  		btrfs_free_logged_extents(log, log_transid);  		mutex_unlock(&log_root_tree->log_mutex); -		ret = 0; +		ret = root_log_ctx.log_ret;  		goto out;  	} +	ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid);  	atomic_set(&log_root_tree->log_commit[index2], 1);  	if (atomic_read(&log_root_tree->log_commit[(index2 + 1) % 2])) {  		wait_log_commit(trans, log_root_tree, -				log_root_tree->log_transid - 1); +				root_log_ctx.log_transid - 1);  	}  	wait_for_writer(trans, log_root_tree); @@ -2493,7 +2617,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	 * now that we've moved on to the tree of log tree roots,  	 * check the full commit flag again  	 */ -	if (root->fs_info->last_trans_log_full_commit == trans->transid) { +	if (btrfs_need_log_full_commit(root->fs_info, trans)) {  		blk_finish_plug(&plug);  		btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark);  		btrfs_free_logged_extents(log, log_transid); @@ -2507,6 +2631,7 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  					 EXTENT_DIRTY | EXTENT_NEW);  	blk_finish_plug(&plug);  	if (ret) { +		btrfs_set_log_full_commit(root->fs_info, trans);  		btrfs_abort_transaction(trans, root, ret);  		btrfs_free_logged_extents(log, log_transid);  		mutex_unlock(&log_root_tree->log_mutex); @@ -2524,8 +2649,6 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  				btrfs_header_level(log_root_tree->node));  	log_root_tree->log_transid++; -	smp_mb(); -  	mutex_unlock(&log_root_tree->log_mutex);  	/* @@ -2535,10 +2658,9 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	 * the running transaction open, so a full commit can't hop  	 * in and cause problems either.  	 */ -	btrfs_scrub_pause_super(root);  	ret = write_ctree_super(trans, root->fs_info->tree_root, 1); -	btrfs_scrub_continue_super(root);  	if (ret) { +		btrfs_set_log_full_commit(root->fs_info, trans);  		btrfs_abort_transaction(trans, root, ret);  		goto out_wake_log_root;  	} @@ -2549,13 +2671,28 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	mutex_unlock(&root->log_mutex);  out_wake_log_root: +	/* +	 * We needn't get log_mutex here because we are sure all +	 * the other tasks are blocked. +	 */ +	btrfs_remove_all_log_ctxs(log_root_tree, index2, ret); + +	mutex_lock(&log_root_tree->log_mutex); +	log_root_tree->log_transid_committed++;  	atomic_set(&log_root_tree->log_commit[index2], 0); -	smp_mb(); +	mutex_unlock(&log_root_tree->log_mutex); +  	if (waitqueue_active(&log_root_tree->log_commit_wait[index2]))  		wake_up(&log_root_tree->log_commit_wait[index2]);  out: +	/* See above. */ +	btrfs_remove_all_log_ctxs(root, index1, ret); + +	mutex_lock(&root->log_mutex); +	root->log_transid_committed++;  	atomic_set(&root->log_commit[index1], 0); -	smp_mb(); +	mutex_unlock(&root->log_mutex); +  	if (waitqueue_active(&root->log_commit_wait[index1]))  		wake_up(&root->log_commit_wait[index1]);  	return ret; @@ -2572,13 +2709,10 @@ static void free_log_tree(struct btrfs_trans_handle *trans,  		.process_func = process_one_buffer  	}; -	if (trans) { -		ret = walk_log_tree(trans, log, &wc); - -		/* I don't think this can happen but just in case */ -		if (ret) -			btrfs_abort_transaction(trans, log, ret); -	} +	ret = walk_log_tree(trans, log, &wc); +	/* I don't think this can happen but just in case */ +	if (ret) +		btrfs_abort_transaction(trans, log, ret);  	while (1) {  		ret = find_first_extent_bit(&log->dirty_log_pages, @@ -2744,7 +2878,7 @@ fail:  out_unlock:  	mutex_unlock(&BTRFS_I(dir)->log_mutex);  	if (ret == -ENOSPC) { -		root->fs_info->last_trans_log_full_commit = trans->transid; +		btrfs_set_log_full_commit(root->fs_info, trans);  		ret = 0;  	} else if (ret < 0)  		btrfs_abort_transaction(trans, root, ret); @@ -2777,7 +2911,7 @@ int btrfs_del_inode_ref_in_log(struct btrfs_trans_handle *trans,  				  dirid, &index);  	mutex_unlock(&BTRFS_I(inode)->log_mutex);  	if (ret == -ENOSPC) { -		root->fs_info->last_trans_log_full_commit = trans->transid; +		btrfs_set_log_full_commit(root->fs_info, trans);  		ret = 0;  	} else if (ret < 0 && ret != -ENOENT)  		btrfs_abort_transaction(trans, root, ret); @@ -2831,7 +2965,6 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,  			  u64 min_offset, u64 *last_offset_ret)  {  	struct btrfs_key min_key; -	struct btrfs_key max_key;  	struct btrfs_root *log = root->log_root;  	struct extent_buffer *src;  	int err = 0; @@ -2843,9 +2976,6 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,  	u64 ino = btrfs_ino(inode);  	log = root->log_root; -	max_key.objectid = ino; -	max_key.offset = (u64)-1; -	max_key.type = key_type;  	min_key.objectid = ino;  	min_key.type = key_type; @@ -2853,8 +2983,7 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,  	path->keep_locks = 1; -	ret = btrfs_search_forward(root, &min_key, &max_key, -				   path, trans->transid); +	ret = btrfs_search_forward(root, &min_key, path, trans->transid);  	/*  	 * we didn't find anything from this transaction, see if there @@ -2907,10 +3036,8 @@ static noinline int log_dir_items(struct btrfs_trans_handle *trans,  	/* find the first key from this transaction again */  	ret = btrfs_search_slot(NULL, root, &min_key, path, 0, 0); -	if (ret != 0) { -		WARN_ON(1); +	if (WARN_ON(ret != 0))  		goto done; -	}  	/*  	 * we have a block from this transaction, log every item in it @@ -3136,11 +3263,10 @@ static int log_inode_item(struct btrfs_trans_handle *trans,  			  struct inode *inode)  {  	struct btrfs_inode_item *inode_item; -	struct btrfs_key key;  	int ret; -	memcpy(&key, &BTRFS_I(inode)->location, sizeof(key)); -	ret = btrfs_insert_empty_item(trans, log, path, &key, +	ret = btrfs_insert_empty_item(trans, log, path, +				      &BTRFS_I(inode)->location,  				      sizeof(*inode_item));  	if (ret && ret != -EEXIST)  		return ret; @@ -3154,7 +3280,7 @@ static int log_inode_item(struct btrfs_trans_handle *trans,  static noinline int copy_items(struct btrfs_trans_handle *trans,  			       struct inode *inode,  			       struct btrfs_path *dst_path, -			       struct extent_buffer *src, +			       struct btrfs_path *src_path, u64 *last_extent,  			       int start_slot, int nr, int inode_only)  {  	unsigned long src_offset; @@ -3162,6 +3288,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  	struct btrfs_root *log = BTRFS_I(inode)->root->log_root;  	struct btrfs_file_extent_item *extent;  	struct btrfs_inode_item *inode_item; +	struct extent_buffer *src = src_path->nodes[0]; +	struct btrfs_key first_key, last_key, key;  	int ret;  	struct btrfs_key *ins_keys;  	u32 *ins_sizes; @@ -3169,6 +3297,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  	int i;  	struct list_head ordered_sums;  	int skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; +	bool has_extents = false; +	bool need_find_last_extent = (*last_extent == 0); +	bool done = false;  	INIT_LIST_HEAD(&ordered_sums); @@ -3177,6 +3308,8 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  	if (!ins_data)  		return -ENOMEM; +	first_key.objectid = (u64)-1; +  	ins_sizes = (u32 *)ins_data;  	ins_keys = (struct btrfs_key *)(ins_data + nr * sizeof(u32)); @@ -3197,6 +3330,9 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  		src_offset = btrfs_item_ptr_offset(src, start_slot + i); +		if ((i == (nr - 1))) +			last_key = ins_keys[i]; +  		if (ins_keys[i].type == BTRFS_INODE_ITEM_KEY) {  			inode_item = btrfs_item_ptr(dst_path->nodes[0],  						    dst_path->slots[0], @@ -3208,6 +3344,21 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  					   src_offset, ins_sizes[i]);  		} +		/* +		 * We set need_find_last_extent here in case we know we were +		 * processing other items and then walk into the first extent in +		 * the inode.  If we don't hit an extent then nothing changes, +		 * we'll do the last search the next time around. +		 */ +		if (ins_keys[i].type == BTRFS_EXTENT_DATA_KEY) { +			has_extents = true; +			if (need_find_last_extent && +			    first_key.objectid == (u64)-1) +				first_key = ins_keys[i]; +		} else { +			need_find_last_extent = false; +		} +  		/* take a reference on file data extents so that truncates  		 * or deletes of this inode don't have to relog the inode  		 * again @@ -3272,6 +3423,128 @@ static noinline int copy_items(struct btrfs_trans_handle *trans,  		list_del(&sums->list);  		kfree(sums);  	} + +	if (!has_extents) +		return ret; + +	/* +	 * Because we use btrfs_search_forward we could skip leaves that were +	 * not modified and then assume *last_extent is valid when it really +	 * isn't.  So back up to the previous leaf and read the end of the last +	 * extent before we go and fill in holes. +	 */ +	if (need_find_last_extent) { +		u64 len; + +		ret = btrfs_prev_leaf(BTRFS_I(inode)->root, src_path); +		if (ret < 0) +			return ret; +		if (ret) +			goto fill_holes; +		if (src_path->slots[0]) +			src_path->slots[0]--; +		src = src_path->nodes[0]; +		btrfs_item_key_to_cpu(src, &key, src_path->slots[0]); +		if (key.objectid != btrfs_ino(inode) || +		    key.type != BTRFS_EXTENT_DATA_KEY) +			goto fill_holes; +		extent = btrfs_item_ptr(src, src_path->slots[0], +					struct btrfs_file_extent_item); +		if (btrfs_file_extent_type(src, extent) == +		    BTRFS_FILE_EXTENT_INLINE) { +			len = btrfs_file_extent_inline_len(src, +							   src_path->slots[0], +							   extent); +			*last_extent = ALIGN(key.offset + len, +					     log->sectorsize); +		} else { +			len = btrfs_file_extent_num_bytes(src, extent); +			*last_extent = key.offset + len; +		} +	} +fill_holes: +	/* So we did prev_leaf, now we need to move to the next leaf, but a few +	 * things could have happened +	 * +	 * 1) A merge could have happened, so we could currently be on a leaf +	 * that holds what we were copying in the first place. +	 * 2) A split could have happened, and now not all of the items we want +	 * are on the same leaf. +	 * +	 * So we need to adjust how we search for holes, we need to drop the +	 * path and re-search for the first extent key we found, and then walk +	 * forward until we hit the last one we copied. +	 */ +	if (need_find_last_extent) { +		/* btrfs_prev_leaf could return 1 without releasing the path */ +		btrfs_release_path(src_path); +		ret = btrfs_search_slot(NULL, BTRFS_I(inode)->root, &first_key, +					src_path, 0, 0); +		if (ret < 0) +			return ret; +		ASSERT(ret == 0); +		src = src_path->nodes[0]; +		i = src_path->slots[0]; +	} else { +		i = start_slot; +	} + +	/* +	 * Ok so here we need to go through and fill in any holes we may have +	 * to make sure that holes are punched for those areas in case they had +	 * extents previously. +	 */ +	while (!done) { +		u64 offset, len; +		u64 extent_end; + +		if (i >= btrfs_header_nritems(src_path->nodes[0])) { +			ret = btrfs_next_leaf(BTRFS_I(inode)->root, src_path); +			if (ret < 0) +				return ret; +			ASSERT(ret == 0); +			src = src_path->nodes[0]; +			i = 0; +		} + +		btrfs_item_key_to_cpu(src, &key, i); +		if (!btrfs_comp_cpu_keys(&key, &last_key)) +			done = true; +		if (key.objectid != btrfs_ino(inode) || +		    key.type != BTRFS_EXTENT_DATA_KEY) { +			i++; +			continue; +		} +		extent = btrfs_item_ptr(src, i, struct btrfs_file_extent_item); +		if (btrfs_file_extent_type(src, extent) == +		    BTRFS_FILE_EXTENT_INLINE) { +			len = btrfs_file_extent_inline_len(src, i, extent); +			extent_end = ALIGN(key.offset + len, log->sectorsize); +		} else { +			len = btrfs_file_extent_num_bytes(src, extent); +			extent_end = key.offset + len; +		} +		i++; + +		if (*last_extent == key.offset) { +			*last_extent = extent_end; +			continue; +		} +		offset = *last_extent; +		len = key.offset - *last_extent; +		ret = btrfs_insert_file_extent(trans, log, btrfs_ino(inode), +					       offset, 0, 0, len, 0, len, 0, +					       0, 0); +		if (ret) +			break; +		*last_extent = offset + len; +	} +	/* +	 * Need to let the callers know we dropped the path so they should +	 * re-search. +	 */ +	if (!ret && need_find_last_extent) +		ret = 1;  	return ret;  } @@ -3291,7 +3564,8 @@ static int extent_cmp(void *priv, struct list_head *a, struct list_head *b)  static int log_one_extent(struct btrfs_trans_handle *trans,  			  struct inode *inode, struct btrfs_root *root, -			  struct extent_map *em, struct btrfs_path *path) +			  struct extent_map *em, struct btrfs_path *path, +			  struct list_head *logged_list)  {  	struct btrfs_root *log = root->log_root;  	struct btrfs_file_extent_item *fi; @@ -3307,23 +3581,28 @@ static int log_one_extent(struct btrfs_trans_handle *trans,  	u64 extent_offset = em->start - em->orig_start;  	u64 block_len;  	int ret; -	int index = log->log_transid % 2;  	bool skip_csum = BTRFS_I(inode)->flags & BTRFS_INODE_NODATASUM; - -	ret = __btrfs_drop_extents(trans, log, inode, path, em->start, -				   em->start + em->len, NULL, 0); -	if (ret) -		return ret; +	int extent_inserted = 0;  	INIT_LIST_HEAD(&ordered_sums);  	btrfs_init_map_token(&token); -	key.objectid = btrfs_ino(inode); -	key.type = BTRFS_EXTENT_DATA_KEY; -	key.offset = em->start; -	ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*fi)); +	ret = __btrfs_drop_extents(trans, log, inode, path, em->start, +				   em->start + em->len, NULL, 0, 1, +				   sizeof(*fi), &extent_inserted);  	if (ret)  		return ret; + +	if (!extent_inserted) { +		key.objectid = btrfs_ino(inode); +		key.type = BTRFS_EXTENT_DATA_KEY; +		key.offset = em->start; + +		ret = btrfs_insert_empty_item(trans, log, path, &key, +					      sizeof(*fi)); +		if (ret) +			return ret; +	}  	leaf = path->nodes[0];  	fi = btrfs_item_ptr(leaf, path->slots[0],  			    struct btrfs_file_extent_item); @@ -3339,7 +3618,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,  		btrfs_set_token_file_extent_type(leaf, fi,  						 BTRFS_FILE_EXTENT_REG,  						 &token); -		if (em->block_start == 0) +		if (em->block_start == EXTENT_MAP_HOLE)  			skip_csum = true;  	} @@ -3381,26 +3660,16 @@ static int log_one_extent(struct btrfs_trans_handle *trans,  	if (skip_csum)  		return 0; -	if (em->compress_type) { -		csum_offset = 0; -		csum_len = block_len; -	} -  	/*  	 * First check and see if our csums are on our outstanding ordered  	 * extents.  	 */ -again: -	spin_lock_irq(&log->log_extents_lock[index]); -	list_for_each_entry(ordered, &log->logged_list[index], log_list) { +	list_for_each_entry(ordered, logged_list, log_list) {  		struct btrfs_ordered_sum *sum;  		if (!mod_len)  			break; -		if (ordered->inode != inode) -			continue; -  		if (ordered->file_offset + ordered->len <= mod_start ||  		    mod_start + mod_len <= ordered->file_offset)  			continue; @@ -3443,34 +3712,32 @@ again:  		if (test_and_set_bit(BTRFS_ORDERED_LOGGED_CSUM,  				     &ordered->flags))  			continue; -		atomic_inc(&ordered->refs); -		spin_unlock_irq(&log->log_extents_lock[index]); -		/* -		 * we've dropped the lock, we must either break or -		 * start over after this. -		 */ -		wait_event(ordered->wait, ordered->csum_bytes_left == 0); +		if (ordered->csum_bytes_left) { +			btrfs_start_ordered_extent(inode, ordered, 0); +			wait_event(ordered->wait, +				   ordered->csum_bytes_left == 0); +		}  		list_for_each_entry(sum, &ordered->list, list) {  			ret = btrfs_csum_file_blocks(trans, log, sum); -			if (ret) { -				btrfs_put_ordered_extent(ordered); +			if (ret)  				goto unlocked; -			}  		} -		btrfs_put_ordered_extent(ordered); -		goto again;  	} -	spin_unlock_irq(&log->log_extents_lock[index]);  unlocked:  	if (!mod_len || ret)  		return ret; -	csum_offset = mod_start - em->start; -	csum_len = mod_len; +	if (em->compress_type) { +		csum_offset = 0; +		csum_len = block_len; +	} else { +		csum_offset = mod_start - em->start; +		csum_len = mod_len; +	}  	/* block start is already adjusted for the file extent offset. */  	ret = btrfs_lookup_csums_range(log->fs_info->csum_root, @@ -3496,7 +3763,8 @@ unlocked:  static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,  				     struct btrfs_root *root,  				     struct inode *inode, -				     struct btrfs_path *path) +				     struct btrfs_path *path, +				     struct list_head *logged_list)  {  	struct extent_map *em, *n;  	struct list_head extents; @@ -3554,7 +3822,7 @@ process:  		write_unlock(&tree->lock); -		ret = log_one_extent(trans, inode, root, em, path); +		ret = log_one_extent(trans, inode, root, em, path, logged_list);  		write_lock(&tree->lock);  		clear_em_logging(tree, em);  		free_extent_map(em); @@ -3590,6 +3858,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  	struct btrfs_key max_key;  	struct btrfs_root *log = root->log_root;  	struct extent_buffer *src = NULL; +	LIST_HEAD(logged_list); +	u64 last_extent = 0;  	int err = 0;  	int ret;  	int nritems; @@ -3637,7 +3907,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  	mutex_lock(&BTRFS_I(inode)->log_mutex); -	btrfs_get_logged_extents(log, inode); +	btrfs_get_logged_extents(inode, &logged_list);  	/*  	 * a brute force approach to making sure we get the most uptodate @@ -3657,7 +3927,8 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  			ret = btrfs_truncate_inode_items(trans, log,  							 inode, 0, 0);  		} else if (test_and_clear_bit(BTRFS_INODE_COPY_EVERYTHING, -					      &BTRFS_I(inode)->runtime_flags)) { +					      &BTRFS_I(inode)->runtime_flags) || +			   inode_only == LOG_INODE_EXISTS) {  			if (inode_only == LOG_INODE_ALL)  				fast_search = true;  			max_key.type = BTRFS_XATTR_ITEM_KEY; @@ -3683,7 +3954,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  	while (1) {  		ins_nr = 0; -		ret = btrfs_search_forward(root, &min_key, &max_key, +		ret = btrfs_search_forward(root, &min_key,  					   path, trans->transid);  		if (ret != 0)  			break; @@ -3704,11 +3975,15 @@ again:  			goto next_slot;  		} -		ret = copy_items(trans, inode, dst_path, src, ins_start_slot, -				 ins_nr, inode_only); -		if (ret) { +		ret = copy_items(trans, inode, dst_path, path, &last_extent, +				 ins_start_slot, ins_nr, inode_only); +		if (ret < 0) {  			err = ret;  			goto out_unlock; +		} if (ret) { +			ins_nr = 0; +			btrfs_release_path(path); +			continue;  		}  		ins_nr = 1;  		ins_start_slot = path->slots[0]; @@ -3722,33 +3997,35 @@ next_slot:  			goto again;  		}  		if (ins_nr) { -			ret = copy_items(trans, inode, dst_path, src, -					 ins_start_slot, +			ret = copy_items(trans, inode, dst_path, path, +					 &last_extent, ins_start_slot,  					 ins_nr, inode_only); -			if (ret) { +			if (ret < 0) {  				err = ret;  				goto out_unlock;  			} +			ret = 0;  			ins_nr = 0;  		}  		btrfs_release_path(path); -		if (min_key.offset < (u64)-1) +		if (min_key.offset < (u64)-1) {  			min_key.offset++; -		else if (min_key.type < (u8)-1) +		} else if (min_key.type < max_key.type) {  			min_key.type++; -		else if (min_key.objectid < (u64)-1) -			min_key.objectid++; -		else +			min_key.offset = 0; +		} else {  			break; +		}  	}  	if (ins_nr) { -		ret = copy_items(trans, inode, dst_path, src, ins_start_slot, -				 ins_nr, inode_only); -		if (ret) { +		ret = copy_items(trans, inode, dst_path, path, &last_extent, +				 ins_start_slot, ins_nr, inode_only); +		if (ret < 0) {  			err = ret;  			goto out_unlock;  		} +		ret = 0;  		ins_nr = 0;  	} @@ -3756,12 +4033,13 @@ log_extents:  	btrfs_release_path(path);  	btrfs_release_path(dst_path);  	if (fast_search) { -		ret = btrfs_log_changed_extents(trans, root, inode, dst_path); +		ret = btrfs_log_changed_extents(trans, root, inode, dst_path, +						&logged_list);  		if (ret) {  			err = ret;  			goto out_unlock;  		} -	} else { +	} else if (inode_only == LOG_INODE_ALL) {  		struct extent_map_tree *tree = &BTRFS_I(inode)->extent_tree;  		struct extent_map *em, *n; @@ -3781,8 +4059,10 @@ log_extents:  	BTRFS_I(inode)->logged_trans = trans->transid;  	BTRFS_I(inode)->last_log_commit = BTRFS_I(inode)->last_sub_trans;  out_unlock: -	if (err) -		btrfs_free_logged_extents(log, log->log_transid); +	if (unlikely(err)) +		btrfs_put_logged_extents(&logged_list); +	else +		btrfs_submit_logged_extents(&logged_list, log);  	mutex_unlock(&BTRFS_I(inode)->log_mutex);  	btrfs_free_path(path); @@ -3805,6 +4085,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  	int ret = 0;  	struct btrfs_root *root;  	struct dentry *old_parent = NULL; +	struct inode *orig_inode = inode;  	/*  	 * for regular files, if its inode is already on disk, we don't @@ -3824,7 +4105,14 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  	}  	while (1) { -		BTRFS_I(inode)->logged_trans = trans->transid; +		/* +		 * If we are logging a directory then we start with our inode, +		 * not our parents inode, so we need to skipp setting the +		 * logged_trans so that further down in the log code we don't +		 * think this inode has already been logged. +		 */ +		if (inode != orig_inode) +			BTRFS_I(inode)->logged_trans = trans->transid;  		smp_mb();  		if (BTRFS_I(inode)->last_unlink_trans > last_committed) { @@ -3834,8 +4122,7 @@ static noinline int check_parent_dirs_for_sync(struct btrfs_trans_handle *trans,  			 * make sure any commits to the log are forced  			 * to be full commits  			 */ -			root->fs_info->last_trans_log_full_commit = -				trans->transid; +			btrfs_set_log_full_commit(root->fs_info, trans);  			ret = 1;  			break;  		} @@ -3865,7 +4152,8 @@ out:   */  static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,  			    	  struct btrfs_root *root, struct inode *inode, -			    	  struct dentry *parent, int exists_only) +			    	  struct dentry *parent, int exists_only, +				  struct btrfs_log_ctx *ctx)  {  	int inode_only = exists_only ? LOG_INODE_EXISTS : LOG_INODE_ALL;  	struct super_block *sb; @@ -3880,6 +4168,10 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,  		goto end_no_trans;  	} +	/* +	 * The prev transaction commit doesn't complete, we need do +	 * full commit by ourselves. +	 */  	if (root->fs_info->last_trans_log_full_commit >  	    root->fs_info->last_trans_committed) {  		ret = 1; @@ -3902,9 +4194,9 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,  		goto end_no_trans;  	} -	ret = start_log_trans(trans, root); +	ret = start_log_trans(trans, root, ctx);  	if (ret) -		goto end_trans; +		goto end_no_trans;  	ret = btrfs_log_inode(trans, root, inode, inode_only);  	if (ret) @@ -3949,9 +4241,12 @@ static int btrfs_log_inode_parent(struct btrfs_trans_handle *trans,  end_trans:  	dput(old_parent);  	if (ret < 0) { -		root->fs_info->last_trans_log_full_commit = trans->transid; +		btrfs_set_log_full_commit(root->fs_info, trans);  		ret = 1;  	} + +	if (ret) +		btrfs_remove_log_ctx(root, ctx);  	btrfs_end_log_trans(root);  end_no_trans:  	return ret; @@ -3964,12 +4259,14 @@ end_no_trans:   * data on disk.   */  int btrfs_log_dentry_safe(struct btrfs_trans_handle *trans, -			  struct btrfs_root *root, struct dentry *dentry) +			  struct btrfs_root *root, struct dentry *dentry, +			  struct btrfs_log_ctx *ctx)  {  	struct dentry *parent = dget_parent(dentry);  	int ret; -	ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, 0); +	ret = btrfs_log_inode_parent(trans, root, dentry->d_inode, parent, +				     0, ctx);  	dput(parent);  	return ret; @@ -4206,6 +4503,6 @@ int btrfs_log_new_name(struct btrfs_trans_handle *trans,  		    root->fs_info->last_trans_committed))  		return 0; -	return btrfs_log_inode_parent(trans, root, inode, parent, 1); +	return btrfs_log_inode_parent(trans, root, inode, parent, 1, NULL);  }  | 
