btrfs: merge btrfs_read_dev_one_super() into btrfs_read_disk_super()
authorQu Wenruo <wqu@suse.com>
Mon, 28 Apr 2025 00:36:50 +0000 (10:06 +0930)
committerDavid Sterba <dsterba@suse.com>
Thu, 15 May 2025 12:30:50 +0000 (14:30 +0200)
We have two functions to read a super block from a block device:

- btrfs_read_dev_one_super()
  Exported from disk-io.c

- btrfs_read_disk_super()
  Local to volumes.c

And they have some minor differences:

- btrfs_read_dev_one_super() uses @copy_num
  Meanwhile btrfs_read_disk_super() relies on the physical and expected
  bytenr passed from the caller.

  The parameter list of btrfs_read_dev_one_super() is more user
  friendly.

- btrfs_read_disk_super() makes sure the label is NUL terminated

We do not need two different functions doing the same job, so merge the
behavior into btrfs_read_disk_super() by:

- Remove btrfs_read_dev_one_super()

- Export btrfs_read_disk_super()
  The name pairs with btrfs_release_disk_super() perfectly.

- Change the parameter list of btrfs_read_disk_super() to mimic
  btrfs_read_dev_one_super()
  All existing callers are calculating the physical address and expect
  bytenr before calling btrfs_read_disk_super() already.

Signed-off-by: Qu Wenruo <wqu@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/disk-io.c
fs/btrfs/disk-io.h
fs/btrfs/super.c
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index 1288b9bac47c46627e45b1bca4d0b0c024d92c8d..1768618d8a604eb09a616e62666b734368c4c0a1 100644 (file)
@@ -3712,57 +3712,6 @@ static void btrfs_end_super_write(struct bio *bio)
        bio_put(bio);
 }
 
-struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
-                                                  int copy_num, bool drop_cache)
-{
-       struct btrfs_super_block *super;
-       struct page *page;
-       u64 bytenr, bytenr_orig;
-       struct address_space *mapping = bdev->bd_mapping;
-       int ret;
-
-       bytenr_orig = btrfs_sb_offset(copy_num);
-       ret = btrfs_sb_log_location_bdev(bdev, copy_num, READ, &bytenr);
-       if (ret == -ENOENT)
-               return ERR_PTR(-EINVAL);
-       else if (ret)
-               return ERR_PTR(ret);
-
-       if (bytenr + BTRFS_SUPER_INFO_SIZE >= bdev_nr_bytes(bdev))
-               return ERR_PTR(-EINVAL);
-
-       if (drop_cache) {
-               /* This should only be called with the primary sb. */
-               ASSERT(copy_num == 0);
-
-               /*
-                * Drop the page of the primary superblock, so later read will
-                * always read from the device.
-                */
-               invalidate_inode_pages2_range(mapping,
-                               bytenr >> PAGE_SHIFT,
-                               (bytenr + BTRFS_SUPER_INFO_SIZE) >> PAGE_SHIFT);
-       }
-
-       page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS);
-       if (IS_ERR(page))
-               return ERR_CAST(page);
-
-       super = page_address(page);
-       if (btrfs_super_magic(super) != BTRFS_MAGIC) {
-               btrfs_release_disk_super(super);
-               return ERR_PTR(-ENODATA);
-       }
-
-       if (btrfs_super_bytenr(super) != bytenr_orig) {
-               btrfs_release_disk_super(super);
-               return ERR_PTR(-EINVAL);
-       }
-
-       return super;
-}
-
-
 struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev)
 {
        struct btrfs_super_block *super, *latest = NULL;
@@ -3775,7 +3724,7 @@ struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev)
         * later supers, using BTRFS_SUPER_MIRROR_MAX instead
         */
        for (i = 0; i < 1; i++) {
-               super = btrfs_read_dev_one_super(bdev, i, false);
+               super = btrfs_read_disk_super(bdev, i, false);
                if (IS_ERR(super))
                        continue;
 
index f87bbb7f8e7e29c6859831c4ce29fb78b05486af..0ac9baf1215fed6425e04a1a275726364e242856 100644 (file)
@@ -59,8 +59,6 @@ int btrfs_validate_super(const struct btrfs_fs_info *fs_info,
 int btrfs_check_features(struct btrfs_fs_info *fs_info, bool is_rw_mount);
 int write_all_supers(struct btrfs_fs_info *fs_info, int max_mirrors);
 struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev);
-struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
-                                                  int copy_num, bool drop_cache);
 int btrfs_commit_super(struct btrfs_fs_info *fs_info);
 struct btrfs_root *btrfs_read_tree_root(struct btrfs_root *tree_root,
                                        const struct btrfs_key *key);
index a8d2c2ca3120933e1b42422bb1615400372c4529..5221bfd1706a82939b393bd953113255f8a29a6e 100644 (file)
@@ -2296,7 +2296,7 @@ static int check_dev_super(struct btrfs_device *dev)
                return 0;
 
        /* Only need to check the primary super block. */
-       sb = btrfs_read_dev_one_super(dev->bdev, 0, true);
+       sb = btrfs_read_disk_super(dev->bdev, 0, true);
        if (IS_ERR(sb))
                return PTR_ERR(sb);
 
index 12260592d37fd400ae5bc373f8b68f546c1c214a..8478fb5ce44617b9346e27c98f9b367309358db2 100644 (file)
@@ -1325,48 +1325,58 @@ void btrfs_release_disk_super(struct btrfs_super_block *super)
        put_page(page);
 }
 
-static struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev,
-                                                      u64 bytenr, u64 bytenr_orig)
+struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev,
+                                               int copy_num, bool drop_cache)
 {
-       struct btrfs_super_block *disk_super;
+       struct btrfs_super_block *super;
        struct page *page;
-       void *p;
-       pgoff_t index;
+       u64 bytenr, bytenr_orig;
+       struct address_space *mapping = bdev->bd_mapping;
+       int ret;
 
-       /* make sure our super fits in the device */
-       if (bytenr + PAGE_SIZE >= bdev_nr_bytes(bdev))
-               return ERR_PTR(-EINVAL);
+       bytenr_orig = btrfs_sb_offset(copy_num);
+       ret = btrfs_sb_log_location_bdev(bdev, copy_num, READ, &bytenr);
+       if (ret < 0) {
+               if (ret == -ENOENT)
+                       ret = -EINVAL;
+               return ERR_PTR(ret);
+       }
 
-       /* make sure our super fits in the page */
-       if (sizeof(*disk_super) > PAGE_SIZE)
+       if (bytenr + BTRFS_SUPER_INFO_SIZE >= bdev_nr_bytes(bdev))
                return ERR_PTR(-EINVAL);
 
-       /* make sure our super doesn't straddle pages on disk */
-       index = bytenr >> PAGE_SHIFT;
-       if ((bytenr + sizeof(*disk_super) - 1) >> PAGE_SHIFT != index)
-               return ERR_PTR(-EINVAL);
+       if (drop_cache) {
+               /* This should only be called with the primary sb. */
+               ASSERT(copy_num == 0);
 
-       /* pull in the page with our super */
-       page = read_cache_page_gfp(bdev->bd_mapping, index, GFP_KERNEL);
+               /*
+                * Drop the page of the primary superblock, so later read will
+                * always read from the device.
+                */
+               invalidate_inode_pages2_range(mapping, bytenr >> PAGE_SHIFT,
+                                     (bytenr + BTRFS_SUPER_INFO_SIZE) >> PAGE_SHIFT);
+       }
 
+       page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS);
        if (IS_ERR(page))
                return ERR_CAST(page);
 
-       p = page_address(page);
-
-       /* align our pointer to the offset of the super block */
-       disk_super = p + offset_in_page(bytenr);
-
-       if (btrfs_super_bytenr(disk_super) != bytenr_orig ||
-           btrfs_super_magic(disk_super) != BTRFS_MAGIC) {
-               btrfs_release_disk_super(p);
+       super = page_address(page);
+       if (btrfs_super_magic(super) != BTRFS_MAGIC ||
+           btrfs_super_bytenr(super) != bytenr_orig) {
+               btrfs_release_disk_super(super);
                return ERR_PTR(-EINVAL);
        }
 
-       if (disk_super->label[0] && disk_super->label[BTRFS_LABEL_SIZE - 1])
-               disk_super->label[BTRFS_LABEL_SIZE - 1] = 0;
+       /*
+        * Make sure the last byte of label is properly NUL termiated.  We use
+        * '%s' to print the label, if not properly NUL termiated we can access
+        * beyond the label.
+        */
+       if (super->label[0] && super->label[BTRFS_LABEL_SIZE - 1])
+               super->label[BTRFS_LABEL_SIZE - 1] = 0;
 
-       return disk_super;
+       return super;
 }
 
 int btrfs_forget_devices(dev_t devt)
@@ -1437,9 +1447,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
        bool new_device_added = false;
        struct btrfs_device *device = NULL;
        struct file *bdev_file;
-       u64 bytenr;
        dev_t devt;
-       int ret;
 
        lockdep_assert_held(&uuid_mutex);
 
@@ -1457,20 +1465,7 @@ struct btrfs_device *btrfs_scan_one_device(const char *path, blk_mode_t flags,
        if (IS_ERR(bdev_file))
                return ERR_CAST(bdev_file);
 
-       /*
-        * We would like to check all the super blocks, but doing so would
-        * allow a mount to succeed after a mkfs from a different filesystem.
-        * Currently, recovery from a bad primary btrfs superblock is done
-        * using the userspace command 'btrfs check --super'.
-        */
-       ret = btrfs_sb_log_location_bdev(file_bdev(bdev_file), 0, READ, &bytenr);
-       if (ret) {
-               device = ERR_PTR(ret);
-               goto error_bdev_put;
-       }
-
-       disk_super = btrfs_read_disk_super(file_bdev(bdev_file), bytenr,
-                                          btrfs_sb_offset(0));
+       disk_super = btrfs_read_disk_super(file_bdev(bdev_file), 0, false);
        if (IS_ERR(disk_super)) {
                device = ERR_CAST(disk_super);
                goto error_bdev_put;
@@ -2136,7 +2131,7 @@ static void btrfs_scratch_superblock(struct btrfs_fs_info *fs_info,
        const u64 bytenr = btrfs_sb_offset(copy_num);
        int ret;
 
-       disk_super = btrfs_read_disk_super(bdev, bytenr, bytenr);
+       disk_super = btrfs_read_disk_super(bdev, copy_num, false);
        if (IS_ERR(disk_super))
                return;
 
index b043c285c409656e6d6eb247a41e3524ce509d10..a9ab1f6c9ed8700d422a9f0cda00470729cdfe1d 100644 (file)
@@ -785,6 +785,8 @@ struct btrfs_chunk_map *btrfs_find_chunk_map_nolock(struct btrfs_fs_info *fs_inf
 struct btrfs_chunk_map *btrfs_get_chunk_map(struct btrfs_fs_info *fs_info,
                                            u64 logical, u64 length);
 void btrfs_remove_chunk_map(struct btrfs_fs_info *fs_info, struct btrfs_chunk_map *map);
+struct btrfs_super_block *btrfs_read_disk_super(struct block_device *bdev,
+                                               int copy_num, bool drop_cache);
 void btrfs_release_disk_super(struct btrfs_super_block *super);
 
 static inline void btrfs_dev_stat_inc(struct btrfs_device *dev,