diff --git a/kmod/src/alloc.c b/kmod/src/alloc.c index 3f0c6aaa..1d370dda 100644 --- a/kmod/src/alloc.c +++ b/kmod/src/alloc.c @@ -657,6 +657,60 @@ out: return ret; } +void scoutfs_dalloc_init(struct scoutfs_data_alloc *dalloc, + struct scoutfs_alloc_root *data_avail) +{ + dalloc->root = *data_avail; + memset(&dalloc->cached, 0, sizeof(dalloc->cached)); + atomic64_set(&dalloc->total_len, le64_to_cpu(dalloc->root.total_len)); +} + +void scoutfs_dalloc_get_root(struct scoutfs_data_alloc *dalloc, + struct scoutfs_alloc_root *data_avail) +{ + *data_avail = dalloc->root; +} + +static void dalloc_update_total_len(struct scoutfs_data_alloc *dalloc) +{ + atomic64_set(&dalloc->total_len, le64_to_cpu(dalloc->root.total_len) + + dalloc->cached.len); +} + +u64 scoutfs_dalloc_total_len(struct scoutfs_data_alloc *dalloc) +{ + return atomic64_read(&dalloc->total_len); +} + +/* + * Return the current in-memory cached free extent to extent items in + * the avail root. This should be locked by the caller just like + * _alloc_data and _free_data. + */ +int scoutfs_dalloc_return_cached(struct super_block *sb, + struct scoutfs_alloc *alloc, + struct scoutfs_block_writer *wri, + struct scoutfs_data_alloc *dalloc) +{ + struct alloc_ext_args args = { + .alloc = alloc, + .wri = wri, + .root = &dalloc->root, + .type = SCOUTFS_FREE_EXTENT_BLKNO_TYPE, + }; + int ret = 0; + + if (dalloc->cached.len) { + ret = scoutfs_ext_insert(sb, &alloc_ext_ops, &args, + dalloc->cached.start, + dalloc->cached.len, 0, 0); + if (ret == 0) + memset(&dalloc->cached, 0, sizeof(dalloc->cached)); + } + + return ret; +} + /* * Allocate a data extent. An extent that's smaller than the requested * size can be returned. @@ -671,14 +725,13 @@ out: */ int scoutfs_alloc_data(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, - struct scoutfs_alloc_root *root, - struct scoutfs_extent *cached, u64 count, + struct scoutfs_data_alloc *dalloc, u64 count, u64 *blkno_ret, u64 *count_ret) { struct alloc_ext_args args = { .alloc = alloc, .wri = wri, - .root = root, + .root = &dalloc->root, .type = SCOUTFS_FREE_EXTENT_LEN_TYPE, }; struct scoutfs_extent ext; @@ -699,20 +752,21 @@ int scoutfs_alloc_data(struct super_block *sb, struct scoutfs_alloc *alloc, } /* smaller allocations come from a cached extent */ - if (cached->len == 0) { + if (dalloc->cached.len == 0) { ret = scoutfs_ext_alloc(sb, &alloc_ext_ops, &args, 0, 0, - SCOUTFS_ALLOC_DATA_LG_THRESH, cached); + SCOUTFS_ALLOC_DATA_LG_THRESH, + &dalloc->cached); if (ret < 0) goto out; } - len = min(count, cached->len); + len = min(count, dalloc->cached.len); - *blkno_ret = cached->start; + *blkno_ret = dalloc->cached.start; *count_ret = len; - cached->start += len; - cached->len -= len; + dalloc->cached.start += len; + dalloc->cached.len -= len; ret = 0; out: if (ret < 0) { @@ -720,6 +774,8 @@ out: ret = -ENOSPC; *blkno_ret = 0; *count_ret = 0; + } else { + dalloc_update_total_len(dalloc); } scoutfs_inc_counter(sb, alloc_alloc_data); diff --git a/kmod/src/alloc.h b/kmod/src/alloc.h index da8686f7..8abf729c 100644 --- a/kmod/src/alloc.h +++ b/kmod/src/alloc.h @@ -80,6 +80,18 @@ struct scoutfs_alloc { struct scoutfs_alloc_list_head freed; }; +/* + * A run-time data allocator. We have a cached extent in memory that is + * a lot cheaper to work with than the extent items, and we have a + * consistent record of the total_len that can be sampled outside of the + * usual heavy serialization of the extent modifications. + */ +struct scoutfs_data_alloc { + struct scoutfs_alloc_root root; + struct scoutfs_extent cached; + atomic64_t total_len; +}; + void scoutfs_alloc_init(struct scoutfs_alloc *alloc, struct scoutfs_alloc_list_head *avail, struct scoutfs_alloc_list_head *freed); @@ -92,10 +104,18 @@ int scoutfs_alloc_meta(struct super_block *sb, struct scoutfs_alloc *alloc, int scoutfs_free_meta(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, u64 blkno); +void scoutfs_dalloc_init(struct scoutfs_data_alloc *dalloc, + struct scoutfs_alloc_root *data_avail); +void scoutfs_dalloc_get_root(struct scoutfs_data_alloc *dalloc, + struct scoutfs_alloc_root *data_avail); +u64 scoutfs_dalloc_total_len(struct scoutfs_data_alloc *dalloc); +int scoutfs_dalloc_return_cached(struct super_block *sb, + struct scoutfs_alloc *alloc, + struct scoutfs_block_writer *wri, + struct scoutfs_data_alloc *dalloc); int scoutfs_alloc_data(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, - struct scoutfs_alloc_root *root, - struct scoutfs_extent *cached, u64 count, + struct scoutfs_data_alloc *dalloc, u64 count, u64 *blkno_ret, u64 *count_ret); int scoutfs_free_data(struct super_block *sb, struct scoutfs_alloc *alloc, struct scoutfs_block_writer *wri, diff --git a/kmod/src/data.c b/kmod/src/data.c index c518e16f..6e110e3a 100644 --- a/kmod/src/data.c +++ b/kmod/src/data.c @@ -53,9 +53,8 @@ struct data_info { struct mutex mutex; struct scoutfs_alloc *alloc; struct scoutfs_block_writer *wri; - struct scoutfs_alloc_root data_avail; struct scoutfs_alloc_root data_freed; - struct scoutfs_extent cached_ext; + struct scoutfs_data_alloc dalloc; }; #define DECLARE_DATA_INFO(sb, name) \ @@ -432,8 +431,7 @@ static int alloc_block(struct super_block *sb, struct inode *inode, count = 1; ret = scoutfs_alloc_data(sb, datinf->alloc, datinf->wri, - &datinf->data_avail, &datinf->cached_ext, - count, &blkno, &count); + &datinf->dalloc, count, &blkno, &count); if (ret < 0) goto out; @@ -916,9 +914,8 @@ static s64 fallocate_extents(struct super_block *sb, struct inode *inode, mutex_lock(&datinf->mutex); ret = scoutfs_alloc_data(sb, datinf->alloc, datinf->wri, - &datinf->data_avail, - &datinf->cached_ext, - count, &blkno, &count); + &datinf->dalloc, count, + &blkno, &count); if (ret == 0) { ret = scoutfs_ext_set(sb, &data_ext_ops, &args, iblock, count, blkno, @@ -926,7 +923,7 @@ static s64 fallocate_extents(struct super_block *sb, struct inode *inode, if (ret < 0) { err = scoutfs_free_data(sb, datinf->alloc, datinf->wri, - &datinf->data_avail, + &datinf->data_freed, blkno, count); BUG_ON(err); /* inconsistent */ } @@ -1532,7 +1529,7 @@ void scoutfs_data_init_btrees(struct super_block *sb, datinf->alloc = alloc; datinf->wri = wri; - datinf->data_avail = lt->data_avail; + scoutfs_dalloc_init(&datinf->dalloc, <->data_avail); datinf->data_freed = lt->data_freed; mutex_unlock(&datinf->mutex); @@ -1545,7 +1542,7 @@ void scoutfs_data_get_btrees(struct super_block *sb, mutex_lock(&datinf->mutex); - lt->data_avail = datinf->data_avail; + scoutfs_dalloc_get_root(&datinf->dalloc, <->data_avail); lt->data_freed = datinf->data_freed; mutex_unlock(&datinf->mutex); @@ -1561,31 +1558,20 @@ int scoutfs_data_prepare_commit(struct super_block *sb) int ret; mutex_lock(&datinf->mutex); - if (datinf->cached_ext.len) { - ret = scoutfs_free_data(sb, datinf->alloc, datinf->wri, - &datinf->data_avail, - datinf->cached_ext.start, - datinf->cached_ext.len); - if (ret == 0) - memset(&datinf->cached_ext, 0, - sizeof(datinf->cached_ext)); - } else { - ret = 0; - } + ret = scoutfs_dalloc_return_cached(sb, datinf->alloc, datinf->wri, + &datinf->dalloc); mutex_unlock(&datinf->mutex); return ret; } -/* - * This isn't serializing with allocators so it can be a bit racey. - */ u64 scoutfs_data_alloc_free_bytes(struct super_block *sb) { DECLARE_DATA_INFO(sb, datinf); - return le64_to_cpu(datinf->data_avail.total_len) << - SCOUTFS_BLOCK_SM_SHIFT; + return scoutfs_dalloc_total_len(&datinf->dalloc) << + SCOUTFS_BLOCK_SM_SHIFT; + } int scoutfs_data_setup(struct super_block *sb)