diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 66 | 
1 files changed, 23 insertions, 43 deletions
| diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 16a74d1a2720..6aaab31a722e 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -4141,6 +4141,7 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,  	INIT_LIST_HEAD(&extents); +	down_write(&BTRFS_I(inode)->dio_sem);  	write_lock(&tree->lock);  	test_gen = root->fs_info->last_trans_committed; @@ -4169,13 +4170,20 @@ static int btrfs_log_changed_extents(struct btrfs_trans_handle *trans,  	}  	list_sort(NULL, &extents, extent_cmp); +	btrfs_get_logged_extents(inode, logged_list, start, end);  	/* -	 * Collect any new ordered extents within the range. This is to -	 * prevent logging file extent items without waiting for the disk -	 * location they point to being written. We do this only to deal -	 * with races against concurrent lockless direct IO writes. +	 * Some ordered extents started by fsync might have completed +	 * before we could collect them into the list logged_list, which +	 * means they're gone, not in our logged_list nor in the inode's +	 * ordered tree. We want the application/user space to know an +	 * error happened while attempting to persist file data so that +	 * it can take proper action. If such error happened, we leave +	 * without writing to the log tree and the fsync must report the +	 * file data write error and not commit the current transaction.  	 */ -	btrfs_get_logged_extents(inode, logged_list, start, end); +	ret = btrfs_inode_check_errors(inode); +	if (ret) +		ctx->io_err = ret;  process:  	while (!list_empty(&extents)) {  		em = list_entry(extents.next, struct extent_map, list); @@ -4202,6 +4210,7 @@ process:  	}  	WARN_ON(!list_empty(&extents));  	write_unlock(&tree->lock); +	up_write(&BTRFS_I(inode)->dio_sem);  	btrfs_release_path(path);  	return ret; @@ -4623,23 +4632,6 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  	mutex_lock(&BTRFS_I(inode)->log_mutex);  	/* -	 * Collect ordered extents only if we are logging data. This is to -	 * ensure a subsequent request to log this inode in LOG_INODE_ALL mode -	 * will process the ordered extents if they still exists at the time, -	 * because when we collect them we test and set for the flag -	 * BTRFS_ORDERED_LOGGED to prevent multiple log requests to process the -	 * same ordered extents. The consequence for the LOG_INODE_ALL log mode -	 * not processing the ordered extents is that we end up logging the -	 * corresponding file extent items, based on the extent maps in the -	 * inode's extent_map_tree's modified_list, without logging the -	 * respective checksums (since the may still be only attached to the -	 * ordered extents and have not been inserted in the csum tree by -	 * btrfs_finish_ordered_io() yet). -	 */ -	if (inode_only == LOG_INODE_ALL) -		btrfs_get_logged_extents(inode, &logged_list, start, end); - -	/*  	 * a brute force approach to making sure we get the most uptodate  	 * copies of everything.  	 */ @@ -4846,21 +4838,6 @@ log_extents:  			goto out_unlock;  	}  	if (fast_search) { -		/* -		 * Some ordered extents started by fsync might have completed -		 * before we collected the ordered extents in logged_list, which -		 * means they're gone, not in our logged_list nor in the inode's -		 * ordered tree. We want the application/user space to know an -		 * error happened while attempting to persist file data so that -		 * it can take proper action. If such error happened, we leave -		 * without writing to the log tree and the fsync must report the -		 * file data write error and not commit the current transaction. -		 */ -		err = btrfs_inode_check_errors(inode); -		if (err) { -			ctx->io_err = err; -			goto out_unlock; -		}  		ret = btrfs_log_changed_extents(trans, root, inode, dst_path,  						&logged_list, ctx, start, end);  		if (ret) { @@ -5158,7 +5135,7 @@ process_leaf:  			}  			ctx->log_new_dentries = false; -			if (type == BTRFS_FT_DIR) +			if (type == BTRFS_FT_DIR || type == BTRFS_FT_SYMLINK)  				log_mode = LOG_INODE_ALL;  			btrfs_release_path(path);  			ret = btrfs_log_inode(trans, root, di_inode, @@ -5278,11 +5255,16 @@ static int btrfs_log_all_parents(struct btrfs_trans_handle *trans,  			if (IS_ERR(dir_inode))  				continue; +			if (ctx) +				ctx->log_new_dentries = false;  			ret = btrfs_log_inode(trans, root, dir_inode,  					      LOG_INODE_ALL, 0, LLONG_MAX, ctx);  			if (!ret &&  			    btrfs_must_commit_transaction(trans, dir_inode))  				ret = 1; +			if (!ret && ctx && ctx->log_new_dentries) +				ret = log_new_dir_dentries(trans, root, +							   dir_inode, ctx);  			iput(dir_inode);  			if (ret)  				goto out; @@ -5652,11 +5634,9 @@ void btrfs_record_unlink_dir(struct btrfs_trans_handle *trans,  	 * into the file.  When the file is logged we check it and  	 * don't log the parents if the file is fully on disk.  	 */ -	if (S_ISREG(inode->i_mode)) { -		mutex_lock(&BTRFS_I(inode)->log_mutex); -		BTRFS_I(inode)->last_unlink_trans = trans->transid; -		mutex_unlock(&BTRFS_I(inode)->log_mutex); -	} +	mutex_lock(&BTRFS_I(inode)->log_mutex); +	BTRFS_I(inode)->last_unlink_trans = trans->transid; +	mutex_unlock(&BTRFS_I(inode)->log_mutex);  	/*  	 * if this directory was already logged any new | 
