u64 features;
        u16 csum_type;
        struct btrfs_key location;
-       struct buffer_head *bh;
        struct btrfs_super_block *disk_super;
        struct btrfs_fs_info *fs_info = btrfs_sb(sb);
        struct btrfs_root *tree_root;
        /*
         * Read super block and check the signature bytes only
         */
-       bh = btrfs_read_dev_super(fs_devices->latest_bdev);
-       if (IS_ERR(bh)) {
-               err = PTR_ERR(bh);
+       disk_super = btrfs_read_dev_super(fs_devices->latest_bdev);
+       if (IS_ERR(disk_super)) {
+               err = PTR_ERR(disk_super);
                goto fail_alloc;
        }
 
         * Verify the type first, if that or the the checksum value are
         * corrupted, we'll find out
         */
-       csum_type = btrfs_super_csum_type((struct btrfs_super_block *)bh->b_data);
+       csum_type = btrfs_super_csum_type(disk_super);
        if (!btrfs_supported_super_csum(csum_type)) {
                btrfs_err(fs_info, "unsupported checksum algorithm: %u",
                          csum_type);
                err = -EINVAL;
-               brelse(bh);
+               btrfs_release_disk_super(disk_super);
                goto fail_alloc;
        }
 
        ret = btrfs_init_csum_hash(fs_info, csum_type);
        if (ret) {
                err = ret;
+               btrfs_release_disk_super(disk_super);
                goto fail_alloc;
        }
 
         * We want to check superblock checksum, the type is stored inside.
         * Pass the whole disk block of size BTRFS_SUPER_INFO_SIZE (4k).
         */
-       if (btrfs_check_super_csum(fs_info, bh->b_data)) {
+       if (btrfs_check_super_csum(fs_info, (u8 *)disk_super)) {
                btrfs_err(fs_info, "superblock checksum mismatch");
                err = -EINVAL;
-               brelse(bh);
+               btrfs_release_disk_super(disk_super);
                goto fail_alloc;
        }
 
         * following bytes up to INFO_SIZE, the checksum is calculated from
         * the whole block of INFO_SIZE
         */
-       memcpy(fs_info->super_copy, bh->b_data, sizeof(*fs_info->super_copy));
-       brelse(bh);
+       memcpy(fs_info->super_copy, disk_super, sizeof(*fs_info->super_copy));
+       btrfs_release_disk_super(disk_super);
 
        disk_super = fs_info->super_copy;
 
        put_bh(bh);
 }
 
-int btrfs_read_dev_one_super(struct block_device *bdev, int copy_num,
-                       struct buffer_head **bh_ret)
+struct btrfs_super_block *btrfs_read_dev_one_super(struct block_device *bdev,
+                                                  int copy_num)
 {
-       struct buffer_head *bh;
        struct btrfs_super_block *super;
+       struct page *page;
        u64 bytenr;
+       struct address_space *mapping = bdev->bd_inode->i_mapping;
 
        bytenr = btrfs_sb_offset(copy_num);
        if (bytenr + BTRFS_SUPER_INFO_SIZE >= i_size_read(bdev->bd_inode))
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
 
-       bh = __bread(bdev, bytenr / BTRFS_BDEV_BLOCKSIZE, BTRFS_SUPER_INFO_SIZE);
-       /*
-        * If we fail to read from the underlying devices, as of now
-        * the best option we have is to mark it EIO.
-        */
-       if (!bh)
-               return -EIO;
+       page = read_cache_page_gfp(mapping, bytenr >> PAGE_SHIFT, GFP_NOFS);
+       if (IS_ERR(page))
+               return ERR_CAST(page);
 
-       super = (struct btrfs_super_block *)bh->b_data;
+       super = page_address(page);
        if (btrfs_super_bytenr(super) != bytenr ||
                    btrfs_super_magic(super) != BTRFS_MAGIC) {
-               brelse(bh);
-               return -EINVAL;
+               btrfs_release_disk_super(super);
+               return ERR_PTR(-EINVAL);
        }
 
-       *bh_ret = bh;
-       return 0;
+       return super;
 }
 
 
-struct buffer_head *btrfs_read_dev_super(struct block_device *bdev)
+struct btrfs_super_block *btrfs_read_dev_super(struct block_device *bdev)
 {
-       struct buffer_head *bh;
-       struct buffer_head *latest = NULL;
-       struct btrfs_super_block *super;
+       struct btrfs_super_block *super, *latest = NULL;
        int i;
        u64 transid = 0;
-       int ret = -EINVAL;
 
        /* we would like to check all the supers, but that would make
         * a btrfs mount succeed after a mkfs from a different FS.
         * later supers, using BTRFS_SUPER_MIRROR_MAX instead
         */
        for (i = 0; i < 1; i++) {
-               ret = btrfs_read_dev_one_super(bdev, i, &bh);
-               if (ret)
+               super = btrfs_read_dev_one_super(bdev, i);
+               if (IS_ERR(super))
                        continue;
 
-               super = (struct btrfs_super_block *)bh->b_data;
-
                if (!latest || btrfs_super_generation(super) > transid) {
-                       brelse(latest);
-                       latest = bh;
+                       if (latest)
+                               btrfs_release_disk_super(super);
+
+                       latest = super;
                        transid = btrfs_super_generation(super);
-               } else {
-                       brelse(bh);
                }
        }
 
-       if (!latest)
-               return ERR_PTR(ret);
-
-       return latest;
+       return super;
 }
 
 /*
 
 #include <linux/sched.h>
 #include <linux/bio.h>
 #include <linux/slab.h>
-#include <linux/buffer_head.h>
 #include <linux/blkdev.h>
 #include <linux/ratelimit.h>
 #include <linux/kthread.h>
 static int
 btrfs_get_bdev_and_sb(const char *device_path, fmode_t flags, void *holder,
                      int flush, struct block_device **bdev,
-                     struct buffer_head **bh)
+                     struct btrfs_super_block **disk_super)
 {
        int ret;
 
                goto error;
        }
        invalidate_bdev(*bdev);
-       *bh = btrfs_read_dev_super(*bdev);
-       if (IS_ERR(*bh)) {
-               ret = PTR_ERR(*bh);
+       *disk_super = btrfs_read_dev_super(*bdev);
+       if (IS_ERR(*disk_super)) {
+               ret = PTR_ERR(*disk_super);
                blkdev_put(*bdev, flags);
                goto error;
        }
 
 error:
        *bdev = NULL;
-       *bh = NULL;
        return ret;
 }
 
 {
        struct request_queue *q;
        struct block_device *bdev;
-       struct buffer_head *bh;
        struct btrfs_super_block *disk_super;
        u64 devid;
        int ret;
                return -EINVAL;
 
        ret = btrfs_get_bdev_and_sb(device->name->str, flags, holder, 1,
-                                   &bdev, &bh);
+                                   &bdev, &disk_super);
        if (ret)
                return ret;
 
-       disk_super = (struct btrfs_super_block *)bh->b_data;
        devid = btrfs_stack_device_id(&disk_super->dev_item);
        if (devid != device->devid)
-               goto error_brelse;
+               goto error_free_page;
 
        if (memcmp(device->uuid, disk_super->dev_item.uuid, BTRFS_UUID_SIZE))
-               goto error_brelse;
+               goto error_free_page;
 
        device->generation = btrfs_super_generation(disk_super);
 
                    BTRFS_FEATURE_INCOMPAT_METADATA_UUID) {
                        pr_err(
                "BTRFS: Invalid seeding and uuid-changed device detected\n");
-                       goto error_brelse;
+                       goto error_free_page;
                }
 
                clear_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state);
                fs_devices->rw_devices++;
                list_add_tail(&device->dev_alloc_list, &fs_devices->alloc_list);
        }
-       brelse(bh);
+       btrfs_release_disk_super(disk_super);
 
        return 0;
 
-error_brelse:
-       brelse(bh);
+error_free_page:
+       btrfs_release_disk_super(disk_super);
        blkdev_put(bdev, flags);
 
        return -EINVAL;
        return ret;
 }
 
-void btrfs_release_disk_super(struct page *page)
+void btrfs_release_disk_super(struct btrfs_super_block *super)
 {
+       struct page *page = virt_to_page(super);
+
        put_page(page);
 }
 
 
        if (btrfs_super_bytenr(*disk_super) != bytenr ||
            btrfs_super_magic(*disk_super) != BTRFS_MAGIC) {
-               btrfs_release_disk_super(*page);
+               btrfs_release_disk_super(p);
                return 1;
        }
 
                        btrfs_free_stale_devices(path, device);
        }
 
-       btrfs_release_disk_super(page);
+       btrfs_release_disk_super(disk_super);
 
 error_bdev_put:
        blkdev_put(bdev, flags);
        return num_devices;
 }
 
-static void btrfs_scratch_superblocks(struct block_device *bdev,
+static void btrfs_scratch_superblocks(struct btrfs_fs_info *fs_info,
+                                     struct block_device *bdev,
                                      const char *device_path)
 {
-       struct buffer_head *bh;
        struct btrfs_super_block *disk_super;
        int copy_num;
 
                return;
 
        for (copy_num = 0; copy_num < BTRFS_SUPER_MIRROR_MAX; copy_num++) {
-               if (btrfs_read_dev_one_super(bdev, copy_num, &bh))
-                       continue;
+               struct page *page;
+               int ret;
 
-               disk_super = (struct btrfs_super_block *)bh->b_data;
+               disk_super = btrfs_read_dev_one_super(bdev, copy_num);
+               if (IS_ERR(disk_super))
+                       continue;
 
                memset(&disk_super->magic, 0, sizeof(disk_super->magic));
-               set_buffer_dirty(bh);
-               sync_dirty_buffer(bh);
-               brelse(bh);
+
+               page = virt_to_page(disk_super);
+               set_page_dirty(page);
+               lock_page(page);
+               /* write_on_page() unlocks the page */
+               ret = write_one_page(page);
+               if (ret)
+                       btrfs_warn(fs_info,
+                               "error clearing superblock number %d (%d)",
+                               copy_num, ret);
+               btrfs_release_disk_super(disk_super);
+
        }
 
        /* Notify udev that device has changed */
         * supers and free the device.
         */
        if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &device->dev_state))
-               btrfs_scratch_superblocks(device->bdev, device->name->str);
+               btrfs_scratch_superblocks(fs_info, device->bdev,
+                                         device->name->str);
 
        btrfs_close_bdev(device);
        synchronize_rcu();
 
        if (test_bit(BTRFS_DEV_STATE_WRITEABLE, &srcdev->dev_state)) {
                /* zero out the old super if it is writable */
-               btrfs_scratch_superblocks(srcdev->bdev, srcdev->name->str);
+               btrfs_scratch_superblocks(fs_info, srcdev->bdev,
+                                         srcdev->name->str);
        }
 
        btrfs_close_bdev(srcdev);
         * is already out of device list, so we don't have to hold
         * the device_list_mutex lock.
         */
-       btrfs_scratch_superblocks(tgtdev->bdev, tgtdev->name->str);
+       btrfs_scratch_superblocks(tgtdev->fs_info, tgtdev->bdev,
+                                 tgtdev->name->str);
 
        btrfs_close_bdev(tgtdev);
        synchronize_rcu();
        u64 devid;
        u8 *dev_uuid;
        struct block_device *bdev;
-       struct buffer_head *bh;
        struct btrfs_device *device;
 
        ret = btrfs_get_bdev_and_sb(device_path, FMODE_READ,
-                                   fs_info->bdev_holder, 0, &bdev, &bh);
+                                   fs_info->bdev_holder, 0, &bdev, &disk_super);
        if (ret)
                return ERR_PTR(ret);
-       disk_super = (struct btrfs_super_block *)bh->b_data;
+
        devid = btrfs_stack_device_id(&disk_super->dev_item);
        dev_uuid = disk_super->dev_item.uuid;
        if (btrfs_fs_incompat(fs_info, METADATA_UUID))
                device = btrfs_find_device(fs_info->fs_devices, devid, dev_uuid,
                                           disk_super->fsid, true);
 
-       brelse(bh);
+       btrfs_release_disk_super(disk_super);
        if (!device)
                device = ERR_PTR(-ENOENT);
        blkdev_put(bdev, FMODE_READ);