diff mbox

[v1,3/6] scrub: print paths of corrupted files

Message ID 4dff2bfbba0f059cf91185c290d0c78bf9f6648d.1307991539.git.list.btrfs@jan-o-sch.net (mailing list archive)
State New, archived
Headers show

Commit Message

Jan Schmidt June 13, 2011, 7:10 p.m. UTC
While scrubbing, we may encounter various errors. Previously, a logical
address was printed to the log only. Now, all paths belonging to that
address are resolved and printed separately. That should work for hardlinks
as well as reflinks.

Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
---
 fs/btrfs/scrub.c |  140 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 137 insertions(+), 3 deletions(-)

Comments

David Sterba June 16, 2011, 9:26 p.m. UTC | #1
On Mon, Jun 13, 2011 at 09:10:36PM +0200, Jan Schmidt wrote:
> While scrubbing, we may encounter various errors. Previously, a logical
> address was printed to the log only. Now, all paths belonging to that
> address are resolved and printed separately. That should work for hardlinks
> as well as reflinks.
> 
> Signed-off-by: Jan Schmidt <list.btrfs@jan-o-sch.net>
> ---
>  fs/btrfs/scrub.c |  140 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 files changed, 137 insertions(+), 3 deletions(-)
> 
> diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
> index 00e4e58..e294d76 100644
> --- a/fs/btrfs/scrub.c
> +++ b/fs/btrfs/scrub.c
> @@ -27,6 +27,7 @@
>  #include "volumes.h"
>  #include "disk-io.h"
>  #include "ordered-data.h"
> +#include "backref.h"
>  
>  /*
>   * This is only the first step towards a full-features scrub. It reads all
> @@ -106,6 +107,19 @@ struct scrub_dev {
>  	spinlock_t		stat_lock;
>  };
>  
> +struct scrub_warning {
> +	struct btrfs_path	*path;
> +	u64			extent_item_size;
> +	char			*scratch_buf;
> +	char			*msg_buf;
> +	const char		*errstr;
> +	u64			sector;

better named 'physical', like in scrub_bio

> +	u64			logical;
> +	struct btrfs_device	*dev;
> +	int			msg_bufsize;
> +	int			scratch_bufsize;
> +};
> +
>  static void scrub_free_csums(struct scrub_dev *sdev)
>  {
>  	while (!list_empty(&sdev->csum_list)) {
> @@ -201,6 +215,121 @@ nomem:
>  	return ERR_PTR(-ENOMEM);
>  }
>  
> +static int scrub_print_warning_inode(u64 inum, loff_t offset, void *ctx)
> +{
> +	u64 isize;
> +	int ret;
> +	struct extent_buffer *eb_i;

'eb' for consistency with others

> +	struct btrfs_inode_item *ii;

inode_item is a commonly used name

> +	struct scrub_warning *swarn = ctx;
> +	struct btrfs_fs_info *fs_info = swarn->dev->dev_root->fs_info;
> +	struct inode_fs_paths ipath;
> +
> +	ret = inode_item_info(inum, 0, fs_info->fs_root, swarn->path);
> +	if (ret) {
> +err:
> +		printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
> +			"%s, sector %llu, inode %llu, offset %llu: path "
> +			"resolving failed with ret=%d\n", swarn->errstr,
> +			swarn->logical, swarn->dev->name, swarn->sector, inum,
> +			offset, ret);
> +		return 0;

please move it to the end after the other printk

> +	}
> +	eb_i = swarn->path->nodes[0];
> +	ii = btrfs_item_ptr(eb_i, swarn->path->slots[0],
> +				struct btrfs_inode_item);
> +	btrfs_release_path(swarn->path);
> +
> +	isize = btrfs_inode_size(eb_i, ii);
> +
> +	ipath.left = swarn->msg_bufsize - 1;
> +	ipath.dest = swarn->msg_buf;
> +	ipath.path = swarn->path;
> +	ipath.scratch_buf = swarn->scratch_buf;
> +	ipath.scratch_bufsize = swarn->scratch_bufsize;
> +	ipath.fs_root = fs_info->fs_root;
> +	ipath.eb_i = eb_i;
> +
> +	ret = paths_from_inode(inum, &ipath);
> +	if (ret < 0)
> +		goto err;
> +
> +	printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
> +		"%s, sector %llu, inode %llu, offset %llu, "
> +		"length %llu, links %u (path:%s)\n", swarn->errstr,
> +		swarn->logical, swarn->dev->name, swarn->sector, inum, offset,
> +		min(isize - offset, (u64)PAGE_SIZE),
> +		btrfs_inode_nlink(eb_i, ii), swarn->msg_buf);
> +
> +	return 0;
> +}
> +
> +static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
> +				int ix)

please rename 'ix' to eg 'idx', more readable

> +{
> +	struct btrfs_device *dev = sbio->sdev->dev;
> +	struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
> +	struct btrfs_path *path;
> +	struct btrfs_key found_key;
> +	struct extent_buffer *eb;
> +	struct btrfs_extent_item *ei;
> +	struct scrub_warning swarn;
> +	u32 item_size;
> +	int ret;
> +	u64 ref_root;

i'm not able to understand what a u64 value describing a root means, seeing it
here or in the printk message. it's returned as 

	*dest_root = btrfs_extent_inline_ref_offset(eb, eiref);

can you please explain it to me?

> +	u64 ref_level;

int is enough, as noted elsewhere

> +	unsigned long ptr = 0;
> +	static const int bufsize = 4096;

drop static, this would allocate a memory cell to hold the value 4096, while
without it, it's just a named constant and this will make the compiler happy

> +	loff_t extent_item_offset;

loff_t is a signed long long, do you really need it?

> +
> +	path = btrfs_alloc_path();
> +
> +	swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
> +	swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
> +	swarn.sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
> +	swarn.logical = sbio->logical + ix * PAGE_SIZE;
> +	swarn.errstr = errstr;
> +	swarn.dev = dev;
> +	swarn.msg_bufsize = bufsize;
> +	swarn.scratch_bufsize = bufsize;
> +
> +	if (!path || !swarn.scratch_buf || !swarn.msg_buf)
> +		goto out;
> +
> +	ret = data_extent_from_logical(fs_info->extent_root,
> +					swarn.logical, path, &found_key);
> +	if (ret < 0)
> +		goto out;
> +
> +	extent_item_offset = swarn.logical - found_key.objectid;
> +	swarn.extent_item_size = found_key.offset;
> +
> +	eb = path->nodes[0];
> +	ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item);
> +	item_size = btrfs_item_size_nr(eb, path->slots[0]);
> +
> +	btrfs_release_path(path);

is it safe to release the path here? it's used in the else branch below

> +
> +	if (ret) {
> +		ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
> +						&ref_root, &ref_level);
> +		printk(KERN_WARNING "%s at logical %llu on dev %s, "
> +			"sector %llu: metadata %s (level %llu) in tree %llu\n",
> +			errstr, swarn.logical, dev->name, swarn.sector,
> +			ref_level ? "node" : "leaf", ret < 0 ? -1 : ref_level,
> +			ret < 0 ? -1 : ref_root);
> +	} else {
> +		swarn.path = path;
> +		iterate_extent_inodes(eb, ei, extent_item_offset, item_size,
> +					scrub_print_warning_inode, &swarn);
> +	}
> +
> +out:
> +	btrfs_free_path(path);
> +	kfree(swarn.scratch_buf);
> +	kfree(swarn.msg_buf);
> +}
> +
>  /*
>   * scrub_recheck_error gets called when either verification of the page
>   * failed or the bio failed to read, e.g. with EIO. In the latter case,
> @@ -218,6 +347,11 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
>  			if (scrub_fixup_check(sbio, ix) == 0)
>  				return 0;
>  		}
> +		if (printk_ratelimit())

please don't use printk_ratelimit; as a simple printk_ratelimited is not
applicable here, we have to use __ratelimit: (include/linux/printk.h)

static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
	DEFAULT_RATELIMIT_BURST);

if (__ratelimit(&_rs))

> +			scrub_print_warning("i/o error", sbio, ix);
> +	} else {
> +		if (printk_ratelimit())

and the ratelimit context can be even shared

> +			scrub_print_warning("checksum error", sbio, ix);
>  	}
>  
>  	spin_lock(&sdev->stat_lock);
> @@ -333,7 +467,7 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix)
>  	spin_unlock(&sdev->stat_lock);
>  
>  	if (printk_ratelimit())

(drop)

> -		printk(KERN_ERR "btrfs: fixed up at %llu\n",
> +		printk(KERN_ERR "btrfs: fixed up error at logical %llu\n",
>  		       (unsigned long long)logical);

                printk_ratelimited

seems that I forgot to convert this (and the one below) some time ago. although
changes should not be mixed within a patch, I guess this one is rather simple
and a good thing to do. 

>  	return;
>  
> @@ -344,8 +478,8 @@ uncorrectable:
>  	spin_unlock(&sdev->stat_lock);
>  
>  	if (printk_ratelimit())

(drop)

> -		printk(KERN_ERR "btrfs: unable to fixup at %llu\n",
> -			 (unsigned long long)logical);
> +		printk(KERN_ERR "btrfs: unable to fixup (regular) error at "
> +			"logical %llu\n", (unsigned long long)logical);

                printk_ratelimited

>  }
>  
>  static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector,
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Jan Schmidt June 18, 2011, 11:26 a.m. UTC | #2
Lots of quotation removed. All removed comments accepted.

On 16.06.2011 23:26, David Sterba wrote:
> On Mon, Jun 13, 2011 at 09:10:36PM +0200, Jan Schmidt wrote:
>> +struct scrub_warning {
>> +	struct btrfs_path	*path;
>> +	u64			extent_item_size;
>> +	char			*scratch_buf;
>> +	char			*msg_buf;
>> +	const char		*errstr;
>> +	u64			sector;
> 
> better named 'physical', like in scrub_bio

As I save a sector there and not a physical address, I still prefer
"sector". But as a compromise, I'll change the type to sector_t instead.

>> +static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
>> +				int ix)
> 
> please rename 'ix' to eg 'idx', more readable

I'd like to stay consistent with the rest of the file, using ix at
similar opportunities. And besides: I like ix more than idx :-)

>> +{
>> +	struct btrfs_device *dev = sbio->sdev->dev;
>> +	struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
>> +	struct btrfs_path *path;
>> +	struct btrfs_key found_key;
>> +	struct extent_buffer *eb;
>> +	struct btrfs_extent_item *ei;
>> +	struct scrub_warning swarn;
>> +	u32 item_size;
>> +	int ret;
>> +	u64 ref_root;
> 
> i'm not able to understand what a u64 value describing a root means, seeing it
> here or in the printk message. it's returned as 
> 
> 	*dest_root = btrfs_extent_inline_ref_offset(eb, eiref);
> 
> can you please explain it to me?

Have a look at the BTRFS_*_TREE_OBJECTID #defines in ctree.h.
btrfs-debug-tree prints them as
                tree block backref root 2

>> +	u64 ref_level;
> 
> int is enough, as noted elsewhere
> 
>> +	unsigned long ptr = 0;
>> +	static const int bufsize = 4096;
> 
> drop static, this would allocate a memory cell to hold the value 4096, while
> without it, it's just a named constant and this will make the compiler happy
> 
>> +	loff_t extent_item_offset;
> 
> loff_t is a signed long long, do you really need it?

Seems I don't. I was looking around how I came to use loff_t in the
first place, but couldn't find a good reason. I'm replacing it with u64,
which makes more sense anyway, since the loff_t parameter I'm passing
around is the result of a subtraction of two u64s.

>> +
>> +	path = btrfs_alloc_path();
>> +
>> +	swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
>> +	swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
>> +	swarn.sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
>> +	swarn.logical = sbio->logical + ix * PAGE_SIZE;
>> +	swarn.errstr = errstr;
>> +	swarn.dev = dev;
>> +	swarn.msg_bufsize = bufsize;
>> +	swarn.scratch_bufsize = bufsize;
>> +
>> +	if (!path || !swarn.scratch_buf || !swarn.msg_buf)
>> +		goto out;
>> +
>> +	ret = data_extent_from_logical(fs_info->extent_root,
>> +					swarn.logical, path, &found_key);
>> +	if (ret < 0)
>> +		goto out;
>> +
>> +	extent_item_offset = swarn.logical - found_key.objectid;
>> +	swarn.extent_item_size = found_key.offset;
>> +
>> +	eb = path->nodes[0];
>> +	ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item);
>> +	item_size = btrfs_item_size_nr(eb, path->slots[0]);
>> +
>> +	btrfs_release_path(path);
> 
> is it safe to release the path here? it's used in the else branch below

Yes, and it's even required, since iterate_extent_inodes() wants a
clean, usable path for itself. To make that more clear, I'll move the
call to btrfs_release_path inside the else block.

>> +
>> +	if (ret) {
>> +		ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
>> +						&ref_root, &ref_level);
>> +		printk(KERN_WARNING "%s at logical %llu on dev %s, "
>> +			"sector %llu: metadata %s (level %llu) in tree %llu\n",
>> +			errstr, swarn.logical, dev->name, swarn.sector,
>> +			ref_level ? "node" : "leaf", ret < 0 ? -1 : ref_level,
>> +			ret < 0 ? -1 : ref_root);
>> +	} else {
>> +		swarn.path = path;
>> +		iterate_extent_inodes(eb, ei, extent_item_offset, item_size,
>> +					scrub_print_warning_inode, &swarn);
>> +	}
>> +
>> +out:
>> +	btrfs_free_path(path);
>> +	kfree(swarn.scratch_buf);
>> +	kfree(swarn.msg_buf);
>> +}
>> +
>>  /*
>>   * scrub_recheck_error gets called when either verification of the page
>>   * failed or the bio failed to read, e.g. with EIO. In the latter case,
>> @@ -218,6 +347,11 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
>>  			if (scrub_fixup_check(sbio, ix) == 0)
>>  				return 0;
>>  		}
>> +		if (printk_ratelimit())
> 
> please don't use printk_ratelimit; as a simple printk_ratelimited is not
> applicable here, we have to use __ratelimit: (include/linux/printk.h)
> 
> static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
> 	DEFAULT_RATELIMIT_BURST);
> 
> if (__ratelimit(&_rs))

Modified as you suggested, I only included linux/ratelimit.h instead.

Thanks!
Jan
--
To unsubscribe from this list: send the line "unsubscribe linux-btrfs" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/fs/btrfs/scrub.c b/fs/btrfs/scrub.c
index 00e4e58..e294d76 100644
--- a/fs/btrfs/scrub.c
+++ b/fs/btrfs/scrub.c
@@ -27,6 +27,7 @@ 
 #include "volumes.h"
 #include "disk-io.h"
 #include "ordered-data.h"
+#include "backref.h"
 
 /*
  * This is only the first step towards a full-features scrub. It reads all
@@ -106,6 +107,19 @@  struct scrub_dev {
 	spinlock_t		stat_lock;
 };
 
+struct scrub_warning {
+	struct btrfs_path	*path;
+	u64			extent_item_size;
+	char			*scratch_buf;
+	char			*msg_buf;
+	const char		*errstr;
+	u64			sector;
+	u64			logical;
+	struct btrfs_device	*dev;
+	int			msg_bufsize;
+	int			scratch_bufsize;
+};
+
 static void scrub_free_csums(struct scrub_dev *sdev)
 {
 	while (!list_empty(&sdev->csum_list)) {
@@ -201,6 +215,121 @@  nomem:
 	return ERR_PTR(-ENOMEM);
 }
 
+static int scrub_print_warning_inode(u64 inum, loff_t offset, void *ctx)
+{
+	u64 isize;
+	int ret;
+	struct extent_buffer *eb_i;
+	struct btrfs_inode_item *ii;
+	struct scrub_warning *swarn = ctx;
+	struct btrfs_fs_info *fs_info = swarn->dev->dev_root->fs_info;
+	struct inode_fs_paths ipath;
+
+	ret = inode_item_info(inum, 0, fs_info->fs_root, swarn->path);
+	if (ret) {
+err:
+		printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
+			"%s, sector %llu, inode %llu, offset %llu: path "
+			"resolving failed with ret=%d\n", swarn->errstr,
+			swarn->logical, swarn->dev->name, swarn->sector, inum,
+			offset, ret);
+		return 0;
+	}
+	eb_i = swarn->path->nodes[0];
+	ii = btrfs_item_ptr(eb_i, swarn->path->slots[0],
+				struct btrfs_inode_item);
+	btrfs_release_path(swarn->path);
+
+	isize = btrfs_inode_size(eb_i, ii);
+
+	ipath.left = swarn->msg_bufsize - 1;
+	ipath.dest = swarn->msg_buf;
+	ipath.path = swarn->path;
+	ipath.scratch_buf = swarn->scratch_buf;
+	ipath.scratch_bufsize = swarn->scratch_bufsize;
+	ipath.fs_root = fs_info->fs_root;
+	ipath.eb_i = eb_i;
+
+	ret = paths_from_inode(inum, &ipath);
+	if (ret < 0)
+		goto err;
+
+	printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
+		"%s, sector %llu, inode %llu, offset %llu, "
+		"length %llu, links %u (path:%s)\n", swarn->errstr,
+		swarn->logical, swarn->dev->name, swarn->sector, inum, offset,
+		min(isize - offset, (u64)PAGE_SIZE),
+		btrfs_inode_nlink(eb_i, ii), swarn->msg_buf);
+
+	return 0;
+}
+
+static void scrub_print_warning(const char *errstr, struct scrub_bio *sbio,
+				int ix)
+{
+	struct btrfs_device *dev = sbio->sdev->dev;
+	struct btrfs_fs_info *fs_info = dev->dev_root->fs_info;
+	struct btrfs_path *path;
+	struct btrfs_key found_key;
+	struct extent_buffer *eb;
+	struct btrfs_extent_item *ei;
+	struct scrub_warning swarn;
+	u32 item_size;
+	int ret;
+	u64 ref_root;
+	u64 ref_level;
+	unsigned long ptr = 0;
+	static const int bufsize = 4096;
+	loff_t extent_item_offset;
+
+	path = btrfs_alloc_path();
+
+	swarn.scratch_buf = kmalloc(bufsize, GFP_NOFS);
+	swarn.msg_buf = kmalloc(bufsize, GFP_NOFS);
+	swarn.sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
+	swarn.logical = sbio->logical + ix * PAGE_SIZE;
+	swarn.errstr = errstr;
+	swarn.dev = dev;
+	swarn.msg_bufsize = bufsize;
+	swarn.scratch_bufsize = bufsize;
+
+	if (!path || !swarn.scratch_buf || !swarn.msg_buf)
+		goto out;
+
+	ret = data_extent_from_logical(fs_info->extent_root,
+					swarn.logical, path, &found_key);
+	if (ret < 0)
+		goto out;
+
+	extent_item_offset = swarn.logical - found_key.objectid;
+	swarn.extent_item_size = found_key.offset;
+
+	eb = path->nodes[0];
+	ei = btrfs_item_ptr(eb, path->slots[0], struct btrfs_extent_item);
+	item_size = btrfs_item_size_nr(eb, path->slots[0]);
+
+	btrfs_release_path(path);
+
+	if (ret) {
+		ret = tree_backref_for_extent(&ptr, eb, ei, item_size,
+						&ref_root, &ref_level);
+		printk(KERN_WARNING "%s at logical %llu on dev %s, "
+			"sector %llu: metadata %s (level %llu) in tree %llu\n",
+			errstr, swarn.logical, dev->name, swarn.sector,
+			ref_level ? "node" : "leaf", ret < 0 ? -1 : ref_level,
+			ret < 0 ? -1 : ref_root);
+	} else {
+		swarn.path = path;
+		iterate_extent_inodes(eb, ei, extent_item_offset, item_size,
+					scrub_print_warning_inode, &swarn);
+	}
+
+out:
+	btrfs_free_path(path);
+	kfree(swarn.scratch_buf);
+	kfree(swarn.msg_buf);
+}
+
 /*
  * scrub_recheck_error gets called when either verification of the page
  * failed or the bio failed to read, e.g. with EIO. In the latter case,
@@ -218,6 +347,11 @@  static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
 			if (scrub_fixup_check(sbio, ix) == 0)
 				return 0;
 		}
+		if (printk_ratelimit())
+			scrub_print_warning("i/o error", sbio, ix);
+	} else {
+		if (printk_ratelimit())
+			scrub_print_warning("checksum error", sbio, ix);
 	}
 
 	spin_lock(&sdev->stat_lock);
@@ -333,7 +467,7 @@  static void scrub_fixup(struct scrub_bio *sbio, int ix)
 	spin_unlock(&sdev->stat_lock);
 
 	if (printk_ratelimit())
-		printk(KERN_ERR "btrfs: fixed up at %llu\n",
+		printk(KERN_ERR "btrfs: fixed up error at logical %llu\n",
 		       (unsigned long long)logical);
 	return;
 
@@ -344,8 +478,8 @@  uncorrectable:
 	spin_unlock(&sdev->stat_lock);
 
 	if (printk_ratelimit())
-		printk(KERN_ERR "btrfs: unable to fixup at %llu\n",
-			 (unsigned long long)logical);
+		printk(KERN_ERR "btrfs: unable to fixup (regular) error at "
+			"logical %llu\n", (unsigned long long)logical);
 }
 
 static int scrub_fixup_io(int rw, struct block_device *bdev, sector_t sector,