diff options
Diffstat (limited to 'fs/btrfs/file.c')
| -rw-r--r-- | fs/btrfs/file.c | 106 | 
1 files changed, 41 insertions, 65 deletions
| diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c index 14e27473c5bc..36f51c311bb1 100644 --- a/fs/btrfs/file.c +++ b/fs/btrfs/file.c @@ -36,52 +36,7 @@  #include "ioctl.h"  #include "file.h"  #include "super.h" - -/* - * Helper to fault in page and copy.  This should go away and be replaced with - * calls into generic code. - */ -static noinline int btrfs_copy_from_user(loff_t pos, size_t write_bytes, -					 struct folio *folio, struct iov_iter *i) -{ -	size_t copied = 0; -	size_t total_copied = 0; -	int offset = offset_in_page(pos); - -	while (write_bytes > 0) { -		size_t count = min_t(size_t, PAGE_SIZE - offset, write_bytes); -		/* -		 * Copy data from userspace to the current page -		 */ -		copied = copy_folio_from_iter_atomic(folio, offset, count, i); - -		/* Flush processor's dcache for this page */ -		flush_dcache_folio(folio); - -		/* -		 * if we get a partial write, we can end up with -		 * partially up to date page.  These add -		 * a lot of complexity, so make sure they don't -		 * happen by forcing this copy to be retried. -		 * -		 * The rest of the btrfs_file_write code will fall -		 * back to page at a time copies after we return 0. -		 */ -		if (unlikely(copied < count)) { -			if (!folio_test_uptodate(folio)) { -				iov_iter_revert(i, copied); -				copied = 0; -			} -			if (!copied) -				break; -		} - -		write_bytes -= copied; -		total_copied += copied; -		offset += copied; -	} -	return total_copied; -} +#include "print-tree.h"  /*   * Unlock folio after btrfs_file_write() is done with it. @@ -106,7 +61,7 @@ static void btrfs_drop_folio(struct btrfs_fs_info *fs_info, struct folio *folio,  }  /* - * After btrfs_copy_from_user(), update the following things for delalloc: + * After copy_folio_from_iter_atomic(), update the following things for delalloc:   * - Mark newly dirtied folio as DELALLOC in the io tree.   *   Used to advise which range is to be written back.   * - Mark modified folio as Uptodate/Dirty and not needing COW fixup @@ -224,7 +179,7 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,  	if (args->drop_cache)  		btrfs_drop_extent_map_range(inode, args->start, args->end - 1, false); -	if (args->start >= inode->disk_i_size && !args->replace_extent) +	if (data_race(args->start >= inode->disk_i_size) && !args->replace_extent)  		modify_tree = 0;  	update_refs = (btrfs_root_id(root) != BTRFS_TREE_LOG_OBJECTID); @@ -245,7 +200,11 @@ int btrfs_drop_extents(struct btrfs_trans_handle *trans,  next_slot:  		leaf = path->nodes[0];  		if (path->slots[0] >= btrfs_header_nritems(leaf)) { -			BUG_ON(del_nr > 0); +			if (WARN_ON(del_nr > 0)) { +				btrfs_print_leaf(leaf); +				ret = -EINVAL; +				break; +			}  			ret = btrfs_next_leaf(root, path);  			if (ret < 0)  				break; @@ -321,7 +280,11 @@ next_slot:  		 *  | -------- extent -------- |  		 */  		if (args->start > key.offset && args->end < extent_end) { -			BUG_ON(del_nr > 0); +			if (WARN_ON(del_nr > 0)) { +				btrfs_print_leaf(leaf); +				ret = -EINVAL; +				break; +			}  			if (extent_type == BTRFS_FILE_EXTENT_INLINE) {  				ret = -EOPNOTSUPP;  				break; @@ -351,7 +314,6 @@ next_slot:  			btrfs_set_file_extent_offset(leaf, fi, extent_offset);  			btrfs_set_file_extent_num_bytes(leaf, fi,  							extent_end - args->start); -			btrfs_mark_buffer_dirty(trans, leaf);  			if (update_refs && disk_bytenr > 0) {  				struct btrfs_ref ref = { @@ -397,7 +359,6 @@ next_slot:  			btrfs_set_file_extent_offset(leaf, fi, extent_offset);  			btrfs_set_file_extent_num_bytes(leaf, fi,  							extent_end - args->end); -			btrfs_mark_buffer_dirty(trans, leaf);  			if (update_refs && disk_bytenr > 0)  				args->bytes_found += args->end - key.offset;  			break; @@ -409,7 +370,11 @@ next_slot:  		 *  | -------- extent -------- |  		 */  		if (args->start > key.offset && args->end >= extent_end) { -			BUG_ON(del_nr > 0); +			if (WARN_ON(del_nr > 0)) { +				btrfs_print_leaf(leaf); +				ret = -EINVAL; +				break; +			}  			if (extent_type == BTRFS_FILE_EXTENT_INLINE) {  				ret = -EOPNOTSUPP;  				break; @@ -417,7 +382,6 @@ next_slot:  			btrfs_set_file_extent_num_bytes(leaf, fi,  							args->start - key.offset); -			btrfs_mark_buffer_dirty(trans, leaf);  			if (update_refs && disk_bytenr > 0)  				args->bytes_found += extent_end - args->start;  			if (args->end == extent_end) @@ -437,7 +401,11 @@ delete_extent_item:  				del_slot = path->slots[0];  				del_nr = 1;  			} else { -				BUG_ON(del_slot + del_nr != path->slots[0]); +				if (WARN_ON(del_slot + del_nr != path->slots[0])) { +					btrfs_print_leaf(leaf); +					ret = -EINVAL; +					break; +				}  				del_nr++;  			} @@ -668,7 +636,6 @@ again:  							 trans->transid);  			btrfs_set_file_extent_num_bytes(leaf, fi,  							end - other_start); -			btrfs_mark_buffer_dirty(trans, leaf);  			goto out;  		}  	} @@ -697,7 +664,6 @@ again:  							other_end - start);  			btrfs_set_file_extent_offset(leaf, fi,  						     start - orig_offset); -			btrfs_mark_buffer_dirty(trans, leaf);  			goto out;  		}  	} @@ -731,7 +697,6 @@ again:  		btrfs_set_file_extent_offset(leaf, fi, split - orig_offset);  		btrfs_set_file_extent_num_bytes(leaf, fi,  						extent_end - split); -		btrfs_mark_buffer_dirty(trans, leaf);  		ref.action = BTRFS_ADD_DELAYED_REF;  		ref.bytenr = bytenr; @@ -810,7 +775,6 @@ again:  		btrfs_set_file_extent_type(leaf, fi,  					   BTRFS_FILE_EXTENT_REG);  		btrfs_set_file_extent_generation(leaf, fi, trans->transid); -		btrfs_mark_buffer_dirty(trans, leaf);  	} else {  		fi = btrfs_item_ptr(leaf, del_slot - 1,  			   struct btrfs_file_extent_item); @@ -819,7 +783,6 @@ again:  		btrfs_set_file_extent_generation(leaf, fi, trans->transid);  		btrfs_set_file_extent_num_bytes(leaf, fi,  						extent_end - key.offset); -		btrfs_mark_buffer_dirty(trans, leaf);  		ret = btrfs_del_items(trans, root, path, del_slot, del_nr);  		if (ret < 0) { @@ -1052,7 +1015,7 @@ int btrfs_check_nocow_lock(struct btrfs_inode *inode, loff_t pos,  						   &cached_state);  	}  	ret = can_nocow_extent(&inode->vfs_inode, lockstart, &num_bytes, -			       NULL, nowait, false); +			       NULL, nowait);  	if (ret <= 0)  		btrfs_drew_write_unlock(&root->snapshot_lock);  	else @@ -1252,7 +1215,23 @@ again:  			break;  		} -		copied = btrfs_copy_from_user(pos, write_bytes, folio, i); +		copied = copy_folio_from_iter_atomic(folio, +				offset_in_folio(folio, pos), write_bytes, i); +		flush_dcache_folio(folio); + +		/* +		 * If we get a partial write, we can end up with partially +		 * uptodate page. Although if sector size < page size we can +		 * handle it, but if it's not sector aligned it can cause +		 * a lot of complexity, so make sure they don't happen by +		 * forcing retry this copy. +		 */ +		if (unlikely(copied < write_bytes)) { +			if (!folio_test_uptodate(folio)) { +				iov_iter_revert(i, copied); +				copied = 0; +			} +		}  		num_sectors = BTRFS_BYTES_TO_BLKS(fs_info, reserve_bytes);  		dirty_sectors = round_up(copied + sector_offset, @@ -2029,7 +2008,6 @@ static int fill_holes(struct btrfs_trans_handle *trans,  		btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);  		btrfs_set_file_extent_offset(leaf, fi, 0);  		btrfs_set_file_extent_generation(leaf, fi, trans->transid); -		btrfs_mark_buffer_dirty(trans, leaf);  		goto out;  	} @@ -2046,7 +2024,6 @@ static int fill_holes(struct btrfs_trans_handle *trans,  		btrfs_set_file_extent_ram_bytes(leaf, fi, num_bytes);  		btrfs_set_file_extent_offset(leaf, fi, 0);  		btrfs_set_file_extent_generation(leaf, fi, trans->transid); -		btrfs_mark_buffer_dirty(trans, leaf);  		goto out;  	}  	btrfs_release_path(path); @@ -2194,7 +2171,6 @@ static int btrfs_insert_replace_extent(struct btrfs_trans_handle *trans,  	btrfs_set_file_extent_num_bytes(leaf, extent, replace_len);  	if (extent_info->is_new_extent)  		btrfs_set_file_extent_generation(leaf, extent, trans->transid); -	btrfs_mark_buffer_dirty(trans, leaf);  	btrfs_release_path(path);  	ret = btrfs_inode_set_file_extent_range(inode, extent_info->file_offset, | 
