btrfs-progs: check: enable repair in lowmem mode
[platform/upstream/btrfs-progs.git] / cmds-check.c
index ab24aaa..89e12c1 100644 (file)
@@ -85,7 +85,6 @@ enum btrfs_check_mode {
 static enum btrfs_check_mode check_mode = CHECK_MODE_DEFAULT;
 
 struct extent_backref {
-       struct list_head list;
        struct rb_node node;
        unsigned int is_data:1;
        unsigned int found_extent_tree:1;
@@ -94,11 +93,6 @@ struct extent_backref {
        unsigned int broken:1;
 };
 
-static inline struct extent_backref* to_extent_backref(struct list_head *entry)
-{
-       return list_entry(entry, struct extent_backref, list);
-}
-
 static inline struct extent_backref* rb_node_to_extent_backref(struct rb_node *node)
 {
        return rb_entry(node, struct extent_backref, node);
@@ -5483,16 +5477,14 @@ static int do_check_fs_roots(struct btrfs_fs_info *fs_info,
 
 static int all_backpointers_checked(struct extent_record *rec, int print_errs)
 {
-       struct list_head *cur = rec->backrefs.next;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct tree_backref *tback;
        struct data_backref *dback;
        u64 found = 0;
        int err = 0;
 
-       while(cur != &rec->backrefs) {
-               back = to_extent_backref(cur);
-               cur = cur->next;
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (!back->found_extent_tree) {
                        err = 1;
                        if (!print_errs)
@@ -5595,17 +5587,16 @@ out:
        return err;
 }
 
-static int free_all_extent_backrefs(struct extent_record *rec)
+static void __free_one_backref(struct rb_node *node)
 {
-       struct extent_backref *back;
-       struct list_head *cur;
-       while (!list_empty(&rec->backrefs)) {
-               cur = rec->backrefs.next;
-               back = to_extent_backref(cur);
-               list_del(cur);
-               free(back);
-       }
-       return 0;
+       struct extent_backref *back = rb_node_to_extent_backref(node);
+
+       free(back);
+}
+
+static void free_all_extent_backrefs(struct extent_record *rec)
+{
+       rb_free_nodes(&rec->backref_tree, __free_one_backref);
 }
 
 static void free_extent_record_cache(struct cache_tree *extent_cache)
@@ -5644,7 +5635,7 @@ static int check_owner_ref(struct btrfs_root *root,
                            struct extent_record *rec,
                            struct extent_buffer *buf)
 {
-       struct extent_backref *node;
+       struct extent_backref *node, *tmp;
        struct tree_backref *back;
        struct btrfs_root *ref_root;
        struct btrfs_key key;
@@ -5654,7 +5645,8 @@ static int check_owner_ref(struct btrfs_root *root,
        int found = 0;
        int ret;
 
-       list_for_each_entry(node, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(node, tmp,
+                                            &rec->backref_tree, node) {
                if (node->is_data)
                        continue;
                if (!node->found_ref)
@@ -5699,14 +5691,12 @@ static int check_owner_ref(struct btrfs_root *root,
 
 static int is_extent_tree_record(struct extent_record *rec)
 {
-       struct list_head *cur = rec->backrefs.next;
-       struct extent_backref *node;
+       struct extent_backref *node, *tmp;
        struct tree_backref *back;
        int is_extent = 0;
 
-       while(cur != &rec->backrefs) {
-               node = to_extent_backref(cur);
-               cur = cur->next;
+       rbtree_postorder_for_each_entry_safe(node, tmp,
+                                            &rec->backref_tree, node) {
                if (node->is_data)
                        return 0;
                back = to_tree_backref(node);
@@ -6116,7 +6106,6 @@ static struct tree_backref *alloc_tree_backref(struct extent_record *rec,
                ref->root = root;
                ref->node.full_backref = 0;
        }
-       list_add_tail(&ref->node.list, &rec->backrefs);
 
        return ref;
 }
@@ -6186,7 +6175,6 @@ static struct data_backref *alloc_data_backref(struct extent_record *rec,
        ref->bytes = max_size;
        ref->found_ref = 0;
        ref->num_refs = 0;
-       list_add_tail(&ref->node.list, &rec->backrefs);
        if (max_size > rec->max_size)
                rec->max_size = max_size;
        return ref;
@@ -6219,12 +6207,12 @@ static void check_extent_type(struct extent_record *rec)
         * Check SYSTEM extent, as it's also marked as metadata, we can only
         * make sure it's a SYSTEM extent by its backref
         */
-       if (!list_empty(&rec->backrefs)) {
+       if (!RB_EMPTY_ROOT(&rec->backref_tree)) {
                struct extent_backref *node;
                struct tree_backref *tback;
                u64 bg_type;
 
-               node = to_extent_backref(rec->backrefs.next);
+               node = rb_node_to_extent_backref(rb_first(&rec->backref_tree));
                if (node->is_data) {
                        /* tree block shouldn't have data backref */
                        rec->wrong_chunk_type = 1;
@@ -8217,7 +8205,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
                        back->node.found_extent_tree = 0;
 
                if (!back->node.found_extent_tree && back->node.found_ref) {
-                       list_del(&back->node.list);
+                       rb_erase(&back->node.node, &rec->backref_tree);
                        free(back);
                }
        } else {
@@ -8236,7 +8224,7 @@ static int free_extent_hook(struct btrfs_trans_handle *trans,
                        back->node.found_extent_tree = 0;
                }
                if (!back->node.found_extent_tree && back->node.found_ref) {
-                       list_del(&back->node.list);
+                       rb_erase(&back->node.node, &rec->backref_tree);
                        free(back);
                }
        }
@@ -8677,7 +8665,7 @@ out:
 static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
                           struct extent_record *rec)
 {
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct extent_entry *entry, *best = NULL;
        LIST_HEAD(entries);
@@ -8693,7 +8681,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
        if (rec->metadata)
                return 0;
 
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data)
                        continue;
 
@@ -8819,7 +8808,8 @@ static int verify_backrefs(struct btrfs_fs_info *info, struct btrfs_path *path,
         * Ok great we all agreed on an extent record, let's go find the real
         * references and fix up the ones that don't match.
         */
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data)
                        continue;
 
@@ -9043,7 +9033,7 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
                                  struct extent_record *rec)
 {
        struct btrfs_root *root;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct cache_extent *cache;
        struct btrfs_file_extent_item *fi;
@@ -9051,7 +9041,8 @@ static int find_possible_backrefs(struct btrfs_fs_info *info,
        u64 bytenr, bytes;
        int ret;
 
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                /* Don't care about full backrefs (poor unloved backrefs) */
                if (back->full_backref || !back->is_data)
                        continue;
@@ -9139,7 +9130,7 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_key key;
        struct btrfs_root *dest_root;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        struct data_backref *dback;
        struct orphan_data_extent *orphan;
        struct btrfs_path path;
@@ -9149,7 +9140,8 @@ static int record_orphan_data_extents(struct btrfs_fs_info *fs_info,
        if (rec->metadata)
                return 1;
        btrfs_init_path(&path);
-       list_for_each_entry(back, &rec->backrefs, list) {
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                if (back->full_backref || !back->is_data ||
                    !back->found_extent_tree)
                        continue;
@@ -9217,9 +9209,8 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
        struct btrfs_trans_handle *trans = NULL;
        int ret;
        struct btrfs_path path;
-       struct list_head *cur = rec->backrefs.next;
        struct cache_extent *cache;
-       struct extent_backref *back;
+       struct extent_backref *back, *tmp;
        int allocated = 0;
        u64 flags = 0;
 
@@ -9267,10 +9258,8 @@ static int fixup_extent_refs(struct btrfs_fs_info *info,
        }
 
        /* step three, recreate all the refs we did find */
-       while(cur != &rec->backrefs) {
-               back = to_extent_backref(cur);
-               cur = cur->next;
-
+       rbtree_postorder_for_each_entry_safe(back, tmp,
+                                            &rec->backref_tree, node) {
                /*
                 * if we didn't find any references, don't create a
                 * new extent record
@@ -13083,12 +13072,10 @@ int cmd_check(int argc, char **argv)
        }
 
        /*
-        * Not supported yet
+        * experimental and dangerous
         */
-       if (repair && check_mode == CHECK_MODE_LOWMEM) {
-               error("low memory mode doesn't support repair yet");
-               exit(1);
-       }
+       if (repair && check_mode == CHECK_MODE_LOWMEM)
+               warning("low-memory mode repair support is only partial");
 
        radix_tree_init();
        cache_tree_init(&root_cache);
@@ -13123,6 +13110,8 @@ int cmd_check(int argc, char **argv)
                        warning(
                        "filesystem mounted, continuing because of --force");
                }
+               /* A block device is mounted in exclusive mode by kernel */
+               ctree_flags &= ~OPEN_CTREE_EXCLUSIVE;
        }
 
        /* only allow partial opening under repair mode */