btrfs-progs: mkfs: add uuid and otime to ROOT_ITEM of, FS_TREE
[platform/upstream/btrfs-progs.git] / qgroup-verify.c
index 43ae11e..571b4d4 100644 (file)
 #include "disk-io.h"
 #include "print-tree.h"
 #include "utils.h"
-#include "ulist.h"
+#include "kernel-shared/ulist.h"
 #include "rbtree-utils.h"
+#include "transaction.h"
+#include "repair.h"
 
 #include "qgroup-verify.h"
 
@@ -66,6 +68,8 @@ struct qgroup_count {
        struct list_head members;
 
        u64 cur_refcnt;
+
+       struct list_head bad_list;
 };
 
 static struct counts_tree {
@@ -75,6 +79,8 @@ static struct counts_tree {
        unsigned int            qgroup_inconsist:1;
 } counts = { .root = RB_ROOT };
 
+static LIST_HEAD(bad_qgroups);
+
 static struct rb_root by_bytenr = RB_ROOT;
 
 /*
@@ -312,10 +318,11 @@ FREE_RB_BASED_TREE(ref, free_ref_node);
 /*
  * Resolves all the possible roots for the ref at parent.
  */
-static void find_parent_roots(struct ulist *roots, u64 parent)
+static int find_parent_roots(struct ulist *roots, u64 parent)
 {
        struct ref *ref;
        struct rb_node *node;
+       int ret;
 
        /*
         * Search the rbtree for the first ref with bytenr == parent.
@@ -323,9 +330,18 @@ static void find_parent_roots(struct ulist *roots, u64 parent)
         * For each unresolved root, we recurse
         */
        ref = find_ref_bytenr(parent);
+       if (!ref) {
+               error("bytenr ref not found for parent %llu",
+                               (unsigned long long)parent);
+               return -EIO;
+       }
        node = &ref->bytenr_node;
-       BUG_ON(ref == NULL);
-       BUG_ON(ref->bytenr != parent);
+       if (ref->bytenr != parent) {
+               error("found bytenr ref does not match parent: %llu != %llu",
+                               (unsigned long long)ref->bytenr,
+                               (unsigned long long)parent);
+               return -EIO;
+       }
 
        {
                /*
@@ -334,24 +350,44 @@ static void find_parent_roots(struct ulist *roots, u64 parent)
                 */
                struct rb_node *prev_node = rb_prev(&ref->bytenr_node);
                struct ref *prev;
+
                if (prev_node) {
                        prev = rb_entry(prev_node, struct ref, bytenr_node);
-                       BUG_ON(prev->bytenr == parent);
+                       if (prev->bytenr == parent) {
+                               error(
+                               "unexpected: prev bytenr same as parent: %llu",
+                                               (unsigned long long)parent);
+                               return -EIO;
+                       }
                }
        }
 
        do {
                if (ref->root) {
-                       if (is_fstree(ref->root))
-                               ulist_add(roots, ref->root, 0, 0);
+                       if (is_fstree(ref->root)) {
+                               ret = ulist_add(roots, ref->root, 0, 0);
+                               if (ret < 0)
+                                       goto out;
+                       }
+               } else if (ref->parent == ref->bytenr) {
+                       /*
+                        * Special loop case for tree reloc tree
+                        */
+                       ref->root = BTRFS_TREE_RELOC_OBJECTID;
                } else {
-                       find_parent_roots(roots, ref->parent);
+                       ret = find_parent_roots(roots, ref->parent);
+                       if (ret < 0)
+                               goto out;
                }
 
                node = rb_next(node);
                if (node)
                        ref = rb_entry(node, struct ref, bytenr_node);
        } while (node && ref->bytenr == parent);
+
+       ret = 0;
+out:
+       return ret;
 }
 
 static int account_one_extent(struct ulist *roots, u64 bytenr, u64 num_bytes)
@@ -482,6 +518,7 @@ static int account_all_refs(int do_qgroups, u64 search_subvol)
        struct rb_node *node;
        u64 bytenr, num_bytes;
        struct ulist *roots = ulist_alloc(0);
+       int ret;
 
        node = rb_first(&by_bytenr);
        while (node) {
@@ -505,7 +542,9 @@ static int account_all_refs(int do_qgroups, u64 search_subvol)
                                                goto enomem;
                                }
                        } else {
-                               find_parent_roots(roots, ref->parent);
+                               ret = find_parent_roots(roots, ref->parent);
+                               if (ret < 0)
+                                       goto enomem;
                        }
 
                        /*
@@ -544,6 +583,8 @@ static u64 resolve_one_root(u64 bytenr)
 
        if (ref->root)
                return ref->root;
+       if (ref->parent == bytenr)
+               return BTRFS_TREE_RELOC_OBJECTID;
        return resolve_one_root(ref->parent);
 }
 
@@ -670,7 +711,7 @@ static int travel_tree(struct btrfs_fs_info *info, struct btrfs_root *root,
 //     printf("travel_tree: bytenr: %llu\tnum_bytes: %llu\tref_parent: %llu\n",
 //            bytenr, num_bytes, ref_parent);
 
-       eb = read_tree_block(root, bytenr, num_bytes, 0);
+       eb = read_tree_block(info, bytenr, 0);
        if (!extent_buffer_uptodate(eb))
                return -EIO;
 
@@ -695,7 +736,7 @@ static int travel_tree(struct btrfs_fs_info *info, struct btrfs_root *root,
        nr = btrfs_header_nritems(eb);
        for (i = 0; i < nr; i++) {
                new_bytenr = btrfs_node_blockptr(eb, i);
-               new_num_bytes = root->nodesize;
+               new_num_bytes = info->nodesize;
 
                ret = travel_tree(info, root, new_bytenr, new_num_bytes,
                                  ref_parent);
@@ -714,6 +755,9 @@ static int add_refs_for_implied(struct btrfs_fs_info *info, u64 bytenr,
        struct btrfs_root *root;
        struct btrfs_key key;
 
+       /* Tree reloc tree doesn't contribute qgroup, skip it */
+       if (root_id == BTRFS_TREE_RELOC_OBJECTID)
+               return 0;
        key.objectid = root_id;
        key.type = BTRFS_ROOT_ITEM_KEY;
        key.offset = (u64)-1;
@@ -819,6 +863,7 @@ static struct qgroup_count *alloc_count(struct btrfs_disk_key *key,
                        btrfs_qgroup_info_exclusive_compressed(leaf, disk);
                INIT_LIST_HEAD(&c->groups);
                INIT_LIST_HEAD(&c->members);
+               INIT_LIST_HEAD(&c->bad_list);
 
                if (insert_count(c)) {
                        free(c);
@@ -854,15 +899,14 @@ static int add_qgroup_relation(u64 memberid, u64 parentid)
        return 0;
 }
 
-static void read_qgroup_status(struct btrfs_path *path,
+static void read_qgroup_status(struct extent_buffer *eb, int slot,
                              struct counts_tree *counts)
 {
        struct btrfs_qgroup_status_item *status_item;
        u64 flags;
 
-       status_item = btrfs_item_ptr(path->nodes[0], path->slots[0],
-                                    struct btrfs_qgroup_status_item);
-       flags = btrfs_qgroup_status_flags(path->nodes[0], status_item);
+       status_item = btrfs_item_ptr(eb, slot, struct btrfs_qgroup_status_item);
+       flags = btrfs_qgroup_status_flags(eb, status_item);
        /*
         * Since qgroup_inconsist/rescan_running is just one bit,
         * assign value directly won't work.
@@ -926,7 +970,7 @@ loop:
                        }
 
                        if (key.type == BTRFS_QGROUP_STATUS_KEY) {
-                               read_qgroup_status(&path, &counts);
+                               read_qgroup_status(leaf, i, &counts);
                                continue;
                        }
 
@@ -1141,7 +1185,7 @@ static int scan_extents(struct btrfs_fs_info *info,
                                bytenr = key.objectid;
                                num_bytes = key.offset;
                                if (key.type == BTRFS_METADATA_ITEM_KEY) {
-                                       num_bytes = info->extent_root->nodesize;
+                                       num_bytes = info->nodesize;
                                        meta = 1;
                                }
 
@@ -1250,34 +1294,36 @@ static int report_qgroup_difference(struct qgroup_count *count, int verbose)
                        print_fields_signed(excl_diff, excl_diff,
                                            "diff:", "exclusive");
        }
-       return (is_different && count->subvol_exists);
+
+       return is_different;
 }
 
-int report_qgroups(int all)
+void report_qgroups(int all)
 {
        struct rb_node *node;
        struct qgroup_count *c;
-       int ret = 0;
 
-       if (counts.rescan_running) {
+       if (!repair && counts.rescan_running) {
                if (all) {
                        printf(
-       "Qgroup rescan is running, qgroup counts difference is expected\n");
+       "Qgroup rescan is running, a difference in qgroup counts is expected\n");
                } else {
                        printf(
-       "Qgroup rescan is running, ignore qgroup check\n");
-                       return ret;
+       "Qgroup rescan is running, qgroups will not be printed.\n");
+                       return;
                }
        }
        if (counts.qgroup_inconsist && !counts.rescan_running)
-               fprintf(stderr, "Qgroup is already inconsistent before checking\n");
+               fprintf(stderr, "Qgroup are marked as inconsistent.\n");
        node = rb_first(&counts.root);
        while (node) {
                c = rb_entry(node, struct qgroup_count, rb_node);
-               ret |= report_qgroup_difference(c, all);
+
+               if (report_qgroup_difference(c, all))
+                       list_add_tail(&c->bad_list, &bad_qgroups);
+
                node = rb_next(node);
        }
-       return ret;
 }
 
 void free_qgroup_counts(void)
@@ -1290,6 +1336,8 @@ void free_qgroup_counts(void)
        while (node) {
                c = rb_entry(node, struct qgroup_count, rb_node);
 
+               list_del(&c->bad_list);
+
                list_for_each_entry_safe(glist, tmpglist, &c->groups,
                                         next_group) {
                        list_del(&glist->next_group);
@@ -1425,3 +1473,140 @@ out:
        return ret;
 }
 
+static int repair_qgroup_info(struct btrfs_fs_info *info,
+                             struct qgroup_count *count)
+{
+       int ret;
+       struct btrfs_root *root = info->quota_root;
+       struct btrfs_trans_handle *trans;
+       struct btrfs_path path;
+       struct btrfs_qgroup_info_item *info_item;
+       struct btrfs_key key;
+
+       printf("Repair qgroup %llu/%llu\n", btrfs_qgroup_level(count->qgroupid),
+              btrfs_qgroup_subvid(count->qgroupid));
+
+       trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
+
+       btrfs_init_path(&path);
+       key.objectid = 0;
+       key.type = BTRFS_QGROUP_INFO_KEY;
+       key.offset = count->qgroupid;
+       ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
+       if (ret) {
+               error("could not find disk item for qgroup %llu/%llu",
+                     btrfs_qgroup_level(count->qgroupid),
+                     btrfs_qgroup_subvid(count->qgroupid));
+               if (ret > 0)
+                       ret = -ENOENT;
+               goto out;
+       }
+
+       info_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
+                                  struct btrfs_qgroup_info_item);
+
+       btrfs_set_qgroup_info_generation(path.nodes[0], info_item,
+                                        trans->transid);
+
+       btrfs_set_qgroup_info_referenced(path.nodes[0], info_item,
+                                        count->info.referenced);
+       btrfs_set_qgroup_info_referenced_compressed(path.nodes[0], info_item,
+                                           count->info.referenced_compressed);
+
+       btrfs_set_qgroup_info_exclusive(path.nodes[0], info_item,
+                                       count->info.exclusive);
+       btrfs_set_qgroup_info_exclusive_compressed(path.nodes[0], info_item,
+                                          count->info.exclusive_compressed);
+
+       btrfs_mark_buffer_dirty(path.nodes[0]);
+
+out:
+       btrfs_commit_transaction(trans, root);
+       btrfs_release_path(&path);
+
+       return ret;
+}
+
+static int repair_qgroup_status(struct btrfs_fs_info *info)
+{
+       int ret;
+       struct btrfs_root *root = info->quota_root;
+       struct btrfs_trans_handle *trans;
+       struct btrfs_path path;
+       struct btrfs_key key;
+       struct btrfs_qgroup_status_item *status_item;
+
+       printf("Repair qgroup status item\n");
+
+       trans = btrfs_start_transaction(root, 1);
+       if (IS_ERR(trans))
+               return PTR_ERR(trans);
+
+       btrfs_init_path(&path);
+       key.objectid = 0;
+       key.type = BTRFS_QGROUP_STATUS_KEY;
+       key.offset = 0;
+       ret = btrfs_search_slot(trans, root, &key, &path, 0, 1);
+       if (ret) {
+               error("could not find qgroup status item");
+               if (ret > 0)
+                       ret = -ENOENT;
+               goto out;
+       }
+
+       status_item = btrfs_item_ptr(path.nodes[0], path.slots[0],
+                                    struct btrfs_qgroup_status_item);
+       btrfs_set_qgroup_status_flags(path.nodes[0], status_item,
+                                     BTRFS_QGROUP_STATUS_FLAG_ON);
+       btrfs_set_qgroup_status_rescan(path.nodes[0], status_item, 0);
+       btrfs_set_qgroup_status_generation(path.nodes[0], status_item,
+                                          trans->transid);
+
+       btrfs_mark_buffer_dirty(path.nodes[0]);
+
+out:
+       btrfs_commit_transaction(trans, root);
+       btrfs_release_path(&path);
+
+       return ret;
+}
+
+int repair_qgroups(struct btrfs_fs_info *info, int *repaired)
+{
+       int ret = 0;
+       struct qgroup_count *count, *tmpcount;
+
+       *repaired = 0;
+
+       if (!repair)
+               return 0;
+
+       list_for_each_entry_safe(count, tmpcount, &bad_qgroups, bad_list) {
+               ret = repair_qgroup_info(info, count);
+               if (ret) {
+                       goto out;
+               }
+
+               (*repaired)++;
+
+               list_del_init(&count->bad_list);
+       }
+
+       /*
+        * Do this step last as we want the latest transaction id on
+        * our qgroup status to avoid a (useless) warning after
+        * mount.
+        */
+       if (*repaired || counts.qgroup_inconsist || counts.rescan_running) {
+               ret = repair_qgroup_status(info);
+               if (ret)
+                       goto out;
+
+               (*repaired)++;
+       }
+
+out:
+       return ret;
+}