btrfs scrub: print paths of corrupted files
authorJan Schmidt <list.btrfs@jan-o-sch.net>
Mon, 13 Jun 2011 17:59:12 +0000 (19:59 +0200)
committerJan Schmidt <list.btrfs@jan-o-sch.net>
Thu, 29 Sep 2011 10:54:28 +0000 (12:54 +0200)
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

index 35099fa97d564433b17ec5e7f435f3ca87d38dd3..221fd5c48736d0629e01cd60cef9c42e2d9658fe 100644 (file)
  */
 
 #include <linux/blkdev.h>
+#include <linux/ratelimit.h>
 #include "ctree.h"
 #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
@@ -100,6 +102,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;
+       sector_t                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)) {
@@ -195,6 +210,143 @@ nomem:
        return ERR_PTR(-ENOMEM);
 }
 
+static int scrub_print_warning_inode(u64 inum, u64 offset, u64 root, void *ctx)
+{
+       u64 isize;
+       u32 nlink;
+       int ret;
+       int i;
+       struct extent_buffer *eb;
+       struct btrfs_inode_item *inode_item;
+       struct scrub_warning *swarn = ctx;
+       struct btrfs_fs_info *fs_info = swarn->dev->dev_root->fs_info;
+       struct inode_fs_paths *ipath = NULL;
+       struct btrfs_root *local_root;
+       struct btrfs_key root_key;
+
+       root_key.objectid = root;
+       root_key.type = BTRFS_ROOT_ITEM_KEY;
+       root_key.offset = (u64)-1;
+       local_root = btrfs_read_fs_root_no_name(fs_info, &root_key);
+       if (IS_ERR(local_root)) {
+               ret = PTR_ERR(local_root);
+               goto err;
+       }
+
+       ret = inode_item_info(inum, 0, local_root, swarn->path);
+       if (ret) {
+               btrfs_release_path(swarn->path);
+               goto err;
+       }
+
+       eb = swarn->path->nodes[0];
+       inode_item = btrfs_item_ptr(eb, swarn->path->slots[0],
+                                       struct btrfs_inode_item);
+       isize = btrfs_inode_size(eb, inode_item);
+       nlink = btrfs_inode_nlink(eb, inode_item);
+       btrfs_release_path(swarn->path);
+
+       ipath = init_ipath(4096, local_root, swarn->path);
+       ret = paths_from_inode(inum, ipath);
+
+       if (ret < 0)
+               goto err;
+
+       /*
+        * we deliberately ignore the bit ipath might have been too small to
+        * hold all of the paths here
+        */
+       for (i = 0; i < ipath->fspath->elem_cnt; ++i)
+               printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
+                       "%s, sector %llu, root %llu, inode %llu, offset %llu, "
+                       "length %llu, links %u (path: %s)\n", swarn->errstr,
+                       swarn->logical, swarn->dev->name,
+                       (unsigned long long)swarn->sector, root, inum, offset,
+                       min(isize - offset, (u64)PAGE_SIZE), nlink,
+                       ipath->fspath->str[i]);
+
+       free_ipath(ipath);
+       return 0;
+
+err:
+       printk(KERN_WARNING "btrfs: %s at logical %llu on dev "
+               "%s, sector %llu, root %llu, inode %llu, offset %llu: path "
+               "resolving failed with ret=%d\n", swarn->errstr,
+               swarn->logical, swarn->dev->name,
+               (unsigned long long)swarn->sector, root, inum, offset, ret);
+
+       free_ipath(ipath);
+       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;
+       u8 ref_level;
+       unsigned long ptr = 0;
+       const int bufsize = 4096;
+       u64 extent_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 = extent_from_logical(fs_info, swarn.logical, path, &found_key);
+       if (ret < 0)
+               goto out;
+
+       extent_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]);
+
+       if (ret & BTRFS_EXTENT_FLAG_TREE_BLOCK) {
+               do {
+                       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 %d) in tree "
+                               "%llu\n", errstr, swarn.logical, dev->name,
+                               (unsigned long long)swarn.sector,
+                               ref_level ? "node" : "leaf",
+                               ret < 0 ? -1 : ref_level,
+                               ret < 0 ? -1 : ref_root);
+               } while (ret != 1);
+       } else {
+               swarn.path = path;
+               iterate_extent_inodes(fs_info, path, found_key.objectid,
+                                       extent_offset,
+                                       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,
@@ -205,6 +357,8 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
 {
        struct scrub_dev *sdev = sbio->sdev;
        u64 sector = (sbio->physical + ix * PAGE_SIZE) >> 9;
+       static DEFINE_RATELIMIT_STATE(_rs, DEFAULT_RATELIMIT_INTERVAL,
+                                       DEFAULT_RATELIMIT_BURST);
 
        if (sbio->err) {
                if (scrub_fixup_io(READ, sbio->sdev->dev->bdev, sector,
@@ -212,6 +366,11 @@ static int scrub_recheck_error(struct scrub_bio *sbio, int ix)
                        if (scrub_fixup_check(sbio, ix) == 0)
                                return 0;
                }
+               if (__ratelimit(&_rs))
+                       scrub_print_warning("i/o error", sbio, ix);
+       } else {
+               if (__ratelimit(&_rs))
+                       scrub_print_warning("checksum error", sbio, ix);
        }
 
        spin_lock(&sdev->stat_lock);
@@ -326,9 +485,8 @@ static void scrub_fixup(struct scrub_bio *sbio, int ix)
        ++sdev->stat.corrected_errors;
        spin_unlock(&sdev->stat_lock);
 
-       if (printk_ratelimit())
-               printk(KERN_ERR "btrfs: fixed up at %llu\n",
-                      (unsigned long long)logical);
+       printk_ratelimited(KERN_ERR "btrfs: fixed up error at logical %llu\n",
+                              (unsigned long long)logical);
        return;
 
 uncorrectable:
@@ -337,9 +495,8 @@ uncorrectable:
        ++sdev->stat.uncorrectable_errors;
        spin_unlock(&sdev->stat_lock);
 
-       if (printk_ratelimit())
-               printk(KERN_ERR "btrfs: unable to fixup at %llu\n",
-                        (unsigned long long)logical);
+       printk_ratelimited(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,