From 52c2a465dbdfe14f486cbc675f2a86edad098bde Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 17 May 2021 12:54:53 -0700 Subject: [PATCH] Add zone awareness to scoutfs_alloc_move() Add parameters so that scoutfs_alloc_move() can first search for source extents in specified zones. It uses relatively cheap searches through the order items to find extents that intersect with the regions described by the zone bitmaps. Signed-off-by: Zach Brown --- kmod/src/alloc.c | 149 ++++++++++++++++++++++++++++++++++++++++++++-- kmod/src/alloc.h | 3 +- kmod/src/server.c | 2 +- 3 files changed, 147 insertions(+), 7 deletions(-) diff --git a/kmod/src/alloc.c b/kmod/src/alloc.c index a6c17093..92276395 100644 --- a/kmod/src/alloc.c +++ b/kmod/src/alloc.c @@ -772,6 +772,95 @@ int scoutfs_free_data(struct super_block *sb, struct scoutfs_alloc *alloc, return ret; } +/* + * Return the first zone bit that the extent intersects with. + */ +static int first_extent_zone(struct scoutfs_extent *ext, __le64 *zones, u64 zone_blocks) +{ + int first; + int last; + int nr; + + first = div64_u64(ext->start, zone_blocks); + last = div64_u64(ext->start + ext->len - 1, zone_blocks); + + nr = find_next_bit_le(zones, SCOUTFS_DATA_ALLOC_MAX_ZONES, first); + if (nr <= last) + return nr; + + return SCOUTFS_DATA_ALLOC_MAX_ZONES; +} + +/* + * Find an extent in specific zones to satisfy an allocation. We use + * the order items to search for the largest extent that intersects with + * the zones whose bits are set in the caller's bitmap. + */ +static int find_zone_extent(struct super_block *sb, struct scoutfs_alloc_root *root, + __le64 *zones, u64 zone_blocks, + struct scoutfs_extent *found_ret, u64 count, + struct scoutfs_extent *ext_ret) +{ + struct alloc_ext_args args = { + .root = root, + .zone = SCOUTFS_FREE_EXTENT_ORDER_ZONE, + }; + struct scoutfs_extent found; + struct scoutfs_extent ext; + u64 start; + u64 len; + int nr; + int ret; + + /* don't bother when there are no bits set */ + if (find_next_bit_le(zones, SCOUTFS_DATA_ALLOC_MAX_ZONES, 0) == + SCOUTFS_DATA_ALLOC_MAX_ZONES) + return -ENOENT; + + /* start searching for largest extent from the first zone */ + len = smallest_order_length(SCOUTFS_BLOCK_SM_MAX); + nr = 0; + + for (;;) { + /* search for extents in the next zone at our order */ + nr = find_next_bit_le(zones, SCOUTFS_DATA_ALLOC_MAX_ZONES, nr); + if (nr >= SCOUTFS_DATA_ALLOC_MAX_ZONES) { + /* wrap down to next smaller order if we run out of bits */ + len >>= 3; + if (len == 0) { + ret = -ENOENT; + break; + } + nr = find_next_bit_le(zones, SCOUTFS_DATA_ALLOC_MAX_ZONES, 0); + } + + start = (u64)nr * zone_blocks; + + ret = scoutfs_ext_next(sb, &alloc_ext_ops, &args, start, len, &found); + if (ret < 0) + break; + + /* see if the next extent intersects any zones */ + nr = first_extent_zone(&found, zones, zone_blocks); + if (nr < SCOUTFS_DATA_ALLOC_MAX_ZONES) { + start = (u64)nr * zone_blocks; + + ext.start = max(start, found.start); + ext.len = min(count, found.start + found.len - ext.start); + + *found_ret = found; + *ext_ret = ext; + ret = 0; + break; + } + + /* continue searching past extent */ + nr = div64_u64(found.start + found.len - 1, zone_blocks) + 1; + len = smallest_order_length(found.len); + } + + return ret; +} /* * Move extent items adding up to the requested total length from the @@ -782,6 +871,11 @@ int scoutfs_free_data(struct super_block *sb, struct scoutfs_alloc *alloc, * -ENOENT is returned if we run out of extents in the source tree * before moving the total. * + * The caller can specify that extents in the source tree should first + * be found based on their zone bitmaps. We'll first try to find + * extents in the exclusive zones, then vacant zones, and then we'll + * fall back to normal allocation that ignores zones. + * * This first pass is not optimal because it performs full btree walks * per extent. We could optimize this with more clever btree item * manipulation functions which can iterate through src and dst blocks @@ -790,30 +884,75 @@ int scoutfs_free_data(struct super_block *sb, struct scoutfs_alloc *alloc, int scoutfs_alloc_move(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *dst, - struct scoutfs_alloc_root *src, u64 total) + struct scoutfs_alloc_root *src, u64 total, + __le64 *exclusive, __le64 *vacant, u64 zone_blocks) { struct alloc_ext_args args = { .alloc = alloc, .wri = wri, }; + struct scoutfs_extent found; struct scoutfs_extent ext; u64 moved = 0; + u64 count; int ret = 0; int err; + if (zone_blocks == 0) { + exclusive = NULL; + vacant = NULL; + } + while (moved < total) { - args.root = src; - args.zone = SCOUTFS_FREE_EXTENT_ORDER_ZONE; - ret = scoutfs_ext_alloc(sb, &alloc_ext_ops, &args, - 0, 0, total - moved, &ext); + count = total - moved; + + if (exclusive) { + /* first try to find extents in our exclusive zones */ + ret = find_zone_extent(sb, src, exclusive, zone_blocks, + &found, count, &ext); + if (ret == -ENOENT) { + exclusive = NULL; + continue; + } + } else if (vacant) { + /* then try to find extents in vacant zones */ + ret = find_zone_extent(sb, src, vacant, zone_blocks, + &found, count, &ext); + if (ret == -ENOENT) { + vacant = NULL; + continue; + } + } else { + /* otherwise fall back to finding extents anywhere */ + args.root = src; + args.zone = SCOUTFS_FREE_EXTENT_ORDER_ZONE; + ret = scoutfs_ext_next(sb, &alloc_ext_ops, &args, 0, 0, &found); + if (ret == 0) { + ext.start = found.start; + ext.len = min(count, found.len); + } + } if (ret < 0) break; + /* searching set start/len, finish initializing alloced extent */ + ext.map = found.map ? ext.start - found.start + found.map : 0; + ext.flags = found.flags; + + /* remove the allocation from the found extent */ + args.root = src; + args.zone = SCOUTFS_FREE_EXTENT_BLKNO_ZONE; + ret = scoutfs_ext_remove(sb, &alloc_ext_ops, &args, ext.start, ext.len); + if (ret < 0) + break; + + /* insert the allocated extent into the dest */ args.root = dst; args.zone = SCOUTFS_FREE_EXTENT_BLKNO_ZONE; ret = scoutfs_ext_insert(sb, &alloc_ext_ops, &args, ext.start, ext.len, ext.map, ext.flags); if (ret < 0) { + /* and put it back in src if insertion failed */ args.root = src; args.zone = SCOUTFS_FREE_EXTENT_BLKNO_ZONE; err = scoutfs_ext_insert(sb, &alloc_ext_ops, &args, diff --git a/kmod/src/alloc.h b/kmod/src/alloc.h index 78572d30..1e245c5f 100644 --- a/kmod/src/alloc.h +++ b/kmod/src/alloc.h @@ -125,7 +125,8 @@ int scoutfs_free_data(struct super_block *sb, struct scoutfs_alloc *alloc, int scoutfs_alloc_move(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, struct scoutfs_alloc_root *dst, - struct scoutfs_alloc_root *src, u64 total); + struct scoutfs_alloc_root *src, u64 total, + __le64 *exclusive, __le64 *vacant, u64 zone_blocks); int scoutfs_alloc_fill_list(struct super_block *sb, struct scoutfs_alloc *alloc, diff --git a/kmod/src/server.c b/kmod/src/server.c index 6544d9b7..74e41a40 100644 --- a/kmod/src/server.c +++ b/kmod/src/server.c @@ -409,7 +409,7 @@ static int alloc_move_refill(struct super_block *sb, return scoutfs_alloc_move(sb, &server->alloc, &server->wri, dst, src, min(target - le64_to_cpu(dst->total_len), - le64_to_cpu(src->total_len))); + le64_to_cpu(src->total_len)), NULL, NULL, 0); } static int alloc_move_empty(struct super_block *sb,