scoutfs: remove allocators that used btree items

Now that we have the allocators that use radix blocks we can remove all
the code that was using btree items to store free block bitmaps.

Signed-off-by: Zach Brown <zab@versity.com>
This commit is contained in:
Zach Brown
2020-02-25 12:03:46 -08:00
committed by Zach Brown
parent 85142dcadf
commit 300b7bc3ba
6 changed files with 0 additions and 1390 deletions
-593
View File
@@ -689,599 +689,6 @@ static int set_extent(struct super_block *sb, struct inode *inode,
return 0;
}
#if 0
static bool block_bitmap_fits(u64 blkno, u64 count)
{
return ((blkno & SCOUTFS_BLOCK_BITMAP_BIT_MASK) + count) <=
SCOUTFS_BLOCK_BITMAP_BITS;
}
static void block_bitmap_bit(u64 *base, int *bit, u64 blkno, u8 type)
{
if (type == SCOUTFS_BLOCK_BITMAP_BIG)
blkno >>= SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
*bit = blkno & SCOUTFS_BLOCK_BITMAP_BIT_MASK;
*base = blkno >> SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
}
static u64 block_bitmap_blkno(u64 base, int bit, u8 type)
{
u64 blkno;
blkno = (base << SCOUTFS_BLOCK_BITMAP_BASE_SHIFT) + bit;
if (type == SCOUTFS_BLOCK_BITMAP_BIG)
blkno <<= SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
return blkno;
}
struct block_bitmap {
u64 base;
u8 type;
bool exists;
unsigned long bits[DIV_ROUND_UP(SCOUTFS_BLOCK_BITMAP_BITS,
BITS_PER_LONG)];
};
static inline __le64 long_bits_to_le64(unsigned long *bits, unsigned int i)
{
#if BITS_PER_LONG == 64
return cpu_to_le64(bits[i]);
#elif BITS_PER_LONG == 32
i <<= 1;
return cpu_to_le64(bits[i] | ((u64)bits[i + 1] << 32));
#else
#error "unexpected BITS_PER_LONG value?"
#endif
}
static inline void u64_to_long_bits(unsigned long *bits, unsigned int i, u64 x)
{
#if BITS_PER_LONG == 64
bits[i] = x;
#else
i <<= 1;
bits[i] = x;
bits[i + 1] = x >> 32;
#endif
}
/*
* Block bitmaps are unpacked into native long bitmaps in memory for use
* with the kernel's bitmap functions. This requires a bit of finesse
* to make sure that we translate the bits appropriately to
* architectures with different word size and endian.
*/
static int unpack_block_bitmap(struct block_bitmap *bb,
struct scoutfs_btree_item_ref *iref)
{
struct scoutfs_block_bitmap_key *bbk;
struct scoutfs_packed_bitmap *pb;
unsigned int nr;
u64 present;
u64 set;
u64 b;
int ret;
int w;
int i;
if (iref->key_len != sizeof(struct scoutfs_block_bitmap_key) ||
iref->val_len < sizeof(struct scoutfs_packed_bitmap)) {
ret = -EIO;
goto out;
}
pb = iref->val;
bbk = iref->key;
bb->type = bbk->type;
bb->base = be64_to_cpu(bbk->base);
nr = hweight64(le64_to_cpu(pb->present));
if (iref->val_len !=
offsetof(struct scoutfs_packed_bitmap, words[nr])) {
ret = -EIO;
goto out;
}
present = le64_to_cpu(pb->present);
set = le64_to_cpu(pb->set);
w = 0;
for (i = 0, b = 1;
(present | set) && i < SCOUTFS_PACKED_BITMAP_WORDS;
i++, b <<= 1) {
if (set & b)
u64_to_long_bits(bb->bits, i, ~0ULL);
else if (present & b)
u64_to_long_bits(bb->bits, i,
le64_to_cpu(pb->words[w++]));
}
ret = 0;
out:
return ret;
}
static int load_block_bitmap(struct super_block *sb,
struct scoutfs_btree_root *root,
u64 blkno, u8 type, bool next, bool zero_enoent,
struct block_bitmap **bb_ret)
{
struct scoutfs_block_bitmap_key bbk;
struct block_bitmap *bb = NULL;
SCOUTFS_BTREE_ITEM_REF(iref);
u64 base;
int bit;
int ret;
bb = kzalloc(sizeof(struct block_bitmap), GFP_NOFS);
if (!bb) {
ret = -ENOMEM;
goto out;
}
block_bitmap_bit(&base, &bit, blkno, type);
bbk.type = type;
bbk.base = cpu_to_be64(base);
if (next)
ret = scoutfs_btree_next(sb, root, &bbk, sizeof(bbk), &iref);
else
ret = scoutfs_btree_lookup(sb, root, &bbk, sizeof(bbk), &iref);
if (ret == 0) {
ret = unpack_block_bitmap(bb, &iref);
bb->exists = true;
scoutfs_btree_put_iref(&iref);
}
if (ret == -ENOENT && zero_enoent) {
bb->base = base;
bb->type = type;
ret = 0;
}
out:
if (ret < 0) {
kfree(bb);
*bb_ret = NULL;
} else {
*bb_ret = bb;
}
return ret;
}
/*
* Block bitmaps start with two flag words that indicate if logical
* words are all 0s, all 1s, or a mix of set and clear bits stored in
* the item payload. Typically the allocators will have long runs of
* set or clear bits so we don't store most of the bitmaps. Badly
* fragmented allocators will be (2/64 = ~3%) larger.
*/
static int pack_block_bitmap(struct scoutfs_packed_bitmap *pb,
struct block_bitmap *bb)
{
__le64 word;
u64 present = 0;
u64 set = 0;
u64 b;
int w;
int i;
w = 0;
for (i = 0, b = 1; i < SCOUTFS_PACKED_BITMAP_WORDS; i++, b <<= 1) {
word = long_bits_to_le64(bb->bits, i);
if (word == cpu_to_le64(~0ULL)) {
set |= b;
} else if (word != 0) {
present |= b;
pb->words[w++] = word;
}
}
pb->set = cpu_to_le64(set);
pb->present = cpu_to_le64(present);
return offsetof(struct scoutfs_packed_bitmap, words[w]);
}
static int store_block_bitmap(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_btree_root *root,
struct block_bitmap *bb)
{
struct scoutfs_block_bitmap_key bbk;
struct scoutfs_packed_bitmap *pb;
int size;
int ret;
bbk.type = bb->type;
bbk.base = cpu_to_be64(bb->base);
if (bitmap_empty(bb->bits, SCOUTFS_BLOCK_BITMAP_BITS)) {
if (!bb->exists) {
ret = 0;
goto out;
}
ret = scoutfs_btree_delete(sb, alloc, wri, root,
&bbk, sizeof(bbk));
} else {
pb = kmalloc(SCOUTFS_PACKED_BITMAP_MAX_BYTES, GFP_NOFS);
if (!pb) {
ret = -ENOMEM;
goto out;
}
size = pack_block_bitmap(pb, bb);
ret = scoutfs_btree_force(sb, alloc, wri, root,
&bbk, sizeof(bbk), pb, size);
kfree(pb);
if (ret == 0)
bb->exists = true;
}
out:
return ret;
}
/*
* Set a region of bitmaps which must fit in one item. The caller's
* blkno is translated to an item base and then the number of bits are
* set. The caller is specifying a number of bits to set, not a block
* extent.
*/
static int set_block_bits(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_btree_root *root, u8 type, u64 blkno,
int nbits)
{
struct block_bitmap *bb = NULL;
u64 base;
int bit;
int ret;
if (WARN_ON_ONCE(!block_bitmap_fits(blkno, nbits)))
return -EINVAL;
ret = load_block_bitmap(sb, root, blkno, type, false, true, &bb);
if (ret < 0)
goto out;
block_bitmap_bit(&base, &bit, blkno, type);
bitmap_set(bb->bits, bit, nbits);
/* if a little bitmap is full, set it's big and delete it */
if (type == SCOUTFS_BLOCK_BITMAP_LITTLE &&
bitmap_full(bb->bits, SCOUTFS_PACKED_BITMAP_BITS)) {
ret = set_block_bits(sb, alloc, wri, root,
SCOUTFS_BLOCK_BITMAP_BIG, blkno, 1);
if (ret < 0)
goto out;
bitmap_zero(bb->bits, SCOUTFS_PACKED_BITMAP_BITS);
}
ret = store_block_bitmap(sb, alloc, wri, root, bb);
BUG_ON(ret < 0); /* cleared bit out of sync with existing littles */
out:
kfree(bb);
return ret;
}
/*
* Find a region of free blocks for the caller. The caller can ask for
* an arbitrarily large extent but we'll only return at most a bitmap's
* worth of blocks from one allocation.
*
* Big bitmap items are stored before little items. This let's large
* allocations naturally fall back to being satisfied by little items
* when there are no more remaining big items. Small allocations first
* look for little items and then search again for big items that they
* can break up.
*
* We always simply look for the first free region. This is operating
* in the client on trees whose items are populated by the server
* between each transaction. The server is responsible for distributing
* the items such that the client tends to allocate across the device
* over time.
*/
static int alloc_blocks(struct super_block *sb, u64 count, u64 *blkno_ret,
u64 *count_ret)
{
DECLARE_DATA_INFO(sb, datinf);
struct scoutfs_balloc_root *broot = &datinf->data_alloc;
struct block_bitmap *bb = NULL;
u64 blkno;
u8 type;
int bit;
int end;
int ret;
if (WARN_ON_ONCE(count == 0))
return -EINVAL;
/* will only allocate from one block bitmap item at a time */
count = min_t(u64, count, SCOUTFS_BLOCK_BITMAP_BITS);
/* small allocations first look for little items, then check big */
if (count < SCOUTFS_BLOCK_BITMAP_BITS)
type = SCOUTFS_BLOCK_BITMAP_LITTLE;
else
type = SCOUTFS_BLOCK_BITMAP_BIG;
do {
ret = load_block_bitmap(sb, &broot->root, 0, type,
true, false, &bb);
} while ((ret == -ENOENT && type == SCOUTFS_BLOCK_BITMAP_LITTLE) &&
(type = SCOUTFS_BLOCK_BITMAP_BIG, 1));
if (ret < 0) {
if (ret == -ENOENT)
ret = -ENOSPC;
goto out;
}
bit = find_first_bit(bb->bits, SCOUTFS_BLOCK_BITMAP_BITS);
if (WARN_ON_ONCE(bit >= SCOUTFS_BLOCK_BITMAP_BITS)) {
ret = -EIO; /* stored items should have bits set */
goto out;
}
blkno = block_bitmap_blkno(bb->base, bit, bb->type);
if (bb->type == SCOUTFS_BLOCK_BITMAP_BIG) {
/* set remaining little bits if using big for partial small */
if (count != SCOUTFS_BLOCK_BITMAP_BITS) {
ret = set_block_bits(sb, datinf->alloc, datinf->wri,
&broot->root,
SCOUTFS_BLOCK_BITMAP_LITTLE,
blkno + count,
SCOUTFS_BLOCK_BITMAP_BITS - count);
if (ret < 0)
goto out;
}
clear_bit(bit, bb->bits);
} else {
end = find_next_zero_bit(bb->bits, SCOUTFS_BLOCK_BITMAP_BITS,
bit + 1);
end = min(end, SCOUTFS_BLOCK_BITMAP_BITS); /* catch > size */
count = min_t(u64, count, end - bit);
bitmap_clear(bb->bits, bit, count);
}
ret = store_block_bitmap(sb, datinf->alloc, datinf->wri,
&broot->root, bb);
BUG_ON(ret < 0); /* little partial out of sync with big */
le64_add_cpu(&broot->total_free, -count);
*blkno_ret = blkno;
*count_ret = count;
trace_scoutfs_data_alloc_blocks(sb, broot, bb->base, bb->type, bit,
blkno, count);
out:
kfree(bb);
return ret;
}
/*
* Set free block bits in the block bitmaps and update the root's
* total_free count. The caller can specifiy the root so that this can
* be used both to free used allocations as well as to return unused
* allocations in error paths. The caller must ensure that the block
* regions fit in a single block bitmap (by for the blocks in an
* extent).
*/
static int free_blocks(struct super_block *sb,
struct scoutfs_balloc_root *broot, u64 blkno, u64 count)
{
DECLARE_DATA_INFO(sb, datinf);
int ret;
if (count == SCOUTFS_BLOCK_BITMAP_BITS)
ret = set_block_bits(sb, datinf->alloc, datinf->wri,
&broot->root, SCOUTFS_BLOCK_BITMAP_BIG,
blkno, 1);
else
ret = set_block_bits(sb, datinf->alloc, datinf->wri,
&broot->root, SCOUTFS_BLOCK_BITMAP_LITTLE,
blkno, count);
if (ret == 0) {
le64_add_cpu(&broot->total_free, count);
trace_scoutfs_data_free_blocks(sb, broot, blkno, count);
}
return ret;
}
/*
* Ensure that the destination free block bitmap tree has the minimum
* total free blocks by moving bits from the source tree. It will first
* try to find big bits starting at the cursor but will fall back to
* little bits after having wrapped the cursor.
*
* This will move all the items from the source to the destination if
* that's what it takes to reach the minimum.
*
* This is called by the server which provides its writer and metadata
* allocation contexts. It has locked the two allocation trees that
* will be modified.
*/
int scoutfs_data_move_alloc_bits(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_balloc_root *dst,
struct scoutfs_balloc_root *src,
__le64 *cursor, u64 min_dst_total)
{
struct block_bitmap *sbb = NULL;
struct block_bitmap *dbb = NULL;
u64 needed;
u64 blocks;
u64 moved;
u64 blkno;
u64 base;
u64 curs;
u8 type;
int nbits;
int bit;
int end;
int ret = 0;
/* start moving big bitmap items */
type = SCOUTFS_BLOCK_BITMAP_BIG;
curs = le64_to_cpup(cursor);
while (le64_to_cpu(dst->total_free) < min_dst_total) {
/* find the next source bitmap item with bits to move */
kfree(sbb);
ret = load_block_bitmap(sb, &src->root, curs, type,
true, false, &sbb);
if (ret == 0 && sbb->type != type)
ret = -ENOENT;
if (ret < 0) {
if (ret == -ENOENT) {
if (curs > 0) {
curs = 0;
continue;
}
if (type == SCOUTFS_BLOCK_BITMAP_BIG) {
type = SCOUTFS_BLOCK_BITMAP_LITTLE;
curs = le64_to_cpup(cursor);
continue;
}
ret = -ENOSPC;
}
break;
}
/* load the destination bitmap */
blkno = block_bitmap_blkno(sbb->base, 0, type);
kfree(dbb);
ret = load_block_bitmap(sb, &dst->root, blkno, type,
false, true, &dbb);
if (ret < 0)
break;
/* figure out how many bits to move, can overshoot */
needed = min_dst_total - le64_to_cpu(dst->total_free);
if (type == SCOUTFS_BLOCK_BITMAP_BIG) {
needed = (needed + SCOUTFS_BLOCK_BITMAP_BITS - 1)
>> SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
}
/* start searching from the cursor if within item */
if (curs > blkno)
blkno = curs;
block_bitmap_bit(&base, &bit, blkno, type);
moved = 0;
while (moved < needed) {
bit = find_next_bit(sbb->bits,
SCOUTFS_BLOCK_BITMAP_BITS, bit);
if (bit >= SCOUTFS_BLOCK_BITMAP_BITS)
break;
end = find_next_zero_bit(sbb->bits,
SCOUTFS_BLOCK_BITMAP_BITS,
bit + 1);
end = min(end, SCOUTFS_BLOCK_BITMAP_BITS);
nbits = min_t(u64, needed - moved, end - bit);
bitmap_clear(sbb->bits, bit, nbits);
bitmap_set(dbb->bits, bit, nbits);
curs = block_bitmap_blkno(dbb->base, bit + nbits, type);
moved += nbits;
}
ret = store_block_bitmap(sb, alloc, wri, &dst->root, dbb);
if (ret < 0)
break;
ret = store_block_bitmap(sb, alloc, wri, &src->root, sbb);
BUG_ON(ret); /* inconsistent src/dst, save orig src */
blocks = moved;
if (sbb->type == SCOUTFS_BLOCK_BITMAP_BIG)
blocks <<= SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
le64_add_cpu(&dst->total_free, blocks);
le64_add_cpu(&src->total_free, -blocks);
*cursor = cpu_to_le64(curs);
}
kfree(sbb);
kfree(dbb);
return ret;
}
/*
* The server caller is making their way through free data blocks
* initializing free block bitmap bits for the first time. This is the
* only mechanism that initializes free block bitmap items so we know
* that we never have to merge with existing items as long as we always
* write a full item.
*
* The caller gives us the fully extent of blknos that we could
* initialize and we figure out the size of the largest item and its
* bits which cover the start of the extent. We can set big bits if the
* extent is aligned to a small bitmap and is large enough.
*/
int scoutfs_data_add_free_blocks(struct super_block *sb,
struct scoutfs_balloc_allocator *alloc,
struct scoutfs_block_writer *wri,
struct scoutfs_balloc_root *broot,
u64 blkno, u64 count)
{
u64 base;
u8 type;
int nbits;
int bit;
int ret;
type = SCOUTFS_BLOCK_BITMAP_LITTLE;
block_bitmap_bit(&base, &bit, blkno, type);
if (bit == 0 && count >= SCOUTFS_BLOCK_BITMAP_BITS) {
type = SCOUTFS_BLOCK_BITMAP_BIG;
block_bitmap_bit(&base, &bit, blkno, type);
nbits = min_t(u64, count >> SCOUTFS_BLOCK_BITMAP_BASE_SHIFT,
SCOUTFS_BLOCK_BITMAP_BITS - bit);
count = (u64)nbits << SCOUTFS_BLOCK_BITMAP_BASE_SHIFT;
} else {
nbits = min_t(u64, count, SCOUTFS_BLOCK_BITMAP_BITS - bit);
count = nbits;
}
ret = set_block_bits(sb, alloc, wri, &broot->root, type, blkno, nbits);
if (ret == 0) {
le64_add_cpu(&broot->total_free, count);
ret = count;
}
return ret;
}
#endif
/*
* Find and remove or mark offline the block mappings that intersect
* with the caller's range. The caller is responsible for transactions