Skip to content

Commit

Permalink
btrfs: zoned: implement zoned chunk allocator
Browse files Browse the repository at this point in the history
Implement a zoned chunk and device extent allocator. One device zone
becomes a device extent so that a zone reset affects only this device
extent and does not change the state of blocks in the neighbor device
extents.

To implement the allocator, we need to extend the following functions for
a zoned filesystem.

- init_alloc_chunk_ctl
- dev_extent_search_start
- dev_extent_hole_check
- decide_stripe_size

init_alloc_chunk_ctl_zoned() is mostly the same as regular one. It always
set the stripe_size to the zone size and aligns the parameters to the zone
size.

dev_extent_search_start() only aligns the start offset to zone boundaries.
We don't care about the first 1MB like in regular filesystem because we
anyway reserve the first two zones for superblock logging.

dev_extent_hole_check_zoned() checks if zones in given hole are either
conventional or empty sequential zones. Also, it skips zones reserved for
superblock logging.

With the change to the hole, the new hole may now contain pending extents.
So, in this case, loop again to check that.

Finally, decide_stripe_size_zoned() should shrink the number of devices
instead of stripe size because we need to honor stripe_size == zone_size.

Reviewed-by: Anand Jain <anand.jain@oracle.com>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
  • Loading branch information
Naohiro Aota authored and David Sterba committed Feb 9, 2021
1 parent 3c9daa0 commit 1cd6121
Show file tree
Hide file tree
Showing 4 changed files with 321 additions and 17 deletions.
171 changes: 154 additions & 17 deletions fs/btrfs/volumes.c
Original file line number Diff line number Diff line change
Expand Up @@ -1414,19 +1414,70 @@ static u64 dev_extent_search_start(struct btrfs_device *device, u64 start)
* make sure to start at an offset of at least 1MB.
*/
return max_t(u64, start, SZ_1M);
case BTRFS_CHUNK_ALLOC_ZONED:
/*
* We don't care about the starting region like regular
* allocator, because we anyway use/reserve the first two zones
* for superblock logging.
*/
return ALIGN(start, device->zone_info->zone_size);
default:
BUG();
}
}

static bool dev_extent_hole_check_zoned(struct btrfs_device *device,
u64 *hole_start, u64 *hole_size,
u64 num_bytes)
{
u64 zone_size = device->zone_info->zone_size;
u64 pos;
int ret;
bool changed = false;

ASSERT(IS_ALIGNED(*hole_start, zone_size));

while (*hole_size > 0) {
pos = btrfs_find_allocatable_zones(device, *hole_start,
*hole_start + *hole_size,
num_bytes);
if (pos != *hole_start) {
*hole_size = *hole_start + *hole_size - pos;
*hole_start = pos;
changed = true;
if (*hole_size < num_bytes)
break;
}

ret = btrfs_ensure_empty_zones(device, pos, num_bytes);

/* Range is ensured to be empty */
if (!ret)
return changed;

/* Given hole range was invalid (outside of device) */
if (ret == -ERANGE) {
*hole_start += *hole_size;
*hole_size = 0;
return 1;
}

*hole_start += zone_size;
*hole_size -= zone_size;
changed = true;
}

return changed;
}

/**
* dev_extent_hole_check - check if specified hole is suitable for allocation
* @device: the device which we have the hole
* @hole_start: starting position of the hole
* @hole_size: the size of the hole
* @num_bytes: the size of the free space that we need
*
* This function may modify @hole_start and @hole_end to reflect the suitable
* This function may modify @hole_start and @hole_size to reflect the suitable
* position for allocation. Returns 1 if hole position is updated, 0 otherwise.
*/
static bool dev_extent_hole_check(struct btrfs_device *device, u64 *hole_start,
Expand All @@ -1435,24 +1486,39 @@ static bool dev_extent_hole_check(struct btrfs_device *device, u64 *hole_start,
bool changed = false;
u64 hole_end = *hole_start + *hole_size;

/*
* Check before we set max_hole_start, otherwise we could end up
* sending back this offset anyway.
*/
if (contains_pending_extent(device, hole_start, *hole_size)) {
if (hole_end >= *hole_start)
*hole_size = hole_end - *hole_start;
else
*hole_size = 0;
changed = true;
}
for (;;) {
/*
* Check before we set max_hole_start, otherwise we could end up
* sending back this offset anyway.
*/
if (contains_pending_extent(device, hole_start, *hole_size)) {
if (hole_end >= *hole_start)
*hole_size = hole_end - *hole_start;
else
*hole_size = 0;
changed = true;
}

switch (device->fs_devices->chunk_alloc_policy) {
case BTRFS_CHUNK_ALLOC_REGULAR:
/* No extra check */
break;
case BTRFS_CHUNK_ALLOC_ZONED:
if (dev_extent_hole_check_zoned(device, hole_start,
hole_size, num_bytes)) {
changed = true;
/*
* The changed hole can contain pending extent.
* Loop again to check that.
*/
continue;
}
break;
default:
BUG();
}

switch (device->fs_devices->chunk_alloc_policy) {
case BTRFS_CHUNK_ALLOC_REGULAR:
/* No extra check */
break;
default:
BUG();
}

return changed;
Expand Down Expand Up @@ -1505,6 +1571,9 @@ static int find_free_dev_extent_start(struct btrfs_device *device,

search_start = dev_extent_search_start(device, search_start);

WARN_ON(device->zone_info &&
!IS_ALIGNED(num_bytes, device->zone_info->zone_size));

path = btrfs_alloc_path();
if (!path)
return -ENOMEM;
Expand Down Expand Up @@ -4899,6 +4968,37 @@ static void init_alloc_chunk_ctl_policy_regular(
ctl->dev_extent_min = BTRFS_STRIPE_LEN * ctl->dev_stripes;
}

static void init_alloc_chunk_ctl_policy_zoned(
struct btrfs_fs_devices *fs_devices,
struct alloc_chunk_ctl *ctl)
{
u64 zone_size = fs_devices->fs_info->zone_size;
u64 limit;
int min_num_stripes = ctl->devs_min * ctl->dev_stripes;
int min_data_stripes = (min_num_stripes - ctl->nparity) / ctl->ncopies;
u64 min_chunk_size = min_data_stripes * zone_size;
u64 type = ctl->type;

ctl->max_stripe_size = zone_size;
if (type & BTRFS_BLOCK_GROUP_DATA) {
ctl->max_chunk_size = round_down(BTRFS_MAX_DATA_CHUNK_SIZE,
zone_size);
} else if (type & BTRFS_BLOCK_GROUP_METADATA) {
ctl->max_chunk_size = ctl->max_stripe_size;
} else if (type & BTRFS_BLOCK_GROUP_SYSTEM) {
ctl->max_chunk_size = 2 * ctl->max_stripe_size;
ctl->devs_max = min_t(int, ctl->devs_max,
BTRFS_MAX_DEVS_SYS_CHUNK);
}

/* We don't want a chunk larger than 10% of writable space */
limit = max(round_down(div_factor(fs_devices->total_rw_bytes, 1),
zone_size),
min_chunk_size);
ctl->max_chunk_size = min(limit, ctl->max_chunk_size);
ctl->dev_extent_min = zone_size * ctl->dev_stripes;
}

static void init_alloc_chunk_ctl(struct btrfs_fs_devices *fs_devices,
struct alloc_chunk_ctl *ctl)
{
Expand All @@ -4919,6 +5019,9 @@ static void init_alloc_chunk_ctl(struct btrfs_fs_devices *fs_devices,
case BTRFS_CHUNK_ALLOC_REGULAR:
init_alloc_chunk_ctl_policy_regular(fs_devices, ctl);
break;
case BTRFS_CHUNK_ALLOC_ZONED:
init_alloc_chunk_ctl_policy_zoned(fs_devices, ctl);
break;
default:
BUG();
}
Expand Down Expand Up @@ -5045,6 +5148,38 @@ static int decide_stripe_size_regular(struct alloc_chunk_ctl *ctl,
return 0;
}

static int decide_stripe_size_zoned(struct alloc_chunk_ctl *ctl,
struct btrfs_device_info *devices_info)
{
u64 zone_size = devices_info[0].dev->zone_info->zone_size;
/* Number of stripes that count for block group size */
int data_stripes;

/*
* It should hold because:
* dev_extent_min == dev_extent_want == zone_size * dev_stripes
*/
ASSERT(devices_info[ctl->ndevs - 1].max_avail == ctl->dev_extent_min);

ctl->stripe_size = zone_size;
ctl->num_stripes = ctl->ndevs * ctl->dev_stripes;
data_stripes = (ctl->num_stripes - ctl->nparity) / ctl->ncopies;

/* stripe_size is fixed in zoned filesysmte. Reduce ndevs instead. */
if (ctl->stripe_size * data_stripes > ctl->max_chunk_size) {
ctl->ndevs = div_u64(div_u64(ctl->max_chunk_size * ctl->ncopies,
ctl->stripe_size) + ctl->nparity,
ctl->dev_stripes);
ctl->num_stripes = ctl->ndevs * ctl->dev_stripes;
data_stripes = (ctl->num_stripes - ctl->nparity) / ctl->ncopies;
ASSERT(ctl->stripe_size * data_stripes <= ctl->max_chunk_size);
}

ctl->chunk_size = ctl->stripe_size * data_stripes;

return 0;
}

static int decide_stripe_size(struct btrfs_fs_devices *fs_devices,
struct alloc_chunk_ctl *ctl,
struct btrfs_device_info *devices_info)
Expand Down Expand Up @@ -5072,6 +5207,8 @@ static int decide_stripe_size(struct btrfs_fs_devices *fs_devices,
switch (fs_devices->chunk_alloc_policy) {
case BTRFS_CHUNK_ALLOC_REGULAR:
return decide_stripe_size_regular(ctl, devices_info);
case BTRFS_CHUNK_ALLOC_ZONED:
return decide_stripe_size_zoned(ctl, devices_info);
default:
BUG();
}
Expand Down
1 change: 1 addition & 0 deletions fs/btrfs/volumes.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ BTRFS_DEVICE_GETSET_FUNCS(bytes_used);

enum btrfs_chunk_allocation_policy {
BTRFS_CHUNK_ALLOC_REGULAR,
BTRFS_CHUNK_ALLOC_ZONED,
};

/*
Expand Down
Loading

0 comments on commit 1cd6121

Please sign in to comment.