Btrfs: avoid starting a transaction in the write path
I noticed while looking at a deadlock that we are always starting a transaction in cow_file_range(). This isn't really needed since we only need a transaction if we are doing an inline extent, or if the allocator needs to allocate a chunk. So push down all the transaction start stuff to be closer to where we actually need a transaction in all of these cases. This will hopefully reduce our write latency when we are committing often. Thanks, Signed-off-by: Josef Bacik <jbacik@fusionio.com> Signed-off-by: Chris Mason <chris.mason@fusionio.com>
This commit is contained in:
		
							parent
							
								
									9ffba8cda9
								
							
						
					
					
						commit
						00361589d2
					
				| @ -3165,11 +3165,9 @@ int btrfs_alloc_logged_file_extent(struct btrfs_trans_handle *trans, | ||||
| 				   struct btrfs_root *root, | ||||
| 				   u64 root_objectid, u64 owner, u64 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, | ||||
| 				  u64 empty_size, u64 hint_byte, | ||||
| 				  struct btrfs_key *ins, int is_data); | ||||
| int btrfs_reserve_extent(struct btrfs_root *root, u64 num_bytes, | ||||
| 			 u64 min_alloc_size, u64 empty_size, u64 hint_byte, | ||||
| 			 struct btrfs_key *ins, int is_data); | ||||
| int btrfs_inc_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | ||||
| 		  struct extent_buffer *buf, int full_backref, int for_cow); | ||||
| int btrfs_dec_ref(struct btrfs_trans_handle *trans, struct btrfs_root *root, | ||||
| @ -3612,8 +3610,7 @@ void btrfs_wait_and_free_delalloc_work(struct btrfs_delalloc_work *work); | ||||
| struct extent_map *btrfs_get_extent_fiemap(struct inode *inode, struct page *page, | ||||
| 					   size_t pg_offset, u64 start, u64 len, | ||||
| 					   int create); | ||||
| noinline int can_nocow_extent(struct btrfs_trans_handle *trans, | ||||
| 			      struct inode *inode, u64 offset, u64 *len, | ||||
| noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len, | ||||
| 			      u64 *orig_start, u64 *orig_block_len, | ||||
| 			      u64 *ram_bytes); | ||||
| 
 | ||||
|  | ||||
| @ -6121,8 +6121,7 @@ enum btrfs_loop_type { | ||||
|  * ins->offset == number of blocks | ||||
|  * Any available blocks before search_start are skipped. | ||||
|  */ | ||||
| static noinline int find_free_extent(struct btrfs_trans_handle *trans, | ||||
| 				     struct btrfs_root *orig_root, | ||||
| static noinline int find_free_extent(struct btrfs_root *orig_root, | ||||
| 				     u64 num_bytes, u64 empty_size, | ||||
| 				     u64 hint_byte, struct btrfs_key *ins, | ||||
| 				     u64 flags) | ||||
| @ -6345,10 +6344,10 @@ refill_cluster: | ||||
| 					      block_group->full_stripe_len); | ||||
| 
 | ||||
| 			/* allocate a cluster in this block group */ | ||||
| 			ret = btrfs_find_space_cluster(trans, root, | ||||
| 					       block_group, last_ptr, | ||||
| 					       search_start, num_bytes, | ||||
| 					       aligned_cluster); | ||||
| 			ret = btrfs_find_space_cluster(root, block_group, | ||||
| 						       last_ptr, search_start, | ||||
| 						       num_bytes, | ||||
| 						       aligned_cluster); | ||||
| 			if (ret == 0) { | ||||
| 				/*
 | ||||
| 				 * now pull our allocation out of this | ||||
| @ -6479,17 +6478,28 @@ loop: | ||||
| 		index = 0; | ||||
| 		loop++; | ||||
| 		if (loop == LOOP_ALLOC_CHUNK) { | ||||
| 			struct btrfs_trans_handle *trans; | ||||
| 
 | ||||
| 			trans = btrfs_join_transaction(root); | ||||
| 			if (IS_ERR(trans)) { | ||||
| 				ret = PTR_ERR(trans); | ||||
| 				goto out; | ||||
| 			} | ||||
| 
 | ||||
| 			ret = do_chunk_alloc(trans, root, flags, | ||||
| 					     CHUNK_ALLOC_FORCE); | ||||
| 			/*
 | ||||
| 			 * Do not bail out on ENOSPC since we | ||||
| 			 * can do more things. | ||||
| 			 */ | ||||
| 			if (ret < 0 && ret != -ENOSPC) { | ||||
| 			if (ret < 0 && ret != -ENOSPC) | ||||
| 				btrfs_abort_transaction(trans, | ||||
| 							root, ret); | ||||
| 			else | ||||
| 				ret = 0; | ||||
| 			btrfs_end_transaction(trans, root); | ||||
| 			if (ret) | ||||
| 				goto out; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (loop == LOOP_NO_EMPTY_SIZE) { | ||||
| @ -6553,8 +6563,7 @@ again: | ||||
| 	up_read(&info->groups_sem); | ||||
| } | ||||
| 
 | ||||
| int btrfs_reserve_extent(struct btrfs_trans_handle *trans, | ||||
| 			 struct btrfs_root *root, | ||||
| int btrfs_reserve_extent(struct btrfs_root *root, | ||||
| 			 u64 num_bytes, u64 min_alloc_size, | ||||
| 			 u64 empty_size, u64 hint_byte, | ||||
| 			 struct btrfs_key *ins, int is_data) | ||||
| @ -6566,8 +6575,8 @@ int btrfs_reserve_extent(struct btrfs_trans_handle *trans, | ||||
| 	flags = btrfs_get_alloc_profile(root, is_data); | ||||
| again: | ||||
| 	WARN_ON(num_bytes < root->sectorsize); | ||||
| 	ret = find_free_extent(trans, root, num_bytes, empty_size, | ||||
| 			       hint_byte, ins, flags); | ||||
| 	ret = find_free_extent(root, num_bytes, empty_size, hint_byte, ins, | ||||
| 			       flags); | ||||
| 
 | ||||
| 	if (ret == -ENOSPC) { | ||||
| 		if (!final_tried) { | ||||
| @ -6955,7 +6964,7 @@ struct extent_buffer *btrfs_alloc_free_block(struct btrfs_trans_handle *trans, | ||||
| 	if (IS_ERR(block_rsv)) | ||||
| 		return ERR_CAST(block_rsv); | ||||
| 
 | ||||
| 	ret = btrfs_reserve_extent(trans, root, blocksize, blocksize, | ||||
| 	ret = btrfs_reserve_extent(root, blocksize, blocksize, | ||||
| 				   empty_size, hint, &ins, 0); | ||||
| 	if (ret) { | ||||
| 		unuse_block_rsv(root->fs_info, block_rsv, blocksize); | ||||
|  | ||||
| @ -1339,7 +1339,6 @@ fail: | ||||
| static noinline int check_can_nocow(struct inode *inode, loff_t pos, | ||||
| 				    size_t *write_bytes) | ||||
| { | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	struct btrfs_ordered_extent *ordered; | ||||
| 	u64 lockstart, lockend; | ||||
| @ -1361,16 +1360,8 @@ static noinline int check_can_nocow(struct inode *inode, loff_t pos, | ||||
| 		btrfs_put_ordered_extent(ordered); | ||||
| 	} | ||||
| 
 | ||||
| 	trans = btrfs_join_transaction(root); | ||||
| 	if (IS_ERR(trans)) { | ||||
| 		unlock_extent(&BTRFS_I(inode)->io_tree, lockstart, lockend); | ||||
| 		return PTR_ERR(trans); | ||||
| 	} | ||||
| 
 | ||||
| 	num_bytes = lockend - lockstart + 1; | ||||
| 	ret = can_nocow_extent(trans, inode, lockstart, &num_bytes, NULL, NULL, | ||||
| 			       NULL); | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 	ret = can_nocow_extent(inode, lockstart, &num_bytes, NULL, NULL, NULL); | ||||
| 	if (ret <= 0) { | ||||
| 		ret = 0; | ||||
| 	} else { | ||||
|  | ||||
| @ -2525,8 +2525,7 @@ setup_cluster_bitmap(struct btrfs_block_group_cache *block_group, | ||||
|  * returns zero and sets up cluster if things worked out, otherwise | ||||
|  * it returns -enospc | ||||
|  */ | ||||
| int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, | ||||
| 			     struct btrfs_root *root, | ||||
| int btrfs_find_space_cluster(struct btrfs_root *root, | ||||
| 			     struct btrfs_block_group_cache *block_group, | ||||
| 			     struct btrfs_free_cluster *cluster, | ||||
| 			     u64 offset, u64 bytes, u64 empty_size) | ||||
|  | ||||
| @ -98,8 +98,7 @@ u64 btrfs_find_space_for_alloc(struct btrfs_block_group_cache *block_group, | ||||
| u64 btrfs_find_ino_for_alloc(struct btrfs_root *fs_root); | ||||
| void btrfs_dump_free_space(struct btrfs_block_group_cache *block_group, | ||||
| 			   u64 bytes); | ||||
| int btrfs_find_space_cluster(struct btrfs_trans_handle *trans, | ||||
| 			     struct btrfs_root *root, | ||||
| int btrfs_find_space_cluster(struct btrfs_root *root, | ||||
| 			     struct btrfs_block_group_cache *block_group, | ||||
| 			     struct btrfs_free_cluster *cluster, | ||||
| 			     u64 offset, u64 bytes, u64 empty_size); | ||||
|  | ||||
							
								
								
									
										217
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							
							
						
						
									
										217
									
								
								fs/btrfs/inode.c
									
									
									
									
									
								
							| @ -230,12 +230,13 @@ fail: | ||||
|  * does the checks required to make sure the data is small enough | ||||
|  * to fit as an inline extent. | ||||
|  */ | ||||
| static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans, | ||||
| 				 struct btrfs_root *root, | ||||
| 				 struct inode *inode, u64 start, u64 end, | ||||
| 				 size_t compressed_size, int compress_type, | ||||
| 				 struct page **compressed_pages) | ||||
| static noinline int cow_file_range_inline(struct btrfs_root *root, | ||||
| 					  struct inode *inode, u64 start, | ||||
| 					  u64 end, size_t compressed_size, | ||||
| 					  int compress_type, | ||||
| 					  struct page **compressed_pages) | ||||
| { | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	u64 isize = i_size_read(inode); | ||||
| 	u64 actual_end = min(end + 1, isize); | ||||
| 	u64 inline_len = actual_end - start; | ||||
| @ -256,9 +257,16 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans, | ||||
| 		return 1; | ||||
| 	} | ||||
| 
 | ||||
| 	trans = btrfs_join_transaction(root); | ||||
| 	if (IS_ERR(trans)) | ||||
| 		return PTR_ERR(trans); | ||||
| 	trans->block_rsv = &root->fs_info->delalloc_block_rsv; | ||||
| 
 | ||||
| 	ret = btrfs_drop_extents(trans, root, inode, start, aligned_end, 1); | ||||
| 	if (ret) | ||||
| 		return ret; | ||||
| 	if (ret) { | ||||
| 		btrfs_abort_transaction(trans, root, ret); | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	if (isize > actual_end) | ||||
| 		inline_len = min_t(u64, isize, actual_end); | ||||
| @ -267,15 +275,18 @@ static noinline int cow_file_range_inline(struct btrfs_trans_handle *trans, | ||||
| 				   compress_type, compressed_pages); | ||||
| 	if (ret && ret != -ENOSPC) { | ||||
| 		btrfs_abort_transaction(trans, root, ret); | ||||
| 		return ret; | ||||
| 		goto out; | ||||
| 	} else if (ret == -ENOSPC) { | ||||
| 		return 1; | ||||
| 		ret = 1; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	set_bit(BTRFS_INODE_NEEDS_FULL_SYNC, &BTRFS_I(inode)->runtime_flags); | ||||
| 	btrfs_delalloc_release_metadata(inode, end + 1 - start); | ||||
| 	btrfs_drop_extent_cache(inode, start, aligned_end - 1, 0); | ||||
| 	return 0; | ||||
| out: | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| struct async_extent { | ||||
| @ -343,7 +354,6 @@ static noinline int compress_file_range(struct inode *inode, | ||||
| 					int *num_added) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	u64 num_bytes; | ||||
| 	u64 blocksize = root->sectorsize; | ||||
| 	u64 actual_end; | ||||
| @ -461,25 +471,16 @@ again: | ||||
| 	} | ||||
| cont: | ||||
| 	if (start == 0) { | ||||
| 		trans = btrfs_join_transaction(root); | ||||
| 		if (IS_ERR(trans)) { | ||||
| 			ret = PTR_ERR(trans); | ||||
| 			trans = NULL; | ||||
| 			goto cleanup_and_out; | ||||
| 		} | ||||
| 		trans->block_rsv = &root->fs_info->delalloc_block_rsv; | ||||
| 
 | ||||
| 		/* lets try to make an inline extent */ | ||||
| 		if (ret || total_in < (actual_end - start)) { | ||||
| 			/* we didn't compress the entire range, try
 | ||||
| 			 * to make an uncompressed inline extent. | ||||
| 			 */ | ||||
| 			ret = cow_file_range_inline(trans, root, inode, | ||||
| 						    start, end, 0, 0, NULL); | ||||
| 			ret = cow_file_range_inline(root, inode, start, end, | ||||
| 						    0, 0, NULL); | ||||
| 		} else { | ||||
| 			/* try making a compressed inline extent */ | ||||
| 			ret = cow_file_range_inline(trans, root, inode, | ||||
| 						    start, end, | ||||
| 			ret = cow_file_range_inline(root, inode, start, end, | ||||
| 						    total_compressed, | ||||
| 						    compress_type, pages); | ||||
| 		} | ||||
| @ -498,10 +499,8 @@ cont: | ||||
| 						     PAGE_CLEAR_DIRTY | | ||||
| 						     PAGE_SET_WRITEBACK | | ||||
| 						     PAGE_END_WRITEBACK); | ||||
| 			btrfs_end_transaction(trans, root); | ||||
| 			goto free_pages_out; | ||||
| 		} | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 	} | ||||
| 
 | ||||
| 	if (will_compress) { | ||||
| @ -592,18 +591,6 @@ free_pages_out: | ||||
| 	kfree(pages); | ||||
| 
 | ||||
| 	goto out; | ||||
| 
 | ||||
| cleanup_and_out: | ||||
| 	extent_clear_unlock_delalloc(inode, start, end, NULL, | ||||
| 				     EXTENT_DELALLOC | EXTENT_DO_ACCOUNTING | | ||||
| 				     EXTENT_DEFRAG, PAGE_UNLOCK | | ||||
| 				     PAGE_CLEAR_DIRTY | PAGE_SET_WRITEBACK | | ||||
| 				     PAGE_END_WRITEBACK); | ||||
| 	if (!trans || IS_ERR(trans)) | ||||
| 		btrfs_error(root->fs_info, ret, "Failed to join transaction"); | ||||
| 	else | ||||
| 		btrfs_abort_transaction(trans, root, ret); | ||||
| 	goto free_pages_out; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
| @ -617,7 +604,6 @@ static noinline int submit_compressed_extents(struct inode *inode, | ||||
| { | ||||
| 	struct async_extent *async_extent; | ||||
| 	u64 alloc_hint = 0; | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct btrfs_key ins; | ||||
| 	struct extent_map *em; | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| @ -678,20 +664,10 @@ retry: | ||||
| 		lock_extent(io_tree, async_extent->start, | ||||
| 			    async_extent->start + async_extent->ram_size - 1); | ||||
| 
 | ||||
| 		trans = btrfs_join_transaction(root); | ||||
| 		if (IS_ERR(trans)) { | ||||
| 			ret = PTR_ERR(trans); | ||||
| 		} else { | ||||
| 			trans->block_rsv = &root->fs_info->delalloc_block_rsv; | ||||
| 			ret = btrfs_reserve_extent(trans, root, | ||||
| 		ret = btrfs_reserve_extent(root, | ||||
| 					   async_extent->compressed_size, | ||||
| 					   async_extent->compressed_size, | ||||
| 					   0, alloc_hint, &ins, 1); | ||||
| 			if (ret && ret != -ENOSPC) | ||||
| 				btrfs_abort_transaction(trans, root, ret); | ||||
| 			btrfs_end_transaction(trans, root); | ||||
| 		} | ||||
| 
 | ||||
| 		if (ret) { | ||||
| 			int i; | ||||
| 
 | ||||
| @ -850,14 +826,13 @@ static u64 get_extent_allocation_hint(struct inode *inode, u64 start, | ||||
|  * required to start IO on it.  It may be clean and already done with | ||||
|  * IO when we return. | ||||
|  */ | ||||
| static noinline int __cow_file_range(struct btrfs_trans_handle *trans, | ||||
| 				     struct inode *inode, | ||||
| 				     struct btrfs_root *root, | ||||
| 				     struct page *locked_page, | ||||
| 				     u64 start, u64 end, int *page_started, | ||||
| 				     unsigned long *nr_written, | ||||
| 				     int unlock) | ||||
| static noinline int cow_file_range(struct inode *inode, | ||||
| 				   struct page *locked_page, | ||||
| 				   u64 start, u64 end, int *page_started, | ||||
| 				   unsigned long *nr_written, | ||||
| 				   int unlock) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	u64 alloc_hint = 0; | ||||
| 	u64 num_bytes; | ||||
| 	unsigned long ram_size; | ||||
| @ -878,12 +853,12 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans, | ||||
| 	/* if this is a small write inside eof, kick off defrag */ | ||||
| 	if (num_bytes < 64 * 1024 && | ||||
| 	    (start > 0 || end + 1 < BTRFS_I(inode)->disk_i_size)) | ||||
| 		btrfs_add_inode_defrag(trans, inode); | ||||
| 		btrfs_add_inode_defrag(NULL, inode); | ||||
| 
 | ||||
| 	if (start == 0) { | ||||
| 		/* lets try to make an inline extent */ | ||||
| 		ret = cow_file_range_inline(trans, root, inode, | ||||
| 					    start, end, 0, 0, NULL); | ||||
| 		ret = cow_file_range_inline(root, inode, start, end, 0, 0, | ||||
| 					    NULL); | ||||
| 		if (ret == 0) { | ||||
| 			extent_clear_unlock_delalloc(inode, start, end, NULL, | ||||
| 				     EXTENT_LOCKED | EXTENT_DELALLOC | | ||||
| @ -896,7 +871,6 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans, | ||||
| 			*page_started = 1; | ||||
| 			goto out; | ||||
| 		} else if (ret < 0) { | ||||
| 			btrfs_abort_transaction(trans, root, ret); | ||||
| 			goto out_unlock; | ||||
| 		} | ||||
| 	} | ||||
| @ -911,13 +885,11 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans, | ||||
| 		unsigned long op; | ||||
| 
 | ||||
| 		cur_alloc_size = disk_num_bytes; | ||||
| 		ret = btrfs_reserve_extent(trans, root, cur_alloc_size, | ||||
| 		ret = btrfs_reserve_extent(root, cur_alloc_size, | ||||
| 					   root->sectorsize, 0, alloc_hint, | ||||
| 					   &ins, 1); | ||||
| 		if (ret < 0) { | ||||
| 			btrfs_abort_transaction(trans, root, ret); | ||||
| 		if (ret < 0) | ||||
| 			goto out_unlock; | ||||
| 		} | ||||
| 
 | ||||
| 		em = alloc_extent_map(); | ||||
| 		if (!em) { | ||||
| @ -963,10 +935,8 @@ static noinline int __cow_file_range(struct btrfs_trans_handle *trans, | ||||
| 		    BTRFS_DATA_RELOC_TREE_OBJECTID) { | ||||
| 			ret = btrfs_reloc_clone_csums(inode, start, | ||||
| 						      cur_alloc_size); | ||||
| 			if (ret) { | ||||
| 				btrfs_abort_transaction(trans, root, ret); | ||||
| 			if (ret) | ||||
| 				goto out_reserve; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		if (disk_num_bytes < cur_alloc_size) | ||||
| @ -1005,37 +975,6 @@ out_unlock: | ||||
| 	goto out; | ||||
| } | ||||
| 
 | ||||
| static noinline int cow_file_range(struct inode *inode, | ||||
| 				   struct page *locked_page, | ||||
| 				   u64 start, u64 end, int *page_started, | ||||
| 				   unsigned long *nr_written, | ||||
| 				   int unlock) | ||||
| { | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	trans = btrfs_join_transaction(root); | ||||
| 	if (IS_ERR(trans)) { | ||||
| 		extent_clear_unlock_delalloc(inode, start, end, locked_page, | ||||
| 					     EXTENT_LOCKED | EXTENT_DELALLOC | | ||||
| 					     EXTENT_DO_ACCOUNTING | | ||||
| 					     EXTENT_DEFRAG, PAGE_UNLOCK | | ||||
| 					     PAGE_CLEAR_DIRTY | | ||||
| 					     PAGE_SET_WRITEBACK | | ||||
| 					     PAGE_END_WRITEBACK); | ||||
| 		return PTR_ERR(trans); | ||||
| 	} | ||||
| 	trans->block_rsv = &root->fs_info->delalloc_block_rsv; | ||||
| 
 | ||||
| 	ret = __cow_file_range(trans, inode, root, locked_page, start, end, | ||||
| 			       page_started, nr_written, unlock); | ||||
| 
 | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * work queue call back to started compression on a file and pages | ||||
|  */ | ||||
| @ -1347,9 +1286,9 @@ out_check: | ||||
| 
 | ||||
| 		btrfs_release_path(path); | ||||
| 		if (cow_start != (u64)-1) { | ||||
| 			ret = __cow_file_range(trans, inode, root, locked_page, | ||||
| 					       cow_start, found_key.offset - 1, | ||||
| 					       page_started, nr_written, 1); | ||||
| 			ret = cow_file_range(inode, locked_page, | ||||
| 					     cow_start, found_key.offset - 1, | ||||
| 					     page_started, nr_written, 1); | ||||
| 			if (ret) { | ||||
| 				btrfs_abort_transaction(trans, root, ret); | ||||
| 				goto error; | ||||
| @ -1423,9 +1362,8 @@ out_check: | ||||
| 	} | ||||
| 
 | ||||
| 	if (cow_start != (u64)-1) { | ||||
| 		ret = __cow_file_range(trans, inode, root, locked_page, | ||||
| 				       cow_start, end, | ||||
| 				       page_started, nr_written, 1); | ||||
| 		ret = cow_file_range(inode, locked_page, cow_start, end, | ||||
| 				     page_started, nr_written, 1); | ||||
| 		if (ret) { | ||||
| 			btrfs_abort_transaction(trans, root, ret); | ||||
| 			goto error; | ||||
| @ -6344,39 +6282,32 @@ static struct extent_map *btrfs_new_extent_direct(struct inode *inode, | ||||
| 						  u64 start, u64 len) | ||||
| { | ||||
| 	struct btrfs_root *root = BTRFS_I(inode)->root; | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct extent_map *em; | ||||
| 	struct btrfs_key ins; | ||||
| 	u64 alloc_hint; | ||||
| 	int ret; | ||||
| 
 | ||||
| 	trans = btrfs_join_transaction(root); | ||||
| 	if (IS_ERR(trans)) | ||||
| 		return ERR_CAST(trans); | ||||
| 
 | ||||
| 	trans->block_rsv = &root->fs_info->delalloc_block_rsv; | ||||
| 
 | ||||
| 	alloc_hint = get_extent_allocation_hint(inode, start, len); | ||||
| 	ret = btrfs_reserve_extent(trans, root, len, root->sectorsize, 0, | ||||
| 	ret = btrfs_reserve_extent(root, len, root->sectorsize, 0, | ||||
| 				   alloc_hint, &ins, 1); | ||||
| 	if (ret) { | ||||
| 		em = ERR_PTR(ret); | ||||
| 		goto out; | ||||
| 	} | ||||
| 	if (ret) | ||||
| 		return ERR_PTR(ret); | ||||
| 
 | ||||
| 	em = create_pinned_em(inode, start, ins.offset, start, ins.objectid, | ||||
| 			      ins.offset, ins.offset, ins.offset, 0); | ||||
| 	if (IS_ERR(em)) | ||||
| 		goto out; | ||||
| 	if (IS_ERR(em)) { | ||||
| 		btrfs_free_reserved_extent(root, ins.objectid, ins.offset); | ||||
| 		return em; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = btrfs_add_ordered_extent_dio(inode, start, ins.objectid, | ||||
| 					   ins.offset, ins.offset, 0); | ||||
| 	if (ret) { | ||||
| 		btrfs_free_reserved_extent(root, ins.objectid, ins.offset); | ||||
| 		em = ERR_PTR(ret); | ||||
| 		free_extent_map(em); | ||||
| 		return ERR_PTR(ret); | ||||
| 	} | ||||
| out: | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 
 | ||||
| 	return em; | ||||
| } | ||||
| 
 | ||||
| @ -6384,11 +6315,11 @@ out: | ||||
|  * returns 1 when the nocow is safe, < 1 on error, 0 if the | ||||
|  * block must be cow'd | ||||
|  */ | ||||
| noinline int can_nocow_extent(struct btrfs_trans_handle *trans, | ||||
| 			      struct inode *inode, u64 offset, u64 *len, | ||||
| noinline int can_nocow_extent(struct inode *inode, u64 offset, u64 *len, | ||||
| 			      u64 *orig_start, u64 *orig_block_len, | ||||
| 			      u64 *ram_bytes) | ||||
| { | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	struct btrfs_path *path; | ||||
| 	int ret; | ||||
| 	struct extent_buffer *leaf; | ||||
| @ -6406,7 +6337,7 @@ noinline int can_nocow_extent(struct btrfs_trans_handle *trans, | ||||
| 	if (!path) | ||||
| 		return -ENOMEM; | ||||
| 
 | ||||
| 	ret = btrfs_lookup_file_extent(trans, root, path, btrfs_ino(inode), | ||||
| 	ret = btrfs_lookup_file_extent(NULL, root, path, btrfs_ino(inode), | ||||
| 				       offset, 0); | ||||
| 	if (ret < 0) | ||||
| 		goto out; | ||||
| @ -6471,9 +6402,19 @@ noinline int can_nocow_extent(struct btrfs_trans_handle *trans, | ||||
| 	 * look for other files referencing this extent, if we | ||||
| 	 * find any we must cow | ||||
| 	 */ | ||||
| 	if (btrfs_cross_ref_exist(trans, root, btrfs_ino(inode), | ||||
| 				  key.offset - backref_offset, disk_bytenr)) | ||||
| 	trans = btrfs_join_transaction(root); | ||||
| 	if (IS_ERR(trans)) { | ||||
| 		ret = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = btrfs_cross_ref_exist(trans, root, btrfs_ino(inode), | ||||
| 				    key.offset - backref_offset, disk_bytenr); | ||||
| 	btrfs_end_transaction(trans, root); | ||||
| 	if (ret) { | ||||
| 		ret = 0; | ||||
| 		goto out; | ||||
| 	} | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * adjust disk_bytenr and num_bytes to cover just the bytes | ||||
| @ -6615,7 +6556,6 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, | ||||
| 	u64 start = iblock << inode->i_blkbits; | ||||
| 	u64 lockstart, lockend; | ||||
| 	u64 len = bh_result->b_size; | ||||
| 	struct btrfs_trans_handle *trans; | ||||
| 	int unlock_bits = EXTENT_LOCKED; | ||||
| 	int ret = 0; | ||||
| 
 | ||||
| @ -6697,16 +6637,7 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, | ||||
| 		len = min(len, em->len - (start - em->start)); | ||||
| 		block_start = em->block_start + (start - em->start); | ||||
| 
 | ||||
| 		/*
 | ||||
| 		 * we're not going to log anything, but we do need | ||||
| 		 * to make sure the current transaction stays open | ||||
| 		 * while we look for nocow cross refs | ||||
| 		 */ | ||||
| 		trans = btrfs_join_transaction(root); | ||||
| 		if (IS_ERR(trans)) | ||||
| 			goto must_cow; | ||||
| 
 | ||||
| 		if (can_nocow_extent(trans, inode, start, &len, &orig_start, | ||||
| 		if (can_nocow_extent(inode, start, &len, &orig_start, | ||||
| 				     &orig_block_len, &ram_bytes) == 1) { | ||||
| 			if (type == BTRFS_ORDERED_PREALLOC) { | ||||
| 				free_extent_map(em); | ||||
| @ -6715,24 +6646,20 @@ static int btrfs_get_blocks_direct(struct inode *inode, sector_t iblock, | ||||
| 						       block_start, len, | ||||
| 						       orig_block_len, | ||||
| 						       ram_bytes, type); | ||||
| 				if (IS_ERR(em)) { | ||||
| 					btrfs_end_transaction(trans, root); | ||||
| 				if (IS_ERR(em)) | ||||
| 					goto unlock_err; | ||||
| 				} | ||||
| 			} | ||||
| 
 | ||||
| 			ret = btrfs_add_ordered_extent_dio(inode, start, | ||||
| 					   block_start, len, len, type); | ||||
| 			btrfs_end_transaction(trans, root); | ||||
| 			if (ret) { | ||||
| 				free_extent_map(em); | ||||
| 				goto unlock_err; | ||||
| 			} | ||||
| 			goto unlock; | ||||
| 		} | ||||
| 		btrfs_end_transaction(trans, root); | ||||
| 	} | ||||
| must_cow: | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * this will cow the extent, reset the len in case we changed | ||||
| 	 * it above | ||||
| @ -8495,8 +8422,8 @@ static int __btrfs_prealloc_file_range(struct inode *inode, int mode, | ||||
| 
 | ||||
| 		cur_bytes = min(num_bytes, 256ULL * 1024 * 1024); | ||||
| 		cur_bytes = max(cur_bytes, min_size); | ||||
| 		ret = btrfs_reserve_extent(trans, root, cur_bytes, | ||||
| 					   min_size, 0, *alloc_hint, &ins, 1); | ||||
| 		ret = btrfs_reserve_extent(root, cur_bytes, min_size, 0, | ||||
| 					   *alloc_hint, &ins, 1); | ||||
| 		if (ret) { | ||||
| 			if (own_trans) | ||||
| 				btrfs_end_transaction(trans, root); | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user