Skip to content

Commit 83448bd

Browse files
jankaratytso
authored andcommitted
ext4: Reserve revoke credits for freed blocks
So far we have reserved only relatively high fixed amount of revoke credits for each transaction. We over-reserved by large amount for most cases but when freeing large directories or files with data journalling, the fixed amount is not enough. In fact the worst case estimate is inconveniently large (maximum extent size) for freeing of one extent. We fix this by doing proper estimate of the amount of blocks that need to be revoked when removing blocks from the inode due to truncate or hole punching and otherwise reserve just a small amount of revoke credits for each transaction to accommodate freeing of xattrs block or so. Signed-off-by: Jan Kara <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]>
1 parent d090707 commit 83448bd

File tree

11 files changed

+140
-67
lines changed

11 files changed

+140
-67
lines changed

fs/ext4/ext4.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3296,7 +3296,8 @@ extern int ext4_swap_extents(handle_t *handle, struct inode *inode1,
32963296
int mark_unwritten,int *err);
32973297
extern int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu);
32983298
extern int ext4_datasem_ensure_credits(handle_t *handle, struct inode *inode,
3299-
int check_cred, int restart_cred);
3299+
int check_cred, int restart_cred,
3300+
int revoke_cred);
33003301

33013302

33023303
/* move_extent.c */

fs/ext4/ext4_jbd2.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,21 +65,23 @@ static int ext4_journal_check_start(struct super_block *sb)
6565
}
6666

6767
handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,
68-
int type, int blocks, int rsv_blocks)
68+
int type, int blocks, int rsv_blocks,
69+
int revoke_creds)
6970
{
7071
journal_t *journal;
7172
int err;
7273

73-
trace_ext4_journal_start(sb, blocks, rsv_blocks, _RET_IP_);
74+
trace_ext4_journal_start(sb, blocks, rsv_blocks, revoke_creds,
75+
_RET_IP_);
7476
err = ext4_journal_check_start(sb);
7577
if (err < 0)
7678
return ERR_PTR(err);
7779

7880
journal = EXT4_SB(sb)->s_journal;
7981
if (!journal)
8082
return ext4_get_nojournal();
81-
return jbd2__journal_start(journal, blocks, rsv_blocks, 1024, GFP_NOFS,
82-
type, line);
83+
return jbd2__journal_start(journal, blocks, rsv_blocks, revoke_creds,
84+
GFP_NOFS, type, line);
8385
}
8486

8587
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle)
@@ -134,14 +136,16 @@ handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
134136
}
135137

136138
int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
137-
int extend_cred)
139+
int extend_cred, int revoke_cred)
138140
{
139141
if (!ext4_handle_valid(handle))
140142
return 0;
141-
if (jbd2_handle_buffer_credits(handle) >= check_cred)
143+
if (jbd2_handle_buffer_credits(handle) >= check_cred &&
144+
handle->h_revoke_credits >= revoke_cred)
142145
return 0;
143-
return ext4_journal_extend(handle,
144-
extend_cred - jbd2_handle_buffer_credits(handle));
146+
extend_cred = max(0, extend_cred - jbd2_handle_buffer_credits(handle));
147+
revoke_cred = max(0, revoke_cred - handle->h_revoke_credits);
148+
return ext4_journal_extend(handle, extend_cred, revoke_cred);
145149
}
146150

147151
static void ext4_journal_abort_handle(const char *caller, unsigned int line,

fs/ext4/ext4_jbd2.h

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,8 @@ int __ext4_handle_dirty_super(const char *where, unsigned int line,
261261
__ext4_handle_dirty_super(__func__, __LINE__, (handle), (sb))
262262

263263
handle_t *__ext4_journal_start_sb(struct super_block *sb, unsigned int line,
264-
int type, int blocks, int rsv_blocks);
264+
int type, int blocks, int rsv_blocks,
265+
int revoke_creds);
265266
int __ext4_journal_stop(const char *where, unsigned int line, handle_t *handle);
266267

267268
#define EXT4_NOJOURNAL_MAX_REF_COUNT ((unsigned long) 4096)
@@ -288,21 +289,41 @@ static inline int ext4_handle_is_aborted(handle_t *handle)
288289
return 0;
289290
}
290291

292+
static inline int ext4_free_metadata_revoke_credits(struct super_block *sb,
293+
int blocks)
294+
{
295+
/* Freeing each metadata block can result in freeing one cluster */
296+
return blocks * EXT4_SB(sb)->s_cluster_ratio;
297+
}
298+
299+
static inline int ext4_trans_default_revoke_credits(struct super_block *sb)
300+
{
301+
return ext4_free_metadata_revoke_credits(sb, 8);
302+
}
303+
291304
#define ext4_journal_start_sb(sb, type, nblocks) \
292-
__ext4_journal_start_sb((sb), __LINE__, (type), (nblocks), 0)
305+
__ext4_journal_start_sb((sb), __LINE__, (type), (nblocks), 0, \
306+
ext4_trans_default_revoke_credits(sb))
293307

294308
#define ext4_journal_start(inode, type, nblocks) \
295-
__ext4_journal_start((inode), __LINE__, (type), (nblocks), 0)
309+
__ext4_journal_start((inode), __LINE__, (type), (nblocks), 0, \
310+
ext4_trans_default_revoke_credits((inode)->i_sb))
296311

297-
#define ext4_journal_start_with_reserve(inode, type, blocks, rsv_blocks) \
298-
__ext4_journal_start((inode), __LINE__, (type), (blocks), (rsv_blocks))
312+
#define ext4_journal_start_with_reserve(inode, type, blocks, rsv_blocks)\
313+
__ext4_journal_start((inode), __LINE__, (type), (blocks), (rsv_blocks),\
314+
ext4_trans_default_revoke_credits((inode)->i_sb))
315+
316+
#define ext4_journal_start_with_revoke(inode, type, blocks, revoke_creds) \
317+
__ext4_journal_start((inode), __LINE__, (type), (blocks), 0, \
318+
(revoke_creds))
299319

300320
static inline handle_t *__ext4_journal_start(struct inode *inode,
301321
unsigned int line, int type,
302-
int blocks, int rsv_blocks)
322+
int blocks, int rsv_blocks,
323+
int revoke_creds)
303324
{
304325
return __ext4_journal_start_sb(inode->i_sb, line, type, blocks,
305-
rsv_blocks);
326+
rsv_blocks, revoke_creds);
306327
}
307328

308329
#define ext4_journal_stop(handle) \
@@ -325,22 +346,23 @@ static inline handle_t *ext4_journal_current_handle(void)
325346
return journal_current_handle();
326347
}
327348

328-
static inline int ext4_journal_extend(handle_t *handle, int nblocks)
349+
static inline int ext4_journal_extend(handle_t *handle, int nblocks, int revoke)
329350
{
330351
if (ext4_handle_valid(handle))
331-
return jbd2_journal_extend(handle, nblocks, 1024);
352+
return jbd2_journal_extend(handle, nblocks, revoke);
332353
return 0;
333354
}
334355

335-
static inline int ext4_journal_restart(handle_t *handle, int nblocks)
356+
static inline int ext4_journal_restart(handle_t *handle, int nblocks,
357+
int revoke)
336358
{
337359
if (ext4_handle_valid(handle))
338-
return jbd2__journal_restart(handle, nblocks, 1024, GFP_NOFS);
360+
return jbd2__journal_restart(handle, nblocks, revoke, GFP_NOFS);
339361
return 0;
340362
}
341363

342364
int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
343-
int extend_cred);
365+
int extend_cred, int revoke_cred);
344366

345367

346368
/*
@@ -353,18 +375,19 @@ int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
353375
* credits or transaction extension succeeded, 1 in case transaction had to be
354376
* restarted.
355377
*/
356-
#define ext4_journal_ensure_credits_fn(handle, check_cred, extend_cred, fn) \
378+
#define ext4_journal_ensure_credits_fn(handle, check_cred, extend_cred, \
379+
revoke_cred, fn) \
357380
({ \
358381
__label__ __ensure_end; \
359382
int err = __ext4_journal_ensure_credits((handle), (check_cred), \
360-
(extend_cred)); \
383+
(extend_cred), (revoke_cred)); \
361384
\
362385
if (err <= 0) \
363386
goto __ensure_end; \
364387
err = (fn); \
365388
if (err < 0) \
366389
goto __ensure_end; \
367-
err = ext4_journal_restart((handle), (extend_cred)); \
390+
err = ext4_journal_restart((handle), (extend_cred), (revoke_cred)); \
368391
if (err == 0) \
369392
err = 1; \
370393
__ensure_end: \
@@ -373,18 +396,16 @@ __ensure_end: \
373396

374397
/*
375398
* Ensure given handle has at least requested amount of credits available,
376-
* possibly restarting transaction if needed.
399+
* possibly restarting transaction if needed. We also make sure the transaction
400+
* has space for at least ext4_trans_default_revoke_credits(sb) revoke records
401+
* as freeing one or two blocks is very common pattern and requesting this is
402+
* very cheap.
377403
*/
378-
static inline int ext4_journal_ensure_credits(handle_t *handle, int credits)
404+
static inline int ext4_journal_ensure_credits(handle_t *handle, int credits,
405+
int revoke_creds)
379406
{
380-
return ext4_journal_ensure_credits_fn(handle, credits, credits, 0);
381-
}
382-
383-
static inline int ext4_journal_ensure_credits_batch(handle_t *handle,
384-
int credits)
385-
{
386-
return ext4_journal_ensure_credits_fn(handle, credits,
387-
EXT4_MAX_TRANS_DATA, 0);
407+
return ext4_journal_ensure_credits_fn(handle, credits, credits,
408+
revoke_creds, 0);
388409
}
389410

390411
static inline int ext4_journal_blocks_per_page(struct inode *inode)
@@ -479,6 +500,19 @@ static inline int ext4_should_writeback_data(struct inode *inode)
479500
return ext4_inode_journal_mode(inode) & EXT4_INODE_WRITEBACK_DATA_MODE;
480501
}
481502

503+
static inline int ext4_free_data_revoke_credits(struct inode *inode, int blocks)
504+
{
505+
if (test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA)
506+
return 0;
507+
if (!ext4_should_journal_data(inode))
508+
return 0;
509+
/*
510+
* Data blocks in one extent are contiguous, just account for partial
511+
* clusters at extent boundaries
512+
*/
513+
return blocks + 2*(EXT4_SB(inode->i_sb)->s_cluster_ratio - 1);
514+
}
515+
482516
/*
483517
* This function controls whether or not we should try to go down the
484518
* dioread_nolock code paths, which makes it safe to avoid taking

fs/ext4/extents.c

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,14 @@ static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
124124
* and < 0 in case of fatal error.
125125
*/
126126
int ext4_datasem_ensure_credits(handle_t *handle, struct inode *inode,
127-
int check_cred, int restart_cred)
127+
int check_cred, int restart_cred,
128+
int revoke_cred)
128129
{
129130
int ret;
130131
int dropped = 0;
131132

132133
ret = ext4_journal_ensure_credits_fn(handle, check_cred, restart_cred,
133-
ext4_ext_trunc_restart_fn(inode, &dropped));
134+
revoke_cred, ext4_ext_trunc_restart_fn(inode, &dropped));
134135
if (dropped)
135136
down_write(&EXT4_I(inode)->i_data_sem);
136137
return ret;
@@ -1851,7 +1852,8 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
18511852
* group descriptor to release the extent tree block. If we
18521853
* can't get the journal credits, give up.
18531854
*/
1854-
if (ext4_journal_extend(handle, 2))
1855+
if (ext4_journal_extend(handle, 2,
1856+
ext4_free_metadata_revoke_credits(inode->i_sb, 1)))
18551857
return;
18561858

18571859
/*
@@ -2738,7 +2740,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
27382740
{
27392741
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
27402742
int err = 0, correct_index = 0;
2741-
int depth = ext_depth(inode), credits;
2743+
int depth = ext_depth(inode), credits, revoke_credits;
27422744
struct ext4_extent_header *eh;
27432745
ext4_lblk_t a, b;
27442746
unsigned num;
@@ -2830,9 +2832,18 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
28302832
credits += (ext_depth(inode)) + 1;
28312833
}
28322834
credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
2835+
/*
2836+
* We may end up freeing some index blocks and data from the
2837+
* punched range. Note that partial clusters are accounted for
2838+
* by ext4_free_data_revoke_credits().
2839+
*/
2840+
revoke_credits =
2841+
ext4_free_metadata_revoke_credits(inode->i_sb,
2842+
ext_depth(inode)) +
2843+
ext4_free_data_revoke_credits(inode, b - a + 1);
28332844

28342845
err = ext4_datasem_ensure_credits(handle, inode, credits,
2835-
credits);
2846+
credits, revoke_credits);
28362847
if (err) {
28372848
if (err > 0)
28382849
err = -EAGAIN;
@@ -2963,7 +2974,9 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
29632974
ext_debug("truncate since %u to %u\n", start, end);
29642975

29652976
/* probably first extent we're gonna free will be last in block */
2966-
handle = ext4_journal_start(inode, EXT4_HT_TRUNCATE, depth + 1);
2977+
handle = ext4_journal_start_with_revoke(inode, EXT4_HT_TRUNCATE,
2978+
depth + 1,
2979+
ext4_free_metadata_revoke_credits(inode->i_sb, depth));
29672980
if (IS_ERR(handle))
29682981
return PTR_ERR(handle);
29692982

@@ -5222,7 +5235,7 @@ ext4_access_path(handle_t *handle, struct inode *inode,
52225235
* groups
52235236
*/
52245237
credits = ext4_writepage_trans_blocks(inode);
5225-
err = ext4_datasem_ensure_credits(handle, inode, 7, credits);
5238+
err = ext4_datasem_ensure_credits(handle, inode, 7, credits, 0);
52265239
if (err < 0)
52275240
return err;
52285241

fs/ext4/ialloc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ struct inode *__ext4_new_inode(handle_t *handle, struct inode *dir,
927927
BUG_ON(nblocks <= 0);
928928
handle = __ext4_journal_start_sb(dir->i_sb, line_no,
929929
handle_type, nblocks,
930-
0);
930+
0, 0);
931931
if (IS_ERR(handle)) {
932932
err = PTR_ERR(handle);
933933
ext4_std_error(sb, err);

fs/ext4/indirect.c

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -736,13 +736,14 @@ static int ext4_ind_trunc_restart_fn(handle_t *handle, struct inode *inode,
736736
*/
737737
static int ext4_ind_truncate_ensure_credits(handle_t *handle,
738738
struct inode *inode,
739-
struct buffer_head *bh)
739+
struct buffer_head *bh,
740+
int revoke_creds)
740741
{
741742
int ret;
742743
int dropped = 0;
743744

744745
ret = ext4_journal_ensure_credits_fn(handle, EXT4_RESERVE_TRANS_BLOCKS,
745-
ext4_blocks_for_truncate(inode),
746+
ext4_blocks_for_truncate(inode), revoke_creds,
746747
ext4_ind_trunc_restart_fn(handle, inode, bh, &dropped));
747748
if (dropped)
748749
down_write(&EXT4_I(inode)->i_data_sem);
@@ -889,7 +890,8 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode,
889890
return 1;
890891
}
891892

892-
err = ext4_ind_truncate_ensure_credits(handle, inode, bh);
893+
err = ext4_ind_truncate_ensure_credits(handle, inode, bh,
894+
ext4_free_data_revoke_credits(inode, count));
893895
if (err < 0)
894896
goto out_err;
895897

@@ -1075,7 +1077,9 @@ static void ext4_free_branches(handle_t *handle, struct inode *inode,
10751077
if (ext4_handle_is_aborted(handle))
10761078
return;
10771079
if (ext4_ind_truncate_ensure_credits(handle, inode,
1078-
NULL) < 0)
1080+
NULL,
1081+
ext4_free_metadata_revoke_credits(
1082+
inode->i_sb, 1)) < 0)
10791083
return;
10801084

10811085
/*

fs/ext4/inode.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5949,7 +5949,7 @@ static int ext4_try_to_expand_extra_isize(struct inode *inode,
59495949
* force a large enough s_min_extra_isize.
59505950
*/
59515951
if (ext4_journal_extend(handle,
5952-
EXT4_DATA_TRANS_BLOCKS(inode->i_sb)) != 0)
5952+
EXT4_DATA_TRANS_BLOCKS(inode->i_sb), 0) != 0)
59535953
return -ENOSPC;
59545954

59555955
if (ext4_write_trylock_xattr(inode, &no_expand) == 0)

0 commit comments

Comments
 (0)