[PATCH 05/10] ext4: count removed reserved blocks for delalloc only extent entry

From: Zhang Yi
Date: Fri May 31 2024 - 23:44:08 EST


From: Zhang Yi <yi.zhang@xxxxxxxxxx>

If bigalloc feature is enabled, __es_remove_extent() only counts
reserved clusters when removing delalloc extent entry, it doesn't count
reserved blocks. However, it's useful to distinguish whether we are
allocating blocks that contains a delalloc range in one cluster, so
let's count the reserved blocks number too.

Signed-off-by: Zhang Yi <yi.zhang@xxxxxxxxxx>
---
fs/ext4/extents_status.c | 64 +++++++++++++++++++++++++---------------
1 file changed, 40 insertions(+), 24 deletions(-)

diff --git a/fs/ext4/extents_status.c b/fs/ext4/extents_status.c
index e54e15eb05b9..ced9826682bb 100644
--- a/fs/ext4/extents_status.c
+++ b/fs/ext4/extents_status.c
@@ -141,13 +141,18 @@
* -- Extent-level locking
*/

+struct rsvd_info {
+ int delonly_cluster; /* reserved clusters for delalloc es entry */
+ int delonly_block; /* reserved blocks for delalloc es entry */
+};
+
static struct kmem_cache *ext4_es_cachep;
static struct kmem_cache *ext4_pending_cachep;

static int __es_insert_extent(struct inode *inode, struct extent_status *newes,
struct extent_status *prealloc);
static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
- ext4_lblk_t end, int *reserved,
+ ext4_lblk_t end, struct rsvd_info *rinfo,
struct extent_status *prealloc);
static int es_reclaim_extents(struct ext4_inode_info *ei, int *nr_to_scan);
static int __es_shrink(struct ext4_sb_info *sbi, int nr_to_scan,
@@ -1042,7 +1047,8 @@ int ext4_es_lookup_extent(struct inode *inode, ext4_lblk_t lblk,
}

struct rsvd_count {
- int ndelonly;
+ int ndelonly_cluster;
+ int ndelonly_block;
bool first_do_lblk_found;
ext4_lblk_t first_do_lblk;
ext4_lblk_t last_do_lblk;
@@ -1068,7 +1074,8 @@ static void init_rsvd(struct inode *inode, ext4_lblk_t lblk,
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
struct rb_node *node;

- rc->ndelonly = 0;
+ rc->ndelonly_cluster = 0;
+ rc->ndelonly_block = 0;

/*
* for bigalloc, note the first delonly block in the range has not
@@ -1116,11 +1123,13 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len,
WARN_ON(len <= 0);

if (sbi->s_cluster_ratio == 1) {
- rc->ndelonly += (int) len;
+ rc->ndelonly_cluster += (int) len;
+ rc->ndelonly_block = rc->ndelonly_cluster;
return;
}

/* bigalloc */
+ rc->ndelonly_block += (int)len;

i = (lblk < es->es_lblk) ? es->es_lblk : lblk;
end = lblk + (ext4_lblk_t) len - 1;
@@ -1140,7 +1149,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len,
* doesn't start with it, count it and stop tracking
*/
if (rc->partial && (rc->lclu != EXT4_B2C(sbi, i))) {
- rc->ndelonly++;
+ rc->ndelonly_cluster++;
rc->partial = false;
}

@@ -1150,7 +1159,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len,
*/
if (EXT4_LBLK_COFF(sbi, i) != 0) {
if (end >= EXT4_LBLK_CFILL(sbi, i)) {
- rc->ndelonly++;
+ rc->ndelonly_cluster++;
rc->partial = false;
i = EXT4_LBLK_CFILL(sbi, i) + 1;
}
@@ -1162,7 +1171,7 @@ static void count_rsvd(struct inode *inode, ext4_lblk_t lblk, long len,
*/
if ((i + sbi->s_cluster_ratio - 1) <= end) {
nclu = (end - i + 1) >> sbi->s_cluster_bits;
- rc->ndelonly += nclu;
+ rc->ndelonly_cluster += nclu;
i += nclu << sbi->s_cluster_bits;
}

@@ -1242,9 +1251,9 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
if (sbi->s_cluster_ratio > 1) {
/* count any remaining partial cluster */
if (rc->partial)
- rc->ndelonly++;
+ rc->ndelonly_cluster++;

- if (rc->ndelonly == 0)
+ if (rc->ndelonly_cluster == 0)
return 0;

first_lclu = EXT4_B2C(sbi, rc->first_do_lblk);
@@ -1261,7 +1270,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
while (es && ext4_es_end(es) >=
EXT4_LBLK_CMASK(sbi, rc->first_do_lblk)) {
if (ext4_es_is_delonly(es)) {
- rc->ndelonly--;
+ rc->ndelonly_cluster--;
left_delonly = true;
break;
}
@@ -1281,7 +1290,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
while (es && es->es_lblk <=
EXT4_LBLK_CFILL(sbi, rc->last_do_lblk)) {
if (ext4_es_is_delonly(es)) {
- rc->ndelonly--;
+ rc->ndelonly_cluster--;
right_delonly = true;
break;
}
@@ -1327,7 +1336,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
if (count_pending) {
pr = __pr_tree_search(&tree->root, first_lclu);
while (pr && pr->lclu <= last_lclu) {
- rc->ndelonly--;
+ rc->ndelonly_cluster--;
node = rb_next(&pr->rb_node);
rb_erase(&pr->rb_node, &tree->root);
__free_pending(pr);
@@ -1338,7 +1347,7 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
}
}
}
- return rc->ndelonly;
+ return rc->ndelonly_cluster;
}


@@ -1348,16 +1357,17 @@ static unsigned int get_rsvd(struct inode *inode, ext4_lblk_t end,
* @inode - file containing range
* @lblk - first block in range
* @end - last block in range
- * @reserved - number of cluster reservations released
+ * @rinfo - reserved information collected, includes number of
+ * block/cluster reservations released
* @prealloc - pre-allocated es to avoid memory allocation failures
*
- * If @reserved is not NULL and delayed allocation is enabled, counts
+ * If @rinfo is not NULL and delayed allocation is enabled, counts
* block/cluster reservations freed by removing range and if bigalloc
* enabled cancels pending reservations as needed. Returns 0 on success,
* error code on failure.
*/
static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
- ext4_lblk_t end, int *reserved,
+ ext4_lblk_t end, struct rsvd_info *rinfo,
struct extent_status *prealloc)
{
struct ext4_es_tree *tree = &EXT4_I(inode)->i_es_tree;
@@ -1367,11 +1377,15 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
ext4_lblk_t len1, len2;
ext4_fsblk_t block;
int err = 0;
- bool count_reserved = true;
+ bool count_reserved = false;
struct rsvd_count rc;

- if (reserved == NULL || !test_opt(inode->i_sb, DELALLOC))
- count_reserved = false;
+ if (rinfo) {
+ rinfo->delonly_cluster = 0;
+ rinfo->delonly_block = 0;
+ if (test_opt(inode->i_sb, DELALLOC))
+ count_reserved = true;
+ }

es = __es_tree_search(&tree->root, lblk);
if (!es)
@@ -1469,8 +1483,10 @@ static int __es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
}

out_get_reserved:
- if (count_reserved)
- *reserved = get_rsvd(inode, end, es, &rc);
+ if (count_reserved) {
+ rinfo->delonly_cluster = get_rsvd(inode, end, es, &rc);
+ rinfo->delonly_block = rc.ndelonly_block;
+ }
out:
return err;
}
@@ -1489,8 +1505,8 @@ void ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
ext4_lblk_t len)
{
ext4_lblk_t end;
+ struct rsvd_info rinfo;
int err = 0;
- int reserved = 0;
struct extent_status *es = NULL;

if (EXT4_SB(inode->i_sb)->s_mount_state & EXT4_FC_REPLAY)
@@ -1515,7 +1531,7 @@ void ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
* is reclaimed.
*/
write_lock(&EXT4_I(inode)->i_es_lock);
- err = __es_remove_extent(inode, lblk, end, &reserved, es);
+ err = __es_remove_extent(inode, lblk, end, &rinfo, es);
/* Free preallocated extent if it didn't get used. */
if (es) {
if (!es->es_len)
@@ -1527,7 +1543,7 @@ void ext4_es_remove_extent(struct inode *inode, ext4_lblk_t lblk,
goto retry;

ext4_es_print_tree(inode);
- ext4_da_release_space(inode, reserved);
+ ext4_da_release_space(inode, rinfo.delonly_cluster);
return;
}

--
2.31.1