Commit 831a155d authored by Ojaswin Mujoo's avatar Ojaswin Mujoo Committed by Baokun Li
Browse files

ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa()

stable inclusion
from stable-v5.10.181
commit fc7237e191b99f88e859316fab2b06c2c26c8344
category: bugfix
bugzilla: https://gitee.com/openeuler/kernel/issues/I8PI1H
CVE: NA

Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=fc7237e191b99f88e859316fab2b06c2c26c8344

--------------------------------

[ Upstream commit 93cdf49f ]

When the length of best extent found is less than the length of goal extent
we need to make sure that the best extent atleast covers the start of the
original request. This is done by adjusting the ac_b_ex.fe_logical (logical
start) of the extent.

While doing so, the current logic sometimes results in the best extent's
logical range overflowing the goal extent. Since this best extent is later
added to the inode preallocation list, we have a possibility of introducing
overlapping preallocations. This is discussed in detail here [1].

As per Jan's suggestion, to fix this, replace the existing logic with the
below logic for adjusting best extent as it keeps fragmentation in check
while ensuring logical range of best extent doesn't overflow out of goal
extent:

1. Check if best extent can be kept at end of goal range and still cover
   original start.
2. Else, check if best extent can be kept at start of goal range and still
   cover original start.
3. Else, keep the best extent at start of original request.

Also, add a few extra BUG_ONs that might help catch errors faster.

[1] https://lore.kernel.org/r/Y+OGkVvzPN0RMv0O@li-bb2b2a4c-3307-11b2-a85c-8fa5c3a69313.ibm.com



Suggested-by: default avatarJan Kara <jack@suse.cz>
Signed-off-by: default avatarOjaswin Mujoo <ojaswin@linux.ibm.com>
Reviewed-by: default avatarRitesh Harjani (IBM) <ritesh.list@gmail.com>
Reviewed-by: default avatarJan Kara <jack@suse.cz>
Link: https://lore.kernel.org/r/f96aca6d415b36d1f90db86c1a8cd7e2e9d7ab0e.1679731817.git.ojaswin@linux.ibm.com


Signed-off-by: default avatarTheodore Ts'o <tytso@mit.edu>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
Signed-off-by: default avatarBaokun Li <libaokun1@huawei.com>
parent 3988d4f8
Loading
Loading
Loading
Loading
+31 −18
Original line number Diff line number Diff line
@@ -3737,6 +3737,7 @@ static void ext4_mb_use_inode_pa(struct ext4_allocation_context *ac,
	BUG_ON(start < pa->pa_pstart);
	BUG_ON(end > pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len));
	BUG_ON(pa->pa_free < len);
	BUG_ON(ac->ac_b_ex.fe_len <= 0);
	pa->pa_free -= len;

	mb_debug(ac->ac_sb, "use %llu/%d from inode pa %p\n", start, len, pa);
@@ -4061,10 +4062,8 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac)
	pa = ac->ac_pa;

	if (ac->ac_b_ex.fe_len < ac->ac_g_ex.fe_len) {
		int winl;
		int wins;
		int win;
		int offs;
		int new_bex_start;
		int new_bex_end;

		/* we can't allocate as much as normalizer wants.
		 * so, found space must get proper lstart
@@ -4072,26 +4071,40 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac)
		BUG_ON(ac->ac_g_ex.fe_logical > ac->ac_o_ex.fe_logical);
		BUG_ON(ac->ac_g_ex.fe_len < ac->ac_o_ex.fe_len);

		/* we're limited by original request in that
		 * logical block must be covered any way
		 * winl is window we can move our chunk within */
		winl = ac->ac_o_ex.fe_logical - ac->ac_g_ex.fe_logical;

		/* also, we should cover whole original request */
		wins = EXT4_C2B(sbi, ac->ac_b_ex.fe_len - ac->ac_o_ex.fe_len);

		/* the smallest one defines real window */
		win = min(winl, wins);

		offs = ac->ac_o_ex.fe_logical %
			EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
		if (offs && offs < win)
			win = offs;
		/*
		 * Use the below logic for adjusting best extent as it keeps
		 * fragmentation in check while ensuring logical range of best
		 * extent doesn't overflow out of goal extent:
		 *
		 * 1. Check if best ex can be kept at end of goal and still
		 *    cover original start
		 * 2. Else, check if best ex can be kept at start of goal and
		 *    still cover original start
		 * 3. Else, keep the best ex at start of original request.
		 */
		new_bex_end = ac->ac_g_ex.fe_logical +
			EXT4_C2B(sbi, ac->ac_g_ex.fe_len);
		new_bex_start = new_bex_end - EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
		if (ac->ac_o_ex.fe_logical >= new_bex_start)
			goto adjust_bex;

		new_bex_start = ac->ac_g_ex.fe_logical;
		new_bex_end =
			new_bex_start + EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
		if (ac->ac_o_ex.fe_logical < new_bex_end)
			goto adjust_bex;

		new_bex_start = ac->ac_o_ex.fe_logical;
		new_bex_end =
			new_bex_start + EXT4_C2B(sbi, ac->ac_b_ex.fe_len);

adjust_bex:
		ac->ac_b_ex.fe_logical = new_bex_start;

		ac->ac_b_ex.fe_logical = ac->ac_o_ex.fe_logical -
			EXT4_NUM_B2C(sbi, win);
		BUG_ON(ac->ac_o_ex.fe_logical < ac->ac_b_ex.fe_logical);
		BUG_ON(ac->ac_o_ex.fe_len > ac->ac_b_ex.fe_len);
		BUG_ON(new_bex_end > (ac->ac_g_ex.fe_logical +
				      EXT4_C2B(sbi, ac->ac_g_ex.fe_len)));
	}

	/* preallocation can change ac_b_ex, thus we store actually