mirror of
https://github.com/torvalds/linux.git
synced 2024-11-11 06:31:49 +00:00
ext4: Fix best extent lstart adjustment logic in ext4_mb_new_inode_pa()
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: Jan Kara <jack@suse.cz> Signed-off-by: Ojaswin Mujoo <ojaswin@linux.ibm.com> Reviewed-by: Ritesh Harjani (IBM) <ritesh.list@gmail.com> Reviewed-by: Jan Kara <jack@suse.cz> Link: https://lore.kernel.org/r/f96aca6d415b36d1f90db86c1a8cd7e2e9d7ab0e.1679731817.git.ojaswin@linux.ibm.com Signed-off-by: Theodore Ts'o <tytso@mit.edu>
This commit is contained in:
parent
0830344c95
commit
93cdf49f6e
@ -4329,6 +4329,7 @@ static void ext4_mb_use_inode_pa(struct ext4_allocation_context *ac,
|
|||||||
BUG_ON(start < pa->pa_pstart);
|
BUG_ON(start < pa->pa_pstart);
|
||||||
BUG_ON(end > pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len));
|
BUG_ON(end > pa->pa_pstart + EXT4_C2B(sbi, pa->pa_len));
|
||||||
BUG_ON(pa->pa_free < len);
|
BUG_ON(pa->pa_free < len);
|
||||||
|
BUG_ON(ac->ac_b_ex.fe_len <= 0);
|
||||||
pa->pa_free -= len;
|
pa->pa_free -= len;
|
||||||
|
|
||||||
mb_debug(ac->ac_sb, "use %llu/%d from inode pa %p\n", start, len, pa);
|
mb_debug(ac->ac_sb, "use %llu/%d from inode pa %p\n", start, len, pa);
|
||||||
@ -4667,10 +4668,8 @@ ext4_mb_new_inode_pa(struct ext4_allocation_context *ac)
|
|||||||
pa = ac->ac_pa;
|
pa = ac->ac_pa;
|
||||||
|
|
||||||
if (ac->ac_b_ex.fe_len < ac->ac_g_ex.fe_len) {
|
if (ac->ac_b_ex.fe_len < ac->ac_g_ex.fe_len) {
|
||||||
int winl;
|
int new_bex_start;
|
||||||
int wins;
|
int new_bex_end;
|
||||||
int win;
|
|
||||||
int offs;
|
|
||||||
|
|
||||||
/* we can't allocate as much as normalizer wants.
|
/* we can't allocate as much as normalizer wants.
|
||||||
* so, found space must get proper lstart
|
* so, found space must get proper lstart
|
||||||
@ -4678,26 +4677,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_logical > ac->ac_o_ex.fe_logical);
|
||||||
BUG_ON(ac->ac_g_ex.fe_len < ac->ac_o_ex.fe_len);
|
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
|
* Use the below logic for adjusting best extent as it keeps
|
||||||
* winl is window we can move our chunk within */
|
* fragmentation in check while ensuring logical range of best
|
||||||
winl = ac->ac_o_ex.fe_logical - ac->ac_g_ex.fe_logical;
|
* 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;
|
||||||
|
|
||||||
/* also, we should cover whole original request */
|
new_bex_start = ac->ac_g_ex.fe_logical;
|
||||||
wins = EXT4_C2B(sbi, ac->ac_b_ex.fe_len - ac->ac_o_ex.fe_len);
|
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;
|
||||||
|
|
||||||
/* the smallest one defines real window */
|
new_bex_start = ac->ac_o_ex.fe_logical;
|
||||||
win = min(winl, wins);
|
new_bex_end =
|
||||||
|
new_bex_start + EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
|
||||||
|
|
||||||
offs = ac->ac_o_ex.fe_logical %
|
adjust_bex:
|
||||||
EXT4_C2B(sbi, ac->ac_b_ex.fe_len);
|
ac->ac_b_ex.fe_logical = new_bex_start;
|
||||||
if (offs && offs < win)
|
|
||||||
win = offs;
|
|
||||||
|
|
||||||
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_logical < ac->ac_b_ex.fe_logical);
|
||||||
BUG_ON(ac->ac_o_ex.fe_len > ac->ac_b_ex.fe_len);
|
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)));
|
||||||
}
|
}
|
||||||
|
|
||||||
pa->pa_lstart = ac->ac_b_ex.fe_logical;
|
pa->pa_lstart = ac->ac_b_ex.fe_logical;
|
||||||
|
Loading…
Reference in New Issue
Block a user