btrfs: set the objectid of the btree inode's location key
[platform/kernel/linux-starfive.git] / fs / btrfs / disk-io.c
index 4ba005c..494e55e 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/fs.h>
 #include <linux/blkdev.h>
+#include <linux/radix-tree.h>
 #include <linux/writeback.h>
 #include <linux/workqueue.h>
 #include <linux/kthread.h>
@@ -50,7 +51,6 @@
                                 BTRFS_SUPER_FLAG_METADUMP |\
                                 BTRFS_SUPER_FLAG_METADUMP_V2)
 
-static void end_workqueue_fn(struct btrfs_work *work);
 static void btrfs_destroy_ordered_extents(struct btrfs_root *root);
 static int btrfs_destroy_delayed_refs(struct btrfs_transaction *trans,
                                      struct btrfs_fs_info *fs_info);
@@ -63,40 +63,6 @@ static int btrfs_destroy_pinned_extent(struct btrfs_fs_info *fs_info,
 static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info);
 static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info);
 
-/*
- * btrfs_end_io_wq structs are used to do processing in task context when an IO
- * is complete.  This is used during reads to verify checksums, and it is used
- * by writes to insert metadata for new file extents after IO is complete.
- */
-struct btrfs_end_io_wq {
-       struct bio *bio;
-       bio_end_io_t *end_io;
-       void *private;
-       struct btrfs_fs_info *info;
-       blk_status_t status;
-       enum btrfs_wq_endio_type metadata;
-       struct btrfs_work work;
-};
-
-static struct kmem_cache *btrfs_end_io_wq_cache;
-
-int __init btrfs_end_io_wq_init(void)
-{
-       btrfs_end_io_wq_cache = kmem_cache_create("btrfs_end_io_wq",
-                                       sizeof(struct btrfs_end_io_wq),
-                                       0,
-                                       SLAB_MEM_SPREAD,
-                                       NULL);
-       if (!btrfs_end_io_wq_cache)
-               return -ENOMEM;
-       return 0;
-}
-
-void __cold btrfs_end_io_wq_exit(void)
-{
-       kmem_cache_destroy(btrfs_end_io_wq_cache);
-}
-
 static void btrfs_free_csum_hash(struct btrfs_fs_info *fs_info)
 {
        if (fs_info->csum_shash)
@@ -255,8 +221,8 @@ static int verify_parent_transid(struct extent_io_tree *io_tree,
                goto out;
        }
        btrfs_err_rl(eb->fs_info,
-               "parent transid verify failed on %llu wanted %llu found %llu",
-                       eb->start,
+"parent transid verify failed on logical %llu mirror %u wanted %llu found %llu",
+                       eb->start, eb->read_mirror,
                        parent_transid, btrfs_header_generation(eb));
        ret = 1;
        clear_extent_buffer_uptodate(eb);
@@ -485,7 +451,7 @@ static int csum_dirty_subpage_buffers(struct btrfs_fs_info *fs_info,
                uptodate = btrfs_subpage_test_uptodate(fs_info, page, cur,
                                                       fs_info->nodesize);
 
-               /* A dirty eb shouldn't disappear from extent_buffers */
+               /* A dirty eb shouldn't disappear from buffer_radix */
                if (WARN_ON(!eb))
                        return -EUCLEAN;
 
@@ -586,21 +552,23 @@ static int validate_extent_buffer(struct extent_buffer *eb)
 
        found_start = btrfs_header_bytenr(eb);
        if (found_start != eb->start) {
-               btrfs_err_rl(fs_info, "bad tree block start, want %llu have %llu",
-                            eb->start, found_start);
+               btrfs_err_rl(fs_info,
+                       "bad tree block start, mirror %u want %llu have %llu",
+                            eb->read_mirror, eb->start, found_start);
                ret = -EIO;
                goto out;
        }
        if (check_tree_block_fsid(eb)) {
-               btrfs_err_rl(fs_info, "bad fsid on block %llu",
-                            eb->start);
+               btrfs_err_rl(fs_info, "bad fsid on logical %llu mirror %u",
+                            eb->start, eb->read_mirror);
                ret = -EIO;
                goto out;
        }
        found_level = btrfs_header_level(eb);
        if (found_level >= BTRFS_MAX_LEVEL) {
-               btrfs_err(fs_info, "bad tree block level %d on %llu",
-                         (int)btrfs_header_level(eb), eb->start);
+               btrfs_err(fs_info,
+                       "bad tree block level, mirror %u level %d on logical %llu",
+                       eb->read_mirror, btrfs_header_level(eb), eb->start);
                ret = -EIO;
                goto out;
        }
@@ -611,8 +579,8 @@ static int validate_extent_buffer(struct extent_buffer *eb)
 
        if (memcmp(result, header_csum, csum_size) != 0) {
                btrfs_warn_rl(fs_info,
-       "checksum verify failed on %llu wanted " CSUM_FMT " found " CSUM_FMT " level %d",
-                             eb->start,
+"checksum verify failed on logical %llu mirror %u wanted " CSUM_FMT " found " CSUM_FMT " level %d",
+                             eb->start, eb->read_mirror,
                              CSUM_FMT_VALUE(csum_size, header_csum),
                              CSUM_FMT_VALUE(csum_size, result),
                              btrfs_header_level(eb));
@@ -637,8 +605,8 @@ static int validate_extent_buffer(struct extent_buffer *eb)
                set_extent_buffer_uptodate(eb);
        else
                btrfs_err(fs_info,
-                         "block=%llu read time tree block corruption detected",
-                         eb->start);
+               "read time tree block corruption detected on logical %llu mirror %u",
+                         eb->start, eb->read_mirror);
 out:
        return ret;
 }
@@ -739,58 +707,6 @@ err:
        return ret;
 }
 
-static void end_workqueue_bio(struct bio *bio)
-{
-       struct btrfs_end_io_wq *end_io_wq = bio->bi_private;
-       struct btrfs_fs_info *fs_info;
-       struct btrfs_workqueue *wq;
-
-       fs_info = end_io_wq->info;
-       end_io_wq->status = bio->bi_status;
-
-       if (btrfs_op(bio) == BTRFS_MAP_WRITE) {
-               if (end_io_wq->metadata == BTRFS_WQ_ENDIO_METADATA)
-                       wq = fs_info->endio_meta_write_workers;
-               else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_FREE_SPACE)
-                       wq = fs_info->endio_freespace_worker;
-               else if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
-                       wq = fs_info->endio_raid56_workers;
-               else
-                       wq = fs_info->endio_write_workers;
-       } else {
-               if (end_io_wq->metadata == BTRFS_WQ_ENDIO_RAID56)
-                       wq = fs_info->endio_raid56_workers;
-               else if (end_io_wq->metadata)
-                       wq = fs_info->endio_meta_workers;
-               else
-                       wq = fs_info->endio_workers;
-       }
-
-       btrfs_init_work(&end_io_wq->work, end_workqueue_fn, NULL, NULL);
-       btrfs_queue_work(wq, &end_io_wq->work);
-}
-
-blk_status_t btrfs_bio_wq_end_io(struct btrfs_fs_info *info, struct bio *bio,
-                       enum btrfs_wq_endio_type metadata)
-{
-       struct btrfs_end_io_wq *end_io_wq;
-
-       end_io_wq = kmem_cache_alloc(btrfs_end_io_wq_cache, GFP_NOFS);
-       if (!end_io_wq)
-               return BLK_STS_RESOURCE;
-
-       end_io_wq->private = bio->bi_private;
-       end_io_wq->end_io = bio->bi_end_io;
-       end_io_wq->info = info;
-       end_io_wq->status = 0;
-       end_io_wq->bio = bio;
-       end_io_wq->metadata = metadata;
-
-       bio->bi_private = end_io_wq;
-       bio->bi_end_io = end_workqueue_bio;
-       return 0;
-}
-
 static void run_one_async_start(struct btrfs_work *work)
 {
        struct async_submit_bio *async;
@@ -815,7 +731,6 @@ static void run_one_async_done(struct btrfs_work *work)
 {
        struct async_submit_bio *async;
        struct inode *inode;
-       blk_status_t ret;
 
        async = container_of(work, struct  async_submit_bio, work);
        inode = async->inode;
@@ -833,11 +748,7 @@ static void run_one_async_done(struct btrfs_work *work)
         * This changes nothing when cgroups aren't in use.
         */
        async->bio->bi_opf |= REQ_CGROUP_PUNT;
-       ret = btrfs_map_bio(btrfs_sb(inode->i_sb), async->bio, async->mirror_num);
-       if (ret) {
-               async->bio->bi_status = ret;
-               bio_endio(async->bio);
-       }
+       btrfs_submit_bio(btrfs_sb(inode->i_sb), async->bio, async->mirror_num);
 }
 
 static void run_one_async_free(struct btrfs_work *work)
@@ -848,16 +759,23 @@ static void run_one_async_free(struct btrfs_work *work)
        kfree(async);
 }
 
-blk_status_t btrfs_wq_submit_bio(struct inode *inode, struct bio *bio,
-                                int mirror_num, u64 dio_file_offset,
-                                extent_submit_bio_start_t *submit_bio_start)
+/*
+ * Submit bio to an async queue.
+ *
+ * Retrun:
+ * - true if the work has been succesfuly submitted
+ * - false in case of error
+ */
+bool btrfs_wq_submit_bio(struct inode *inode, struct bio *bio, int mirror_num,
+                        u64 dio_file_offset,
+                        extent_submit_bio_start_t *submit_bio_start)
 {
        struct btrfs_fs_info *fs_info = BTRFS_I(inode)->root->fs_info;
        struct async_submit_bio *async;
 
        async = kmalloc(sizeof(*async), GFP_NOFS);
        if (!async)
-               return BLK_STS_RESOURCE;
+               return false;
 
        async->inode = inode;
        async->bio = bio;
@@ -875,7 +793,7 @@ blk_status_t btrfs_wq_submit_bio(struct inode *inode, struct bio *bio,
                btrfs_queue_work(fs_info->hipri_workers, &async->work);
        else
                btrfs_queue_work(fs_info->workers, &async->work);
-       return 0;
+       return true;
 }
 
 static blk_status_t btree_csum_one_bio(struct bio *bio)
@@ -901,7 +819,7 @@ static blk_status_t btree_submit_bio_start(struct inode *inode, struct bio *bio,
 {
        /*
         * when we're called for a write, we're already in the async
-        * submission context.  Just jump into btrfs_map_bio
+        * submission context.  Just jump into btrfs_submit_bio.
         */
        return btree_csum_one_bio(bio);
 }
@@ -923,32 +841,29 @@ void btrfs_submit_metadata_bio(struct inode *inode, struct bio *bio, int mirror_
        struct btrfs_fs_info *fs_info = btrfs_sb(inode->i_sb);
        blk_status_t ret;
 
+       bio->bi_opf |= REQ_META;
+
        if (btrfs_op(bio) != BTRFS_MAP_WRITE) {
-               /*
-                * called for a read, do the setup so that checksum validation
-                * can happen in the async kernel threads
-                */
-               ret = btrfs_bio_wq_end_io(fs_info, bio,
-                                         BTRFS_WQ_ENDIO_METADATA);
-               if (!ret)
-                       ret = btrfs_map_bio(fs_info, bio, mirror_num);
-       } else if (!should_async_write(fs_info, BTRFS_I(inode))) {
-               ret = btree_csum_one_bio(bio);
-               if (!ret)
-                       ret = btrfs_map_bio(fs_info, bio, mirror_num);
-       } else {
-               /*
-                * kthread helpers are used to submit writes so that
-                * checksumming can happen in parallel across all CPUs
-                */
-               ret = btrfs_wq_submit_bio(inode, bio, mirror_num, 0,
-                                         btree_submit_bio_start);
+               btrfs_submit_bio(fs_info, bio, mirror_num);
+               return;
        }
 
+       /*
+        * Kthread helpers are used to submit writes so that checksumming can
+        * happen in parallel across all CPUs.
+        */
+       if (should_async_write(fs_info, BTRFS_I(inode)) &&
+           btrfs_wq_submit_bio(inode, bio, mirror_num, 0, btree_submit_bio_start))
+               return;
+
+       ret = btree_csum_one_bio(bio);
        if (ret) {
                bio->bi_status = ret;
                bio_endio(bio);
+               return;
        }
+
+       btrfs_submit_bio(fs_info, bio, mirror_num);
 }
 
 #ifdef CONFIG_MIGRATION
@@ -1158,7 +1073,7 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
        root->nr_delalloc_inodes = 0;
        root->nr_ordered_extents = 0;
        root->inode_tree = RB_ROOT;
-       xa_init_flags(&root->delayed_nodes, GFP_ATOMIC);
+       INIT_RADIX_TREE(&root->delayed_nodes_tree, GFP_ATOMIC);
 
        btrfs_init_root_block_rsv(root);
 
@@ -1210,9 +1125,9 @@ static void __setup_root(struct btrfs_root *root, struct btrfs_fs_info *fs_info,
        btrfs_qgroup_init_swapped_blocks(&root->swapped_blocks);
 #ifdef CONFIG_BTRFS_DEBUG
        INIT_LIST_HEAD(&root->leak_list);
-       spin_lock(&fs_info->fs_roots_lock);
+       spin_lock(&fs_info->fs_roots_radix_lock);
        list_add_tail(&root->leak_list, &fs_info->allocated_roots);
-       spin_unlock(&fs_info->fs_roots_lock);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
 #endif
 }
 
@@ -1659,11 +1574,12 @@ static struct btrfs_root *btrfs_lookup_fs_root(struct btrfs_fs_info *fs_info,
 {
        struct btrfs_root *root;
 
-       spin_lock(&fs_info->fs_roots_lock);
-       root = xa_load(&fs_info->fs_roots, (unsigned long)root_id);
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       root = radix_tree_lookup(&fs_info->fs_roots_radix,
+                                (unsigned long)root_id);
        if (root)
                root = btrfs_grab_root(root);
-       spin_unlock(&fs_info->fs_roots_lock);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
        return root;
 }
 
@@ -1705,14 +1621,20 @@ int btrfs_insert_fs_root(struct btrfs_fs_info *fs_info,
 {
        int ret;
 
-       spin_lock(&fs_info->fs_roots_lock);
-       ret = xa_insert(&fs_info->fs_roots, (unsigned long)root->root_key.objectid,
-                       root, GFP_NOFS);
+       ret = radix_tree_preload(GFP_NOFS);
+       if (ret)
+               return ret;
+
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       ret = radix_tree_insert(&fs_info->fs_roots_radix,
+                               (unsigned long)root->root_key.objectid,
+                               root);
        if (ret == 0) {
                btrfs_grab_root(root);
-               set_bit(BTRFS_ROOT_REGISTERED, &root->state);
+               set_bit(BTRFS_ROOT_IN_RADIX, &root->state);
        }
-       spin_unlock(&fs_info->fs_roots_lock);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
+       radix_tree_preload_end();
 
        return ret;
 }
@@ -1864,7 +1786,7 @@ again:
 fail:
        /*
         * If our caller provided us an anonymous device, then it's his
-        * responsability to free it in case we fail. So we have to set our
+        * responsibility to free it in case we fail. So we have to set our
         * root's anon_dev to 0 to avoid a double free, once by btrfs_put_root()
         * and once again by our caller.
         */
@@ -1947,25 +1869,6 @@ struct btrfs_root *btrfs_get_fs_root_commit_root(struct btrfs_fs_info *fs_info,
        return root;
 }
 
-/*
- * called by the kthread helper functions to finally call the bio end_io
- * functions.  This is where read checksum verification actually happens
- */
-static void end_workqueue_fn(struct btrfs_work *work)
-{
-       struct bio *bio;
-       struct btrfs_end_io_wq *end_io_wq;
-
-       end_io_wq = container_of(work, struct btrfs_end_io_wq, work);
-       bio = end_io_wq->bio;
-
-       bio->bi_status = end_io_wq->status;
-       bio->bi_private = end_io_wq->private;
-       bio->bi_end_io = end_io_wq->end_io;
-       bio_endio(bio);
-       kmem_cache_free(btrfs_end_io_wq_cache, end_io_wq);
-}
-
 static int cleaner_kthread(void *arg)
 {
        struct btrfs_fs_info *fs_info = arg;
@@ -2272,10 +2175,14 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
        btrfs_destroy_workqueue(fs_info->delalloc_workers);
        btrfs_destroy_workqueue(fs_info->hipri_workers);
        btrfs_destroy_workqueue(fs_info->workers);
-       btrfs_destroy_workqueue(fs_info->endio_workers);
-       btrfs_destroy_workqueue(fs_info->endio_raid56_workers);
+       if (fs_info->endio_workers)
+               destroy_workqueue(fs_info->endio_workers);
+       if (fs_info->endio_raid56_workers)
+               destroy_workqueue(fs_info->endio_raid56_workers);
        if (fs_info->rmw_workers)
                destroy_workqueue(fs_info->rmw_workers);
+       if (fs_info->compressed_write_workers)
+               destroy_workqueue(fs_info->compressed_write_workers);
        btrfs_destroy_workqueue(fs_info->endio_write_workers);
        btrfs_destroy_workqueue(fs_info->endio_freespace_worker);
        btrfs_destroy_workqueue(fs_info->delayed_workers);
@@ -2289,8 +2196,8 @@ static void btrfs_stop_all_workers(struct btrfs_fs_info *fs_info)
         * the queues used for metadata I/O, since tasks from those other work
         * queues can do metadata I/O operations.
         */
-       btrfs_destroy_workqueue(fs_info->endio_meta_workers);
-       btrfs_destroy_workqueue(fs_info->endio_meta_write_workers);
+       if (fs_info->endio_meta_workers)
+               destroy_workqueue(fs_info->endio_meta_workers);
 }
 
 static void free_root_extent_buffers(struct btrfs_root *root)
@@ -2342,9 +2249,9 @@ void btrfs_put_root(struct btrfs_root *root)
                btrfs_drew_lock_destroy(&root->snapshot_lock);
                free_root_extent_buffers(root);
 #ifdef CONFIG_BTRFS_DEBUG
-               spin_lock(&root->fs_info->fs_roots_lock);
+               spin_lock(&root->fs_info->fs_roots_radix_lock);
                list_del_init(&root->leak_list);
-               spin_unlock(&root->fs_info->fs_roots_lock);
+               spin_unlock(&root->fs_info->fs_roots_radix_lock);
 #endif
                kfree(root);
        }
@@ -2352,21 +2259,28 @@ void btrfs_put_root(struct btrfs_root *root)
 
 void btrfs_free_fs_roots(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_root *root;
-       unsigned long index = 0;
+       int ret;
+       struct btrfs_root *gang[8];
+       int i;
 
        while (!list_empty(&fs_info->dead_roots)) {
-               root = list_entry(fs_info->dead_roots.next,
-                                 struct btrfs_root, root_list);
-               list_del(&root->root_list);
+               gang[0] = list_entry(fs_info->dead_roots.next,
+                                    struct btrfs_root, root_list);
+               list_del(&gang[0]->root_list);
 
-               if (test_bit(BTRFS_ROOT_REGISTERED, &root->state))
-                       btrfs_drop_and_free_fs_root(fs_info, root);
-               btrfs_put_root(root);
+               if (test_bit(BTRFS_ROOT_IN_RADIX, &gang[0]->state))
+                       btrfs_drop_and_free_fs_root(fs_info, gang[0]);
+               btrfs_put_root(gang[0]);
        }
 
-       xa_for_each(&fs_info->fs_roots, index, root) {
-               btrfs_drop_and_free_fs_root(fs_info, root);
+       while (1) {
+               ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
+                                            (void **)gang, 0,
+                                            ARRAY_SIZE(gang));
+               if (!ret)
+                       break;
+               for (i = 0; i < ret; i++)
+                       btrfs_drop_and_free_fs_root(fs_info, gang[i]);
        }
 }
 
@@ -2413,7 +2327,9 @@ static void btrfs_init_btree_inode(struct btrfs_fs_info *fs_info)
        extent_map_tree_init(&BTRFS_I(inode)->extent_tree);
 
        BTRFS_I(inode)->root = btrfs_grab_root(fs_info->tree_root);
-       memset(&BTRFS_I(inode)->location, 0, sizeof(struct btrfs_key));
+       BTRFS_I(inode)->location.objectid = BTRFS_BTREE_INODE_OBJECTID;
+       BTRFS_I(inode)->location.type = 0;
+       BTRFS_I(inode)->location.offset = 0;
        set_bit(BTRFS_INODE_DUMMY, &BTRFS_I(inode)->runtime_flags);
        btrfs_insert_inode_hash(inode);
 }
@@ -2462,25 +2378,18 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
        fs_info->fixup_workers =
                btrfs_alloc_workqueue(fs_info, "fixup", flags, 1, 0);
 
-       /*
-        * endios are largely parallel and should have a very
-        * low idle thresh
-        */
        fs_info->endio_workers =
-               btrfs_alloc_workqueue(fs_info, "endio", flags, max_active, 4);
+               alloc_workqueue("btrfs-endio", flags, max_active);
        fs_info->endio_meta_workers =
-               btrfs_alloc_workqueue(fs_info, "endio-meta", flags,
-                                     max_active, 4);
-       fs_info->endio_meta_write_workers =
-               btrfs_alloc_workqueue(fs_info, "endio-meta-write", flags,
-                                     max_active, 2);
+               alloc_workqueue("btrfs-endio-meta", flags, max_active);
        fs_info->endio_raid56_workers =
-               btrfs_alloc_workqueue(fs_info, "endio-raid56", flags,
-                                     max_active, 4);
+               alloc_workqueue("btrfs-endio-raid56", flags, max_active);
        fs_info->rmw_workers = alloc_workqueue("btrfs-rmw", flags, max_active);
        fs_info->endio_write_workers =
                btrfs_alloc_workqueue(fs_info, "endio-write", flags,
                                      max_active, 2);
+       fs_info->compressed_write_workers =
+               alloc_workqueue("btrfs-compressed-write", flags, max_active);
        fs_info->endio_freespace_worker =
                btrfs_alloc_workqueue(fs_info, "freespace-write", flags,
                                      max_active, 0);
@@ -2495,7 +2404,7 @@ static int btrfs_init_workqueues(struct btrfs_fs_info *fs_info)
        if (!(fs_info->workers && fs_info->hipri_workers &&
              fs_info->delalloc_workers && fs_info->flush_workers &&
              fs_info->endio_workers && fs_info->endio_meta_workers &&
-             fs_info->endio_meta_write_workers &&
+             fs_info->compressed_write_workers &&
              fs_info->endio_write_workers && fs_info->endio_raid56_workers &&
              fs_info->endio_freespace_worker && fs_info->rmw_workers &&
              fs_info->caching_workers && fs_info->fixup_workers &&
@@ -2522,6 +2431,9 @@ static int btrfs_init_csum_hash(struct btrfs_fs_info *fs_info, u16 csum_type)
 
        fs_info->csum_shash = csum_shash;
 
+       btrfs_info(fs_info, "using %s (%s) checksum algorithm",
+                       btrfs_super_csum_name(csum_type),
+                       crypto_shash_driver_name(csum_shash));
        return 0;
 }
 
@@ -3134,8 +3046,8 @@ static int __cold init_tree_roots(struct btrfs_fs_info *fs_info)
 
 void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
 {
-       xa_init_flags(&fs_info->fs_roots, GFP_ATOMIC);
-       xa_init_flags(&fs_info->extent_buffers, GFP_ATOMIC);
+       INIT_RADIX_TREE(&fs_info->fs_roots_radix, GFP_ATOMIC);
+       INIT_RADIX_TREE(&fs_info->buffer_radix, GFP_ATOMIC);
        INIT_LIST_HEAD(&fs_info->trans_list);
        INIT_LIST_HEAD(&fs_info->dead_roots);
        INIT_LIST_HEAD(&fs_info->delayed_iputs);
@@ -3143,7 +3055,7 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info)
        INIT_LIST_HEAD(&fs_info->caching_block_groups);
        spin_lock_init(&fs_info->delalloc_root_lock);
        spin_lock_init(&fs_info->trans_lock);
-       spin_lock_init(&fs_info->fs_roots_lock);
+       spin_lock_init(&fs_info->fs_roots_radix_lock);
        spin_lock_init(&fs_info->delayed_iput_lock);
        spin_lock_init(&fs_info->defrag_inodes_lock);
        spin_lock_init(&fs_info->super_lock);
@@ -3374,7 +3286,7 @@ int btrfs_start_pre_rw_mount(struct btrfs_fs_info *fs_info)
        /*
         * btrfs_find_orphan_roots() is responsible for finding all the dead
         * roots (with 0 refs), flag them with BTRFS_ROOT_DEAD_TREE and load
-        * them into the fs_info->fs_roots. This must be done before
+        * them into the fs_info->fs_roots_radix tree. This must be done before
         * calling btrfs_orphan_cleanup() on the tree root. If we don't do it
         * first, then btrfs_orphan_cleanup() will delete a dead root's orphan
         * item before the root's tree is deleted - this means that if we unmount
@@ -3578,16 +3490,6 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
         */
        fs_info->compress_type = BTRFS_COMPRESS_ZLIB;
 
-       /*
-        * Flag our filesystem as having big metadata blocks if they are bigger
-        * than the page size.
-        */
-       if (btrfs_super_nodesize(disk_super) > PAGE_SIZE) {
-               if (!(features & BTRFS_FEATURE_INCOMPAT_BIG_METADATA))
-                       btrfs_info(fs_info,
-                               "flagging fs with big metadata feature");
-               features |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
-       }
 
        /* Set up fs_info before parsing mount options */
        nodesize = btrfs_super_nodesize(disk_super);
@@ -3625,8 +3527,12 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
        else if (fs_info->compress_type == BTRFS_COMPRESS_ZSTD)
                features |= BTRFS_FEATURE_INCOMPAT_COMPRESS_ZSTD;
 
-       if (features & BTRFS_FEATURE_INCOMPAT_SKINNY_METADATA)
-               btrfs_info(fs_info, "has skinny extents");
+       /*
+        * Flag our filesystem as having big metadata blocks if they are bigger
+        * than the page size.
+        */
+       if (btrfs_super_nodesize(disk_super) > PAGE_SIZE)
+               features |= BTRFS_FEATURE_INCOMPAT_BIG_METADATA;
 
        /*
         * mixed block groups end up with duplicate but slightly offset
@@ -3655,6 +3561,20 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
                err = -EINVAL;
                goto fail_alloc;
        }
+       /*
+        * We have unsupported RO compat features, although RO mounted, we
+        * should not cause any metadata write, including log replay.
+        * Or we could screw up whatever the new feature requires.
+        */
+       if (unlikely(features && btrfs_super_log_root(disk_super) &&
+                    !btrfs_test_opt(fs_info, NOLOGREPLAY))) {
+               btrfs_err(fs_info,
+"cannot replay dirty log with unsupported compat_ro features (0x%llx), try rescue=nologreplay",
+                         features);
+               err = -EINVAL;
+               goto fail_alloc;
+       }
+
 
        if (sectorsize < PAGE_SIZE) {
                struct btrfs_subpage_info *subpage_info;
@@ -4499,11 +4419,12 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
 {
        bool drop_ref = false;
 
-       spin_lock(&fs_info->fs_roots_lock);
-       xa_erase(&fs_info->fs_roots, (unsigned long)root->root_key.objectid);
-       if (test_and_clear_bit(BTRFS_ROOT_REGISTERED, &root->state))
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       radix_tree_delete(&fs_info->fs_roots_radix,
+                         (unsigned long)root->root_key.objectid);
+       if (test_and_clear_bit(BTRFS_ROOT_IN_RADIX, &root->state))
                drop_ref = true;
-       spin_unlock(&fs_info->fs_roots_lock);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
 
        if (BTRFS_FS_ERROR(fs_info)) {
                ASSERT(root->log_root == NULL);
@@ -4519,48 +4440,50 @@ void btrfs_drop_and_free_fs_root(struct btrfs_fs_info *fs_info,
 
 int btrfs_cleanup_fs_roots(struct btrfs_fs_info *fs_info)
 {
-       struct btrfs_root *roots[8];
-       unsigned long index = 0;
-       int i;
+       u64 root_objectid = 0;
+       struct btrfs_root *gang[8];
+       int i = 0;
        int err = 0;
-       int grabbed;
+       unsigned int ret = 0;
 
        while (1) {
-               struct btrfs_root *root;
-
-               spin_lock(&fs_info->fs_roots_lock);
-               if (!xa_find(&fs_info->fs_roots, &index, ULONG_MAX, XA_PRESENT)) {
-                       spin_unlock(&fs_info->fs_roots_lock);
-                       return err;
+               spin_lock(&fs_info->fs_roots_radix_lock);
+               ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
+                                            (void **)gang, root_objectid,
+                                            ARRAY_SIZE(gang));
+               if (!ret) {
+                       spin_unlock(&fs_info->fs_roots_radix_lock);
+                       break;
                }
+               root_objectid = gang[ret - 1]->root_key.objectid + 1;
 
-               grabbed = 0;
-               xa_for_each_start(&fs_info->fs_roots, index, root, index) {
-                       /* Avoid grabbing roots in dead_roots */
-                       if (btrfs_root_refs(&root->root_item) > 0)
-                               roots[grabbed++] = btrfs_grab_root(root);
-                       if (grabbed >= ARRAY_SIZE(roots))
-                               break;
+               for (i = 0; i < ret; i++) {
+                       /* Avoid to grab roots in dead_roots */
+                       if (btrfs_root_refs(&gang[i]->root_item) == 0) {
+                               gang[i] = NULL;
+                               continue;
+                       }
+                       /* grab all the search result for later use */
+                       gang[i] = btrfs_grab_root(gang[i]);
                }
-               spin_unlock(&fs_info->fs_roots_lock);
+               spin_unlock(&fs_info->fs_roots_radix_lock);
 
-               for (i = 0; i < grabbed; i++) {
-                       if (!roots[i])
+               for (i = 0; i < ret; i++) {
+                       if (!gang[i])
                                continue;
-                       index = roots[i]->root_key.objectid;
-                       err = btrfs_orphan_cleanup(roots[i]);
+                       root_objectid = gang[i]->root_key.objectid;
+                       err = btrfs_orphan_cleanup(gang[i]);
                        if (err)
-                               goto out;
-                       btrfs_put_root(roots[i]);
+                               break;
+                       btrfs_put_root(gang[i]);
                }
-               index++;
+               root_objectid++;
        }
 
-out:
-       /* Release the roots that remain uncleaned due to error */
-       for (; i < grabbed; i++) {
-               if (roots[i])
-                       btrfs_put_root(roots[i]);
+       /* release the uncleaned roots due to error */
+       for (; i < ret; i++) {
+               if (gang[i])
+                       btrfs_put_root(gang[i]);
        }
        return err;
 }
@@ -4879,28 +4802,31 @@ static void btrfs_error_commit_super(struct btrfs_fs_info *fs_info)
 
 static void btrfs_drop_all_logs(struct btrfs_fs_info *fs_info)
 {
-       unsigned long index = 0;
-       int grabbed = 0;
-       struct btrfs_root *roots[8];
+       struct btrfs_root *gang[8];
+       u64 root_objectid = 0;
+       int ret;
+
+       spin_lock(&fs_info->fs_roots_radix_lock);
+       while ((ret = radix_tree_gang_lookup(&fs_info->fs_roots_radix,
+                                            (void **)gang, root_objectid,
+                                            ARRAY_SIZE(gang))) != 0) {
+               int i;
 
-       spin_lock(&fs_info->fs_roots_lock);
-       while ((grabbed = xa_extract(&fs_info->fs_roots, (void **)roots, index,
-                                    ULONG_MAX, 8, XA_PRESENT))) {
-               for (int i = 0; i < grabbed; i++)
-                       roots[i] = btrfs_grab_root(roots[i]);
-               spin_unlock(&fs_info->fs_roots_lock);
+               for (i = 0; i < ret; i++)
+                       gang[i] = btrfs_grab_root(gang[i]);
+               spin_unlock(&fs_info->fs_roots_radix_lock);
 
-               for (int i = 0; i < grabbed; i++) {
-                       if (!roots[i])
+               for (i = 0; i < ret; i++) {
+                       if (!gang[i])
                                continue;
-                       index = roots[i]->root_key.objectid;
-                       btrfs_free_log(NULL, roots[i]);
-                       btrfs_put_root(roots[i]);
+                       root_objectid = gang[i]->root_key.objectid;
+                       btrfs_free_log(NULL, gang[i]);
+                       btrfs_put_root(gang[i]);
                }
-               index++;
-               spin_lock(&fs_info->fs_roots_lock);
+               root_objectid++;
+               spin_lock(&fs_info->fs_roots_radix_lock);
        }
-       spin_unlock(&fs_info->fs_roots_lock);
+       spin_unlock(&fs_info->fs_roots_radix_lock);
        btrfs_free_log_root_tree(NULL, fs_info);
 }