diff options
Diffstat (limited to 'fs/btrfs/tree-log.c')
| -rw-r--r-- | fs/btrfs/tree-log.c | 50 | 
1 files changed, 40 insertions, 10 deletions
diff --git a/fs/btrfs/tree-log.c b/fs/btrfs/tree-log.c index 286213cec861..9a02da16f2be 100644 --- a/fs/btrfs/tree-log.c +++ b/fs/btrfs/tree-log.c @@ -2599,12 +2599,14 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  	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); +		ret = btrfs_wait_marked_extents(log, &log->dirty_log_pages, +						mark); +		btrfs_wait_logged_extents(trans, log, log_transid);  		wait_log_commit(trans, log_root_tree,  				root_log_ctx.log_transid); -		btrfs_free_logged_extents(log, log_transid);  		mutex_unlock(&log_root_tree->log_mutex); -		ret = root_log_ctx.log_ret; +		if (!ret) +			ret = root_log_ctx.log_ret;  		goto out;  	}  	ASSERT(root_log_ctx.log_transid == log_root_tree->log_transid); @@ -2641,11 +2643,18 @@ int btrfs_sync_log(struct btrfs_trans_handle *trans,  		mutex_unlock(&log_root_tree->log_mutex);  		goto out_wake_log_root;  	} -	btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark); -	btrfs_wait_marked_extents(log_root_tree, -				  &log_root_tree->dirty_log_pages, -				  EXTENT_NEW | EXTENT_DIRTY); -	btrfs_wait_logged_extents(log, log_transid); +	ret = btrfs_wait_marked_extents(log, &log->dirty_log_pages, mark); +	if (!ret) +		ret = btrfs_wait_marked_extents(log_root_tree, +						&log_root_tree->dirty_log_pages, +						EXTENT_NEW | EXTENT_DIRTY); +	if (ret) { +		btrfs_set_log_full_commit(root->fs_info, trans); +		btrfs_free_logged_extents(log, log_transid); +		mutex_unlock(&log_root_tree->log_mutex); +		goto out_wake_log_root; +	} +	btrfs_wait_logged_extents(trans, log, log_transid);  	btrfs_set_super_log_root(root->fs_info->super_for_commit,  				log_root_tree->node->start); @@ -3626,6 +3635,12 @@ static int wait_ordered_extents(struct btrfs_trans_handle *trans,  			    test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)));  		if (test_bit(BTRFS_ORDERED_IOERR, &ordered->flags)) { +			/* +			 * Clear the AS_EIO/AS_ENOSPC flags from the inode's +			 * i_mapping flags, so that the next fsync won't get +			 * an outdated io error too. +			 */ +			btrfs_inode_check_errors(inode);  			*ordered_io_error = true;  			break;  		} @@ -3766,7 +3781,7 @@ static int log_one_extent(struct btrfs_trans_handle *trans,  	fi = btrfs_item_ptr(leaf, path->slots[0],  			    struct btrfs_file_extent_item); -	btrfs_set_token_file_extent_generation(leaf, fi, em->generation, +	btrfs_set_token_file_extent_generation(leaf, fi, trans->transid,  					       &token);  	if (test_bit(EXTENT_FLAG_PREALLOC, &em->flags))  		btrfs_set_token_file_extent_type(leaf, fi, @@ -3963,7 +3978,7 @@ static int btrfs_log_inode(struct btrfs_trans_handle *trans,  	mutex_lock(&BTRFS_I(inode)->log_mutex); -	btrfs_get_logged_extents(inode, &logged_list); +	btrfs_get_logged_extents(inode, &logged_list, start, end);  	/*  	 * a brute force approach to making sure we get the most uptodate @@ -4089,6 +4104,21 @@ log_extents:  	btrfs_release_path(path);  	btrfs_release_path(dst_path);  	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);  		if (ret) {  |