diff mbox series

btrfs: Improve error reporting in lookup_inline_extent_backref

Message ID 20220420115401.186147-1-nborisov@suse.com (mailing list archive)
State New, archived
Headers show
Series btrfs: Improve error reporting in lookup_inline_extent_backref | expand

Commit Message

Nikolay Borisov April 20, 2022, 11:54 a.m. UTC
When iterating the backrefs in an extent item if the ptr to the
'current' backref record goes beyond the extent item a warning is
generated and -ENOENT is returned. However what's more appropriate to
debug such cases would be to return EUCLEAN and also print the in-memory
state of the offending leaf.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/extent-tree.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

Comments

David Sterba April 20, 2022, 2:52 p.m. UTC | #1
On Wed, Apr 20, 2022 at 02:54:00PM +0300, Nikolay Borisov wrote:
> When iterating the backrefs in an extent item if the ptr to the
> 'current' backref record goes beyond the extent item a warning is
> generated and -ENOENT is returned. However what's more appropriate to
> debug such cases would be to return EUCLEAN and also print the in-memory
> state of the offending leaf.

Agreed, EUCLEAN makese sense. Added to misc-next, thanks.
David Sterba April 20, 2022, 3:54 p.m. UTC | #2
On Wed, Apr 20, 2022 at 02:54:00PM +0300, Nikolay Borisov wrote:
> When iterating the backrefs in an extent item if the ptr to the
> 'current' backref record goes beyond the extent item a warning is
> generated and -ENOENT is returned. However what's more appropriate to
> debug such cases would be to return EUCLEAN and also print the in-memory
> state of the offending leaf.
> 
> Signed-off-by: Nikolay Borisov <nborisov@suse.com>
> ---
>  fs/btrfs/extent-tree.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
> index 963160a0c393..5a53bcfdca83 100644
> --- a/fs/btrfs/extent-tree.c
> +++ b/fs/btrfs/extent-tree.c
> @@ -895,7 +895,10 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
>  	err = -ENOENT;
>  	while (1) {
>  		if (ptr >= end) {
> -			WARN_ON(ptr > end);
> +			if (WARN_ON(ptr > end)) {
> +				err = -EUCLEAN;
> +				btrfs_print_leaf(path->slots[0]);

This gives a warning, the slots array does not contain the extent buffer
pointer so this should have been path->nodes[0] but this is already in
'leaf', fixed.
Nikolay Borisov April 20, 2022, 4:01 p.m. UTC | #3
On 20.04.22 г. 18:54 ч., David Sterba wrote:
> On Wed, Apr 20, 2022 at 02:54:00PM +0300, Nikolay Borisov wrote:
>> When iterating the backrefs in an extent item if the ptr to the
>> 'current' backref record goes beyond the extent item a warning is
>> generated and -ENOENT is returned. However what's more appropriate to
>> debug such cases would be to return EUCLEAN and also print the in-memory
>> state of the offending leaf.
>>
>> Signed-off-by: Nikolay Borisov <nborisov@suse.com>
>> ---
>>   fs/btrfs/extent-tree.c | 5 ++++-
>>   1 file changed, 4 insertions(+), 1 deletion(-)
>>
>> diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
>> index 963160a0c393..5a53bcfdca83 100644
>> --- a/fs/btrfs/extent-tree.c
>> +++ b/fs/btrfs/extent-tree.c
>> @@ -895,7 +895,10 @@ int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
>>   	err = -ENOENT;
>>   	while (1) {
>>   		if (ptr >= end) {
>> -			WARN_ON(ptr > end);
>> +			if (WARN_ON(ptr > end)) {
>> +				err = -EUCLEAN;
>> +				btrfs_print_leaf(path->slots[0]);
> 
> This gives a warning, the slots array does not contain the extent buffer
> pointer so this should have been path->nodes[0] but this is already in
> 'leaf', fixed.
> 

Ah you are right... should have been ->nodes[0] not slot...
kernel test robot April 21, 2022, 6:40 a.m. UTC | #4
Hi Nikolay,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.18-rc3 next-20220420]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: hexagon-randconfig-r041-20220420 (https://download.01.org/0day-ci/archive/20220420/202204202351.jCXN37xH-lkp@intel.com/config)
compiler: clang version 15.0.0 (https://github.com/llvm/llvm-project bac6cd5bf85669e3376610cfc4c4f9ca015e7b9b)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/a77df79d2f67b166cb64a21808ec432edcbf5bba
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
        git checkout a77df79d2f67b166cb64a21808ec432edcbf5bba
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=hexagon SHELL=/bin/bash fs/btrfs/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> fs/btrfs/extent-tree.c:900:22: warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'struct extent_buffer *' [-Wint-conversion]
                                   btrfs_print_leaf(path->slots[0]);
                                                    ^~~~~~~~~~~~~~
   fs/btrfs/print-tree.h:12:45: note: passing argument to parameter 'l' here
   void btrfs_print_leaf(struct extent_buffer *l);
                                               ^
   1 warning generated.


vim +900 fs/btrfs/extent-tree.c

   770	
   771	/*
   772	 * look for inline back ref. if back ref is found, *ref_ret is set
   773	 * to the address of inline back ref, and 0 is returned.
   774	 *
   775	 * if back ref isn't found, *ref_ret is set to the address where it
   776	 * should be inserted, and -ENOENT is returned.
   777	 *
   778	 * if insert is true and there are too many inline back refs, the path
   779	 * points to the extent item, and -EAGAIN is returned.
   780	 *
   781	 * NOTE: inline back refs are ordered in the same way that back ref
   782	 *	 items in the tree are ordered.
   783	 */
   784	static noinline_for_stack
   785	int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
   786					 struct btrfs_path *path,
   787					 struct btrfs_extent_inline_ref **ref_ret,
   788					 u64 bytenr, u64 num_bytes,
   789					 u64 parent, u64 root_objectid,
   790					 u64 owner, u64 offset, int insert)
   791	{
   792		struct btrfs_fs_info *fs_info = trans->fs_info;
   793		struct btrfs_root *root = btrfs_extent_root(fs_info, bytenr);
   794		struct btrfs_key key;
   795		struct extent_buffer *leaf;
   796		struct btrfs_extent_item *ei;
   797		struct btrfs_extent_inline_ref *iref;
   798		u64 flags;
   799		u64 item_size;
   800		unsigned long ptr;
   801		unsigned long end;
   802		int extra_size;
   803		int type;
   804		int want;
   805		int ret;
   806		int err = 0;
   807		bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
   808		int needed;
   809	
   810		key.objectid = bytenr;
   811		key.type = BTRFS_EXTENT_ITEM_KEY;
   812		key.offset = num_bytes;
   813	
   814		want = extent_ref_type(parent, owner);
   815		if (insert) {
   816			extra_size = btrfs_extent_inline_ref_size(want);
   817			path->search_for_extension = 1;
   818			path->keep_locks = 1;
   819		} else
   820			extra_size = -1;
   821	
   822		/*
   823		 * Owner is our level, so we can just add one to get the level for the
   824		 * block we are interested in.
   825		 */
   826		if (skinny_metadata && owner < BTRFS_FIRST_FREE_OBJECTID) {
   827			key.type = BTRFS_METADATA_ITEM_KEY;
   828			key.offset = owner;
   829		}
   830	
   831	again:
   832		ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
   833		if (ret < 0) {
   834			err = ret;
   835			goto out;
   836		}
   837	
   838		/*
   839		 * We may be a newly converted file system which still has the old fat
   840		 * extent entries for metadata, so try and see if we have one of those.
   841		 */
   842		if (ret > 0 && skinny_metadata) {
   843			skinny_metadata = false;
   844			if (path->slots[0]) {
   845				path->slots[0]--;
   846				btrfs_item_key_to_cpu(path->nodes[0], &key,
   847						      path->slots[0]);
   848				if (key.objectid == bytenr &&
   849				    key.type == BTRFS_EXTENT_ITEM_KEY &&
   850				    key.offset == num_bytes)
   851					ret = 0;
   852			}
   853			if (ret) {
   854				key.objectid = bytenr;
   855				key.type = BTRFS_EXTENT_ITEM_KEY;
   856				key.offset = num_bytes;
   857				btrfs_release_path(path);
   858				goto again;
   859			}
   860		}
   861	
   862		if (ret && !insert) {
   863			err = -ENOENT;
   864			goto out;
   865		} else if (WARN_ON(ret)) {
   866			err = -EIO;
   867			goto out;
   868		}
   869	
   870		leaf = path->nodes[0];
   871		item_size = btrfs_item_size(leaf, path->slots[0]);
   872		if (unlikely(item_size < sizeof(*ei))) {
   873			err = -EINVAL;
   874			btrfs_print_v0_err(fs_info);
   875			btrfs_abort_transaction(trans, err);
   876			goto out;
   877		}
   878	
   879		ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
   880		flags = btrfs_extent_flags(leaf, ei);
   881	
   882		ptr = (unsigned long)(ei + 1);
   883		end = (unsigned long)ei + item_size;
   884	
   885		if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
   886			ptr += sizeof(struct btrfs_tree_block_info);
   887			BUG_ON(ptr > end);
   888		}
   889	
   890		if (owner >= BTRFS_FIRST_FREE_OBJECTID)
   891			needed = BTRFS_REF_TYPE_DATA;
   892		else
   893			needed = BTRFS_REF_TYPE_BLOCK;
   894	
   895		err = -ENOENT;
   896		while (1) {
   897			if (ptr >= end) {
   898				if (WARN_ON(ptr > end)) {
   899					err = -EUCLEAN;
 > 900					btrfs_print_leaf(path->slots[0]);
   901				}
   902				break;
   903			}
   904			iref = (struct btrfs_extent_inline_ref *)ptr;
   905			type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
   906			if (type == BTRFS_REF_TYPE_INVALID) {
   907				err = -EUCLEAN;
   908				goto out;
   909			}
   910	
   911			if (want < type)
   912				break;
   913			if (want > type) {
   914				ptr += btrfs_extent_inline_ref_size(type);
   915				continue;
   916			}
   917	
   918			if (type == BTRFS_EXTENT_DATA_REF_KEY) {
   919				struct btrfs_extent_data_ref *dref;
   920				dref = (struct btrfs_extent_data_ref *)(&iref->offset);
   921				if (match_extent_data_ref(leaf, dref, root_objectid,
   922							  owner, offset)) {
   923					err = 0;
   924					break;
   925				}
   926				if (hash_extent_data_ref_item(leaf, dref) <
   927				    hash_extent_data_ref(root_objectid, owner, offset))
   928					break;
   929			} else {
   930				u64 ref_offset;
   931				ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
   932				if (parent > 0) {
   933					if (parent == ref_offset) {
   934						err = 0;
   935						break;
   936					}
   937					if (ref_offset < parent)
   938						break;
   939				} else {
   940					if (root_objectid == ref_offset) {
   941						err = 0;
   942						break;
   943					}
   944					if (ref_offset < root_objectid)
   945						break;
   946				}
   947			}
   948			ptr += btrfs_extent_inline_ref_size(type);
   949		}
   950		if (err == -ENOENT && insert) {
   951			if (item_size + extra_size >=
   952			    BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
   953				err = -EAGAIN;
   954				goto out;
   955			}
   956			/*
   957			 * To add new inline back ref, we have to make sure
   958			 * there is no corresponding back ref item.
   959			 * For simplicity, we just do not add new inline back
   960			 * ref if there is any kind of item for this block
   961			 */
   962			if (find_next_key(path, 0, &key) == 0 &&
   963			    key.objectid == bytenr &&
   964			    key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
   965				err = -EAGAIN;
   966				goto out;
   967			}
   968		}
   969		*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
   970	out:
   971		if (insert) {
   972			path->keep_locks = 0;
   973			path->search_for_extension = 0;
   974			btrfs_unlock_up_safe(path, 1);
   975		}
   976		return err;
   977	}
   978
kernel test robot April 21, 2022, 6:41 a.m. UTC | #5
Hi Nikolay,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.18-rc3 next-20220420]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: alpha-randconfig-r014-20220420 (https://download.01.org/0day-ci/archive/20220421/202204210142.BF3uWUQj-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/a77df79d2f67b166cb64a21808ec432edcbf5bba
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
        git checkout a77df79d2f67b166cb64a21808ec432edcbf5bba
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross W=1 O=build_dir ARCH=alpha SHELL=/bin/bash fs/btrfs/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   fs/btrfs/extent-tree.c: In function 'lookup_inline_extent_backref':
>> fs/btrfs/extent-tree.c:900:61: warning: passing argument 1 of 'btrfs_print_leaf' makes pointer from integer without a cast [-Wint-conversion]
     900 |                                 btrfs_print_leaf(path->slots[0]);
         |                                                  ~~~~~~~~~~~^~~
         |                                                             |
         |                                                             int
   In file included from fs/btrfs/extent-tree.c:22:
   fs/btrfs/print-tree.h:12:45: note: expected 'struct extent_buffer *' but argument is of type 'int'
      12 | void btrfs_print_leaf(struct extent_buffer *l);
         |                       ~~~~~~~~~~~~~~~~~~~~~~^


vim +/btrfs_print_leaf +900 fs/btrfs/extent-tree.c

   770	
   771	/*
   772	 * look for inline back ref. if back ref is found, *ref_ret is set
   773	 * to the address of inline back ref, and 0 is returned.
   774	 *
   775	 * if back ref isn't found, *ref_ret is set to the address where it
   776	 * should be inserted, and -ENOENT is returned.
   777	 *
   778	 * if insert is true and there are too many inline back refs, the path
   779	 * points to the extent item, and -EAGAIN is returned.
   780	 *
   781	 * NOTE: inline back refs are ordered in the same way that back ref
   782	 *	 items in the tree are ordered.
   783	 */
   784	static noinline_for_stack
   785	int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
   786					 struct btrfs_path *path,
   787					 struct btrfs_extent_inline_ref **ref_ret,
   788					 u64 bytenr, u64 num_bytes,
   789					 u64 parent, u64 root_objectid,
   790					 u64 owner, u64 offset, int insert)
   791	{
   792		struct btrfs_fs_info *fs_info = trans->fs_info;
   793		struct btrfs_root *root = btrfs_extent_root(fs_info, bytenr);
   794		struct btrfs_key key;
   795		struct extent_buffer *leaf;
   796		struct btrfs_extent_item *ei;
   797		struct btrfs_extent_inline_ref *iref;
   798		u64 flags;
   799		u64 item_size;
   800		unsigned long ptr;
   801		unsigned long end;
   802		int extra_size;
   803		int type;
   804		int want;
   805		int ret;
   806		int err = 0;
   807		bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
   808		int needed;
   809	
   810		key.objectid = bytenr;
   811		key.type = BTRFS_EXTENT_ITEM_KEY;
   812		key.offset = num_bytes;
   813	
   814		want = extent_ref_type(parent, owner);
   815		if (insert) {
   816			extra_size = btrfs_extent_inline_ref_size(want);
   817			path->search_for_extension = 1;
   818			path->keep_locks = 1;
   819		} else
   820			extra_size = -1;
   821	
   822		/*
   823		 * Owner is our level, so we can just add one to get the level for the
   824		 * block we are interested in.
   825		 */
   826		if (skinny_metadata && owner < BTRFS_FIRST_FREE_OBJECTID) {
   827			key.type = BTRFS_METADATA_ITEM_KEY;
   828			key.offset = owner;
   829		}
   830	
   831	again:
   832		ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
   833		if (ret < 0) {
   834			err = ret;
   835			goto out;
   836		}
   837	
   838		/*
   839		 * We may be a newly converted file system which still has the old fat
   840		 * extent entries for metadata, so try and see if we have one of those.
   841		 */
   842		if (ret > 0 && skinny_metadata) {
   843			skinny_metadata = false;
   844			if (path->slots[0]) {
   845				path->slots[0]--;
   846				btrfs_item_key_to_cpu(path->nodes[0], &key,
   847						      path->slots[0]);
   848				if (key.objectid == bytenr &&
   849				    key.type == BTRFS_EXTENT_ITEM_KEY &&
   850				    key.offset == num_bytes)
   851					ret = 0;
   852			}
   853			if (ret) {
   854				key.objectid = bytenr;
   855				key.type = BTRFS_EXTENT_ITEM_KEY;
   856				key.offset = num_bytes;
   857				btrfs_release_path(path);
   858				goto again;
   859			}
   860		}
   861	
   862		if (ret && !insert) {
   863			err = -ENOENT;
   864			goto out;
   865		} else if (WARN_ON(ret)) {
   866			err = -EIO;
   867			goto out;
   868		}
   869	
   870		leaf = path->nodes[0];
   871		item_size = btrfs_item_size(leaf, path->slots[0]);
   872		if (unlikely(item_size < sizeof(*ei))) {
   873			err = -EINVAL;
   874			btrfs_print_v0_err(fs_info);
   875			btrfs_abort_transaction(trans, err);
   876			goto out;
   877		}
   878	
   879		ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
   880		flags = btrfs_extent_flags(leaf, ei);
   881	
   882		ptr = (unsigned long)(ei + 1);
   883		end = (unsigned long)ei + item_size;
   884	
   885		if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
   886			ptr += sizeof(struct btrfs_tree_block_info);
   887			BUG_ON(ptr > end);
   888		}
   889	
   890		if (owner >= BTRFS_FIRST_FREE_OBJECTID)
   891			needed = BTRFS_REF_TYPE_DATA;
   892		else
   893			needed = BTRFS_REF_TYPE_BLOCK;
   894	
   895		err = -ENOENT;
   896		while (1) {
   897			if (ptr >= end) {
   898				if (WARN_ON(ptr > end)) {
   899					err = -EUCLEAN;
 > 900					btrfs_print_leaf(path->slots[0]);
   901				}
   902				break;
   903			}
   904			iref = (struct btrfs_extent_inline_ref *)ptr;
   905			type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
   906			if (type == BTRFS_REF_TYPE_INVALID) {
   907				err = -EUCLEAN;
   908				goto out;
   909			}
   910	
   911			if (want < type)
   912				break;
   913			if (want > type) {
   914				ptr += btrfs_extent_inline_ref_size(type);
   915				continue;
   916			}
   917	
   918			if (type == BTRFS_EXTENT_DATA_REF_KEY) {
   919				struct btrfs_extent_data_ref *dref;
   920				dref = (struct btrfs_extent_data_ref *)(&iref->offset);
   921				if (match_extent_data_ref(leaf, dref, root_objectid,
   922							  owner, offset)) {
   923					err = 0;
   924					break;
   925				}
   926				if (hash_extent_data_ref_item(leaf, dref) <
   927				    hash_extent_data_ref(root_objectid, owner, offset))
   928					break;
   929			} else {
   930				u64 ref_offset;
   931				ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
   932				if (parent > 0) {
   933					if (parent == ref_offset) {
   934						err = 0;
   935						break;
   936					}
   937					if (ref_offset < parent)
   938						break;
   939				} else {
   940					if (root_objectid == ref_offset) {
   941						err = 0;
   942						break;
   943					}
   944					if (ref_offset < root_objectid)
   945						break;
   946				}
   947			}
   948			ptr += btrfs_extent_inline_ref_size(type);
   949		}
   950		if (err == -ENOENT && insert) {
   951			if (item_size + extra_size >=
   952			    BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
   953				err = -EAGAIN;
   954				goto out;
   955			}
   956			/*
   957			 * To add new inline back ref, we have to make sure
   958			 * there is no corresponding back ref item.
   959			 * For simplicity, we just do not add new inline back
   960			 * ref if there is any kind of item for this block
   961			 */
   962			if (find_next_key(path, 0, &key) == 0 &&
   963			    key.objectid == bytenr &&
   964			    key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
   965				err = -EAGAIN;
   966				goto out;
   967			}
   968		}
   969		*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
   970	out:
   971		if (insert) {
   972			path->keep_locks = 0;
   973			path->search_for_extension = 0;
   974			btrfs_unlock_up_safe(path, 1);
   975		}
   976		return err;
   977	}
   978
kernel test robot April 21, 2022, 6:43 a.m. UTC | #6
Hi Nikolay,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.18-rc3 next-20220420]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: i386-randconfig-s002 (https://download.01.org/0day-ci/archive/20220421/202204210801.4G6iUKKd-lkp@intel.com/config)
compiler: gcc-11 (Debian 11.2.0-20) 11.2.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.4-dirty
        # https://github.com/intel-lab-lkp/linux/commit/a77df79d2f67b166cb64a21808ec432edcbf5bba
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
        git checkout a77df79d2f67b166cb64a21808ec432edcbf5bba
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=i386 SHELL=/bin/bash fs/btrfs/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)
>> fs/btrfs/extent-tree.c:900:61: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected struct extent_buffer *l @@     got int @@
   fs/btrfs/extent-tree.c:900:61: sparse:     expected struct extent_buffer *l
   fs/btrfs/extent-tree.c:900:61: sparse:     got int
   fs/btrfs/extent-tree.c:1784:9: sparse: sparse: context imbalance in 'run_and_cleanup_extent_op' - unexpected unlock
   fs/btrfs/extent-tree.c:1838:28: sparse: sparse: context imbalance in 'cleanup_ref_head' - unexpected unlock
   fs/btrfs/extent-tree.c:1917:36: sparse: sparse: context imbalance in 'btrfs_run_delayed_refs_for_head' - unexpected unlock
   fs/btrfs/extent-tree.c:1982:21: sparse: sparse: context imbalance in '__btrfs_run_delayed_refs' - wrong count at exit

vim +900 fs/btrfs/extent-tree.c

   770	
   771	/*
   772	 * look for inline back ref. if back ref is found, *ref_ret is set
   773	 * to the address of inline back ref, and 0 is returned.
   774	 *
   775	 * if back ref isn't found, *ref_ret is set to the address where it
   776	 * should be inserted, and -ENOENT is returned.
   777	 *
   778	 * if insert is true and there are too many inline back refs, the path
   779	 * points to the extent item, and -EAGAIN is returned.
   780	 *
   781	 * NOTE: inline back refs are ordered in the same way that back ref
   782	 *	 items in the tree are ordered.
   783	 */
   784	static noinline_for_stack
   785	int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
   786					 struct btrfs_path *path,
   787					 struct btrfs_extent_inline_ref **ref_ret,
   788					 u64 bytenr, u64 num_bytes,
   789					 u64 parent, u64 root_objectid,
   790					 u64 owner, u64 offset, int insert)
   791	{
   792		struct btrfs_fs_info *fs_info = trans->fs_info;
   793		struct btrfs_root *root = btrfs_extent_root(fs_info, bytenr);
   794		struct btrfs_key key;
   795		struct extent_buffer *leaf;
   796		struct btrfs_extent_item *ei;
   797		struct btrfs_extent_inline_ref *iref;
   798		u64 flags;
   799		u64 item_size;
   800		unsigned long ptr;
   801		unsigned long end;
   802		int extra_size;
   803		int type;
   804		int want;
   805		int ret;
   806		int err = 0;
   807		bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
   808		int needed;
   809	
   810		key.objectid = bytenr;
   811		key.type = BTRFS_EXTENT_ITEM_KEY;
   812		key.offset = num_bytes;
   813	
   814		want = extent_ref_type(parent, owner);
   815		if (insert) {
   816			extra_size = btrfs_extent_inline_ref_size(want);
   817			path->search_for_extension = 1;
   818			path->keep_locks = 1;
   819		} else
   820			extra_size = -1;
   821	
   822		/*
   823		 * Owner is our level, so we can just add one to get the level for the
   824		 * block we are interested in.
   825		 */
   826		if (skinny_metadata && owner < BTRFS_FIRST_FREE_OBJECTID) {
   827			key.type = BTRFS_METADATA_ITEM_KEY;
   828			key.offset = owner;
   829		}
   830	
   831	again:
   832		ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
   833		if (ret < 0) {
   834			err = ret;
   835			goto out;
   836		}
   837	
   838		/*
   839		 * We may be a newly converted file system which still has the old fat
   840		 * extent entries for metadata, so try and see if we have one of those.
   841		 */
   842		if (ret > 0 && skinny_metadata) {
   843			skinny_metadata = false;
   844			if (path->slots[0]) {
   845				path->slots[0]--;
   846				btrfs_item_key_to_cpu(path->nodes[0], &key,
   847						      path->slots[0]);
   848				if (key.objectid == bytenr &&
   849				    key.type == BTRFS_EXTENT_ITEM_KEY &&
   850				    key.offset == num_bytes)
   851					ret = 0;
   852			}
   853			if (ret) {
   854				key.objectid = bytenr;
   855				key.type = BTRFS_EXTENT_ITEM_KEY;
   856				key.offset = num_bytes;
   857				btrfs_release_path(path);
   858				goto again;
   859			}
   860		}
   861	
   862		if (ret && !insert) {
   863			err = -ENOENT;
   864			goto out;
   865		} else if (WARN_ON(ret)) {
   866			err = -EIO;
   867			goto out;
   868		}
   869	
   870		leaf = path->nodes[0];
   871		item_size = btrfs_item_size(leaf, path->slots[0]);
   872		if (unlikely(item_size < sizeof(*ei))) {
   873			err = -EINVAL;
   874			btrfs_print_v0_err(fs_info);
   875			btrfs_abort_transaction(trans, err);
   876			goto out;
   877		}
   878	
   879		ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
   880		flags = btrfs_extent_flags(leaf, ei);
   881	
   882		ptr = (unsigned long)(ei + 1);
   883		end = (unsigned long)ei + item_size;
   884	
   885		if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
   886			ptr += sizeof(struct btrfs_tree_block_info);
   887			BUG_ON(ptr > end);
   888		}
   889	
   890		if (owner >= BTRFS_FIRST_FREE_OBJECTID)
   891			needed = BTRFS_REF_TYPE_DATA;
   892		else
   893			needed = BTRFS_REF_TYPE_BLOCK;
   894	
   895		err = -ENOENT;
   896		while (1) {
   897			if (ptr >= end) {
   898				if (WARN_ON(ptr > end)) {
   899					err = -EUCLEAN;
 > 900					btrfs_print_leaf(path->slots[0]);
   901				}
   902				break;
   903			}
   904			iref = (struct btrfs_extent_inline_ref *)ptr;
   905			type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
   906			if (type == BTRFS_REF_TYPE_INVALID) {
   907				err = -EUCLEAN;
   908				goto out;
   909			}
   910	
   911			if (want < type)
   912				break;
   913			if (want > type) {
   914				ptr += btrfs_extent_inline_ref_size(type);
   915				continue;
   916			}
   917	
   918			if (type == BTRFS_EXTENT_DATA_REF_KEY) {
   919				struct btrfs_extent_data_ref *dref;
   920				dref = (struct btrfs_extent_data_ref *)(&iref->offset);
   921				if (match_extent_data_ref(leaf, dref, root_objectid,
   922							  owner, offset)) {
   923					err = 0;
   924					break;
   925				}
   926				if (hash_extent_data_ref_item(leaf, dref) <
   927				    hash_extent_data_ref(root_objectid, owner, offset))
   928					break;
   929			} else {
   930				u64 ref_offset;
   931				ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
   932				if (parent > 0) {
   933					if (parent == ref_offset) {
   934						err = 0;
   935						break;
   936					}
   937					if (ref_offset < parent)
   938						break;
   939				} else {
   940					if (root_objectid == ref_offset) {
   941						err = 0;
   942						break;
   943					}
   944					if (ref_offset < root_objectid)
   945						break;
   946				}
   947			}
   948			ptr += btrfs_extent_inline_ref_size(type);
   949		}
   950		if (err == -ENOENT && insert) {
   951			if (item_size + extra_size >=
   952			    BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
   953				err = -EAGAIN;
   954				goto out;
   955			}
   956			/*
   957			 * To add new inline back ref, we have to make sure
   958			 * there is no corresponding back ref item.
   959			 * For simplicity, we just do not add new inline back
   960			 * ref if there is any kind of item for this block
   961			 */
   962			if (find_next_key(path, 0, &key) == 0 &&
   963			    key.objectid == bytenr &&
   964			    key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
   965				err = -EAGAIN;
   966				goto out;
   967			}
   968		}
   969		*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
   970	out:
   971		if (insert) {
   972			path->keep_locks = 0;
   973			path->search_for_extension = 0;
   974			btrfs_unlock_up_safe(path, 1);
   975		}
   976		return err;
   977	}
   978
kernel test robot April 21, 2022, 6:44 a.m. UTC | #7
Hi Nikolay,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on kdave/for-next]
[also build test WARNING on v5.18-rc3 next-20220420]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/intel-lab-lkp/linux/commits/Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: alpha-randconfig-s032-20220420 (https://download.01.org/0day-ci/archive/20220421/202204210906.VK0dZtcM-lkp@intel.com/config)
compiler: alpha-linux-gcc (GCC) 11.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # apt-get install sparse
        # sparse version: v0.6.4-dirty
        # https://github.com/intel-lab-lkp/linux/commit/a77df79d2f67b166cb64a21808ec432edcbf5bba
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Nikolay-Borisov/btrfs-Improve-error-reporting-in-lookup_inline_extent_backref/20220420-195528
        git checkout a77df79d2f67b166cb64a21808ec432edcbf5bba
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' O=build_dir ARCH=alpha SHELL=/bin/bash fs/btrfs/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)
   fs/btrfs/extent-tree.c:900:61: sparse: sparse: incorrect type in argument 1 (different base types) @@     expected struct extent_buffer *l @@     got int @@
   fs/btrfs/extent-tree.c:900:61: sparse:     expected struct extent_buffer *l
   fs/btrfs/extent-tree.c:900:61: sparse:     got int
>> fs/btrfs/extent-tree.c:900:50: sparse: sparse: non size-preserving integer to pointer cast
   fs/btrfs/extent-tree.c:1784:9: sparse: sparse: context imbalance in 'run_and_cleanup_extent_op' - unexpected unlock
   fs/btrfs/extent-tree.c:1838:28: sparse: sparse: context imbalance in 'cleanup_ref_head' - unexpected unlock
   fs/btrfs/extent-tree.c:1917:36: sparse: sparse: context imbalance in 'btrfs_run_delayed_refs_for_head' - unexpected unlock
   fs/btrfs/extent-tree.c:1982:21: sparse: sparse: context imbalance in '__btrfs_run_delayed_refs' - wrong count at exit

vim +900 fs/btrfs/extent-tree.c

   770	
   771	/*
   772	 * look for inline back ref. if back ref is found, *ref_ret is set
   773	 * to the address of inline back ref, and 0 is returned.
   774	 *
   775	 * if back ref isn't found, *ref_ret is set to the address where it
   776	 * should be inserted, and -ENOENT is returned.
   777	 *
   778	 * if insert is true and there are too many inline back refs, the path
   779	 * points to the extent item, and -EAGAIN is returned.
   780	 *
   781	 * NOTE: inline back refs are ordered in the same way that back ref
   782	 *	 items in the tree are ordered.
   783	 */
   784	static noinline_for_stack
   785	int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
   786					 struct btrfs_path *path,
   787					 struct btrfs_extent_inline_ref **ref_ret,
   788					 u64 bytenr, u64 num_bytes,
   789					 u64 parent, u64 root_objectid,
   790					 u64 owner, u64 offset, int insert)
   791	{
   792		struct btrfs_fs_info *fs_info = trans->fs_info;
   793		struct btrfs_root *root = btrfs_extent_root(fs_info, bytenr);
   794		struct btrfs_key key;
   795		struct extent_buffer *leaf;
   796		struct btrfs_extent_item *ei;
   797		struct btrfs_extent_inline_ref *iref;
   798		u64 flags;
   799		u64 item_size;
   800		unsigned long ptr;
   801		unsigned long end;
   802		int extra_size;
   803		int type;
   804		int want;
   805		int ret;
   806		int err = 0;
   807		bool skinny_metadata = btrfs_fs_incompat(fs_info, SKINNY_METADATA);
   808		int needed;
   809	
   810		key.objectid = bytenr;
   811		key.type = BTRFS_EXTENT_ITEM_KEY;
   812		key.offset = num_bytes;
   813	
   814		want = extent_ref_type(parent, owner);
   815		if (insert) {
   816			extra_size = btrfs_extent_inline_ref_size(want);
   817			path->search_for_extension = 1;
   818			path->keep_locks = 1;
   819		} else
   820			extra_size = -1;
   821	
   822		/*
   823		 * Owner is our level, so we can just add one to get the level for the
   824		 * block we are interested in.
   825		 */
   826		if (skinny_metadata && owner < BTRFS_FIRST_FREE_OBJECTID) {
   827			key.type = BTRFS_METADATA_ITEM_KEY;
   828			key.offset = owner;
   829		}
   830	
   831	again:
   832		ret = btrfs_search_slot(trans, root, &key, path, extra_size, 1);
   833		if (ret < 0) {
   834			err = ret;
   835			goto out;
   836		}
   837	
   838		/*
   839		 * We may be a newly converted file system which still has the old fat
   840		 * extent entries for metadata, so try and see if we have one of those.
   841		 */
   842		if (ret > 0 && skinny_metadata) {
   843			skinny_metadata = false;
   844			if (path->slots[0]) {
   845				path->slots[0]--;
   846				btrfs_item_key_to_cpu(path->nodes[0], &key,
   847						      path->slots[0]);
   848				if (key.objectid == bytenr &&
   849				    key.type == BTRFS_EXTENT_ITEM_KEY &&
   850				    key.offset == num_bytes)
   851					ret = 0;
   852			}
   853			if (ret) {
   854				key.objectid = bytenr;
   855				key.type = BTRFS_EXTENT_ITEM_KEY;
   856				key.offset = num_bytes;
   857				btrfs_release_path(path);
   858				goto again;
   859			}
   860		}
   861	
   862		if (ret && !insert) {
   863			err = -ENOENT;
   864			goto out;
   865		} else if (WARN_ON(ret)) {
   866			err = -EIO;
   867			goto out;
   868		}
   869	
   870		leaf = path->nodes[0];
   871		item_size = btrfs_item_size(leaf, path->slots[0]);
   872		if (unlikely(item_size < sizeof(*ei))) {
   873			err = -EINVAL;
   874			btrfs_print_v0_err(fs_info);
   875			btrfs_abort_transaction(trans, err);
   876			goto out;
   877		}
   878	
   879		ei = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_extent_item);
   880		flags = btrfs_extent_flags(leaf, ei);
   881	
   882		ptr = (unsigned long)(ei + 1);
   883		end = (unsigned long)ei + item_size;
   884	
   885		if (flags & BTRFS_EXTENT_FLAG_TREE_BLOCK && !skinny_metadata) {
   886			ptr += sizeof(struct btrfs_tree_block_info);
   887			BUG_ON(ptr > end);
   888		}
   889	
   890		if (owner >= BTRFS_FIRST_FREE_OBJECTID)
   891			needed = BTRFS_REF_TYPE_DATA;
   892		else
   893			needed = BTRFS_REF_TYPE_BLOCK;
   894	
   895		err = -ENOENT;
   896		while (1) {
   897			if (ptr >= end) {
   898				if (WARN_ON(ptr > end)) {
   899					err = -EUCLEAN;
 > 900					btrfs_print_leaf(path->slots[0]);
   901				}
   902				break;
   903			}
   904			iref = (struct btrfs_extent_inline_ref *)ptr;
   905			type = btrfs_get_extent_inline_ref_type(leaf, iref, needed);
   906			if (type == BTRFS_REF_TYPE_INVALID) {
   907				err = -EUCLEAN;
   908				goto out;
   909			}
   910	
   911			if (want < type)
   912				break;
   913			if (want > type) {
   914				ptr += btrfs_extent_inline_ref_size(type);
   915				continue;
   916			}
   917	
   918			if (type == BTRFS_EXTENT_DATA_REF_KEY) {
   919				struct btrfs_extent_data_ref *dref;
   920				dref = (struct btrfs_extent_data_ref *)(&iref->offset);
   921				if (match_extent_data_ref(leaf, dref, root_objectid,
   922							  owner, offset)) {
   923					err = 0;
   924					break;
   925				}
   926				if (hash_extent_data_ref_item(leaf, dref) <
   927				    hash_extent_data_ref(root_objectid, owner, offset))
   928					break;
   929			} else {
   930				u64 ref_offset;
   931				ref_offset = btrfs_extent_inline_ref_offset(leaf, iref);
   932				if (parent > 0) {
   933					if (parent == ref_offset) {
   934						err = 0;
   935						break;
   936					}
   937					if (ref_offset < parent)
   938						break;
   939				} else {
   940					if (root_objectid == ref_offset) {
   941						err = 0;
   942						break;
   943					}
   944					if (ref_offset < root_objectid)
   945						break;
   946				}
   947			}
   948			ptr += btrfs_extent_inline_ref_size(type);
   949		}
   950		if (err == -ENOENT && insert) {
   951			if (item_size + extra_size >=
   952			    BTRFS_MAX_EXTENT_ITEM_SIZE(root)) {
   953				err = -EAGAIN;
   954				goto out;
   955			}
   956			/*
   957			 * To add new inline back ref, we have to make sure
   958			 * there is no corresponding back ref item.
   959			 * For simplicity, we just do not add new inline back
   960			 * ref if there is any kind of item for this block
   961			 */
   962			if (find_next_key(path, 0, &key) == 0 &&
   963			    key.objectid == bytenr &&
   964			    key.type < BTRFS_BLOCK_GROUP_ITEM_KEY) {
   965				err = -EAGAIN;
   966				goto out;
   967			}
   968		}
   969		*ref_ret = (struct btrfs_extent_inline_ref *)ptr;
   970	out:
   971		if (insert) {
   972			path->keep_locks = 0;
   973			path->search_for_extension = 0;
   974			btrfs_unlock_up_safe(path, 1);
   975		}
   976		return err;
   977	}
   978
Filipe Manana April 22, 2022, 10:11 a.m. UTC | #8
On Wed, Apr 20, 2022 at 04:52:04PM +0200, David Sterba wrote:
> On Wed, Apr 20, 2022 at 02:54:00PM +0300, Nikolay Borisov wrote:
> > When iterating the backrefs in an extent item if the ptr to the
> > 'current' backref record goes beyond the extent item a warning is
> > generated and -ENOENT is returned. However what's more appropriate to
> > debug such cases would be to return EUCLEAN and also print the in-memory
> > state of the offending leaf.

How does printing only the leaf helps debugging anything?

You get the leaf dumped, but how do you know what you should be looking for?
Which key in the leaf, and for which inline backref are we searching for?

Dumping the leaf alone is not really useful, unless we also mention what we
are searching for...

So this should print as well:

1) The key, which tells us which key to look at, the extent's bytenr, type
   and size or owner;

2) The type of reference we are looking for, stored in the variable 'want';

3) The values of the variables 'root_objectid', 'parent' and 'offset'.
   These are used to search for the backreference we want, once we find
   one with the type we want.

Thanks.

> 
> Agreed, EUCLEAN makese sense. Added to misc-next, thanks.
diff mbox series

Patch

diff --git a/fs/btrfs/extent-tree.c b/fs/btrfs/extent-tree.c
index 963160a0c393..5a53bcfdca83 100644
--- a/fs/btrfs/extent-tree.c
+++ b/fs/btrfs/extent-tree.c
@@ -895,7 +895,10 @@  int lookup_inline_extent_backref(struct btrfs_trans_handle *trans,
 	err = -ENOENT;
 	while (1) {
 		if (ptr >= end) {
-			WARN_ON(ptr > end);
+			if (WARN_ON(ptr > end)) {
+				err = -EUCLEAN;
+				btrfs_print_leaf(path->slots[0]);
+			}
 			break;
 		}
 		iref = (struct btrfs_extent_inline_ref *)ptr;