btrfs: zoned: cache reported zone during mount
commit 16beac87e95e2fb278b552397c8260637f8a63f7 upstream. When mounting a device, we are reporting the zones twice: once for checking the zone attributes in btrfs_get_dev_zone_info and once for loading block groups' zone info in btrfs_load_block_group_zone_info(). With a lot of block groups, that leads to a lot of REPORT ZONE commands and slows down the mount process. This patch introduces a zone info cache in struct btrfs_zoned_device_info. The cache is populated while in btrfs_get_dev_zone_info() and used for btrfs_load_block_group_zone_info() to reduce the number of REPORT ZONE commands. The zone cache is then released after loading the block groups, as it will not be much effective during the run time. Benchmark: Mount an HDD with 57,007 block groups Before patch: 171.368 seconds After patch: 64.064 seconds While it still takes a minute due to the slowness of loading all the block groups, the patch reduces the mount time by 1/3. Link: https://lore.kernel.org/linux-btrfs/CAHQ7scUiLtcTqZOMMY5kbWUBOhGRwKo6J6wYPT5WY+C=cD49nQ@mail.gmail.com/ Fixes: 5b316468983d ("btrfs: get zone information of zoned block devices") CC: stable@vger.kernel.org Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com> Signed-off-by: David Sterba <dsterba@suse.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
a8dd0cfa37
commit
ae6ca63439
@ -325,7 +325,7 @@ static int btrfs_init_dev_replace_tgtdev(struct btrfs_fs_info *fs_info,
|
||||
set_blocksize(device->bdev, BTRFS_BDEV_BLOCKSIZE);
|
||||
device->fs_devices = fs_info->fs_devices;
|
||||
|
||||
ret = btrfs_get_dev_zone_info(device);
|
||||
ret = btrfs_get_dev_zone_info(device, false);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
|
@ -3565,6 +3565,8 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
|
||||
goto fail_sysfs;
|
||||
}
|
||||
|
||||
btrfs_free_zone_cache(fs_info);
|
||||
|
||||
if (!sb_rdonly(sb) && fs_info->fs_devices->missing_devices &&
|
||||
!btrfs_check_rw_degradable(fs_info, NULL)) {
|
||||
btrfs_warn(fs_info,
|
||||
|
@ -2596,7 +2596,7 @@ int btrfs_init_new_device(struct btrfs_fs_info *fs_info, const char *device_path
|
||||
device->fs_info = fs_info;
|
||||
device->bdev = bdev;
|
||||
|
||||
ret = btrfs_get_dev_zone_info(device);
|
||||
ret = btrfs_get_dev_zone_info(device, false);
|
||||
if (ret)
|
||||
goto error_free_device;
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include "ctree.h"
|
||||
#include "volumes.h"
|
||||
#include "zoned.h"
|
||||
@ -195,6 +196,8 @@ static int emulate_report_zones(struct btrfs_device *device, u64 pos,
|
||||
static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
|
||||
struct blk_zone *zones, unsigned int *nr_zones)
|
||||
{
|
||||
struct btrfs_zoned_device_info *zinfo = device->zone_info;
|
||||
u32 zno;
|
||||
int ret;
|
||||
|
||||
if (!*nr_zones)
|
||||
@ -206,6 +209,34 @@ static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check cache */
|
||||
if (zinfo->zone_cache) {
|
||||
unsigned int i;
|
||||
|
||||
ASSERT(IS_ALIGNED(pos, zinfo->zone_size));
|
||||
zno = pos >> zinfo->zone_size_shift;
|
||||
/*
|
||||
* We cannot report zones beyond the zone end. So, it is OK to
|
||||
* cap *nr_zones to at the end.
|
||||
*/
|
||||
*nr_zones = min_t(u32, *nr_zones, zinfo->nr_zones - zno);
|
||||
|
||||
for (i = 0; i < *nr_zones; i++) {
|
||||
struct blk_zone *zone_info;
|
||||
|
||||
zone_info = &zinfo->zone_cache[zno + i];
|
||||
if (!zone_info->len)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == *nr_zones) {
|
||||
/* Cache hit on all the zones */
|
||||
memcpy(zones, zinfo->zone_cache + zno,
|
||||
sizeof(*zinfo->zone_cache) * *nr_zones);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
ret = blkdev_report_zones(device->bdev, pos >> SECTOR_SHIFT, *nr_zones,
|
||||
copy_zone_info_cb, zones);
|
||||
if (ret < 0) {
|
||||
@ -219,6 +250,11 @@ static int btrfs_get_dev_zones(struct btrfs_device *device, u64 pos,
|
||||
if (!ret)
|
||||
return -EIO;
|
||||
|
||||
/* Populate cache */
|
||||
if (zinfo->zone_cache)
|
||||
memcpy(zinfo->zone_cache + zno, zones,
|
||||
sizeof(*zinfo->zone_cache) * *nr_zones);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -282,7 +318,7 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
|
||||
if (!device->bdev)
|
||||
continue;
|
||||
|
||||
ret = btrfs_get_dev_zone_info(device);
|
||||
ret = btrfs_get_dev_zone_info(device, true);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
@ -291,7 +327,7 @@ int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info)
|
||||
return ret;
|
||||
}
|
||||
|
||||
int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache)
|
||||
{
|
||||
struct btrfs_fs_info *fs_info = device->fs_info;
|
||||
struct btrfs_zoned_device_info *zone_info = NULL;
|
||||
@ -318,6 +354,8 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
if (!zone_info)
|
||||
return -ENOMEM;
|
||||
|
||||
device->zone_info = zone_info;
|
||||
|
||||
if (!bdev_is_zoned(bdev)) {
|
||||
if (!fs_info->zone_size) {
|
||||
ret = calculate_emulated_zone_size(fs_info);
|
||||
@ -369,6 +407,23 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable zone cache only for a zoned device. On a non-zoned device, we
|
||||
* fill the zone info with emulated CONVENTIONAL zones, so no need to
|
||||
* use the cache.
|
||||
*/
|
||||
if (populate_cache && bdev_is_zoned(device->bdev)) {
|
||||
zone_info->zone_cache = vzalloc(sizeof(struct blk_zone) *
|
||||
zone_info->nr_zones);
|
||||
if (!zone_info->zone_cache) {
|
||||
btrfs_err_in_rcu(device->fs_info,
|
||||
"zoned: failed to allocate zone cache for %s",
|
||||
rcu_str_deref(device->name));
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* Get zones type */
|
||||
while (sector < nr_sectors) {
|
||||
nr_zones = BTRFS_REPORT_NR_ZONES;
|
||||
@ -444,8 +499,6 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
|
||||
kfree(zones);
|
||||
|
||||
device->zone_info = zone_info;
|
||||
|
||||
switch (bdev_zoned_model(bdev)) {
|
||||
case BLK_ZONED_HM:
|
||||
model = "host-managed zoned";
|
||||
@ -478,10 +531,7 @@ int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
out:
|
||||
kfree(zones);
|
||||
out_free_zone_info:
|
||||
bitmap_free(zone_info->empty_zones);
|
||||
bitmap_free(zone_info->seq_zones);
|
||||
kfree(zone_info);
|
||||
device->zone_info = NULL;
|
||||
btrfs_destroy_dev_zone_info(device);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -495,6 +545,7 @@ void btrfs_destroy_dev_zone_info(struct btrfs_device *device)
|
||||
|
||||
bitmap_free(zone_info->seq_zones);
|
||||
bitmap_free(zone_info->empty_zones);
|
||||
vfree(zone_info->zone_cache);
|
||||
kfree(zone_info);
|
||||
device->zone_info = NULL;
|
||||
}
|
||||
@ -1551,3 +1602,21 @@ void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg)
|
||||
fs_info->data_reloc_bg = 0;
|
||||
spin_unlock(&fs_info->relocation_bg_lock);
|
||||
}
|
||||
|
||||
void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info)
|
||||
{
|
||||
struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
|
||||
struct btrfs_device *device;
|
||||
|
||||
if (!btrfs_is_zoned(fs_info))
|
||||
return;
|
||||
|
||||
mutex_lock(&fs_devices->device_list_mutex);
|
||||
list_for_each_entry(device, &fs_devices->devices, dev_list) {
|
||||
if (device->zone_info) {
|
||||
vfree(device->zone_info->zone_cache);
|
||||
device->zone_info->zone_cache = NULL;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&fs_devices->device_list_mutex);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ struct btrfs_zoned_device_info {
|
||||
u32 nr_zones;
|
||||
unsigned long *seq_zones;
|
||||
unsigned long *empty_zones;
|
||||
struct blk_zone *zone_cache;
|
||||
struct blk_zone sb_zones[2 * BTRFS_SUPER_MIRROR_MAX];
|
||||
};
|
||||
|
||||
@ -32,7 +33,7 @@ struct btrfs_zoned_device_info {
|
||||
int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
|
||||
struct blk_zone *zone);
|
||||
int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_get_dev_zone_info(struct btrfs_device *device);
|
||||
int btrfs_get_dev_zone_info(struct btrfs_device *device, bool populate_cache);
|
||||
void btrfs_destroy_dev_zone_info(struct btrfs_device *device);
|
||||
int btrfs_check_zoned_mode(struct btrfs_fs_info *fs_info);
|
||||
int btrfs_check_mountopts_zoned(struct btrfs_fs_info *info);
|
||||
@ -67,6 +68,7 @@ int btrfs_sync_zone_write_pointer(struct btrfs_device *tgt_dev, u64 logical,
|
||||
struct btrfs_device *btrfs_zoned_get_device(struct btrfs_fs_info *fs_info,
|
||||
u64 logical, u64 length);
|
||||
void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg);
|
||||
void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info);
|
||||
#else /* CONFIG_BLK_DEV_ZONED */
|
||||
static inline int btrfs_get_dev_zone(struct btrfs_device *device, u64 pos,
|
||||
struct blk_zone *zone)
|
||||
@ -79,7 +81,8 @@ static inline int btrfs_get_dev_zone_info_all_devices(struct btrfs_fs_info *fs_i
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int btrfs_get_dev_zone_info(struct btrfs_device *device)
|
||||
static inline int btrfs_get_dev_zone_info(struct btrfs_device *device,
|
||||
bool populate_cache)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@ -202,6 +205,7 @@ static inline struct btrfs_device *btrfs_zoned_get_device(
|
||||
|
||||
static inline void btrfs_clear_data_reloc_bg(struct btrfs_block_group *bg) { }
|
||||
|
||||
static inline void btrfs_free_zone_cache(struct btrfs_fs_info *fs_info) { }
|
||||
#endif
|
||||
|
||||
static inline bool btrfs_dev_is_sequential(struct btrfs_device *device, u64 pos)
|
||||
|
Loading…
x
Reference in New Issue
Block a user