Skip to content

Commit a6d4040

Browse files
committed
Merge branch 'jk/jbd2-revoke-overflow'
2 parents 0d0a60c + 19014d6 commit a6d4040

File tree

21 files changed

+675
-478
lines changed

21 files changed

+675
-478
lines changed

fs/ext4/ext4.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2609,7 +2609,6 @@ extern int ext4_can_truncate(struct inode *inode);
26092609
extern int ext4_truncate(struct inode *);
26102610
extern int ext4_break_layouts(struct inode *);
26112611
extern int ext4_punch_hole(struct inode *inode, loff_t offset, loff_t length);
2612-
extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks);
26132612
extern void ext4_set_inode_flags(struct inode *);
26142613
extern int ext4_alloc_da_blocks(struct inode *inode);
26152614
extern void ext4_set_aops(struct inode *inode);
@@ -3303,6 +3302,10 @@ extern int ext4_swap_extents(handle_t *handle, struct inode *inode1,
33033302
ext4_lblk_t lblk2, ext4_lblk_t count,
33043303
int mark_unwritten,int *err);
33053304
extern int ext4_clu_mapped(struct inode *inode, ext4_lblk_t lclu);
3305+
extern int ext4_datasem_ensure_credits(handle_t *handle, struct inode *inode,
3306+
int check_cred, int restart_cred,
3307+
int revoke_cred);
3308+
33063309

33073310
/* move_extent.c */
33083311
extern void ext4_double_down_write_data_sem(struct inode *first,

fs/ext4/ext4_jbd2.c

Lines changed: 24 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, 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)
@@ -119,8 +121,8 @@ handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
119121
return ext4_get_nojournal();
120122

121123
sb = handle->h_journal->j_private;
122-
trace_ext4_journal_start_reserved(sb, handle->h_buffer_credits,
123-
_RET_IP_);
124+
trace_ext4_journal_start_reserved(sb,
125+
jbd2_handle_buffer_credits(handle), _RET_IP_);
124126
err = ext4_journal_check_start(sb);
125127
if (err < 0) {
126128
jbd2_journal_free_reserved(handle);
@@ -133,6 +135,19 @@ handle_t *__ext4_journal_start_reserved(handle_t *handle, unsigned int line,
133135
return handle;
134136
}
135137

138+
int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
139+
int extend_cred, int revoke_cred)
140+
{
141+
if (!ext4_handle_valid(handle))
142+
return 0;
143+
if (jbd2_handle_buffer_credits(handle) >= check_cred &&
144+
handle->h_revoke_credits >= revoke_cred)
145+
return 0;
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);
149+
}
150+
136151
static void ext4_journal_abort_handle(const char *caller, unsigned int line,
137152
const char *err_fn,
138153
struct buffer_head *bh,
@@ -278,7 +293,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
278293
handle->h_type,
279294
handle->h_line_no,
280295
handle->h_requested_credits,
281-
handle->h_buffer_credits, err);
296+
jbd2_handle_buffer_credits(handle), err);
282297
return err;
283298
}
284299
ext4_error_inode(inode, where, line,
@@ -289,7 +304,8 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
289304
handle->h_type,
290305
handle->h_line_no,
291306
handle->h_requested_credits,
292-
handle->h_buffer_credits, err);
307+
jbd2_handle_buffer_credits(handle),
308+
err);
293309
}
294310
} else {
295311
if (inode)

fs/ext4/ext4_jbd2.h

Lines changed: 91 additions & 15 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,28 +289,41 @@ static inline int ext4_handle_is_aborted(handle_t *handle)
288289
return 0;
289290
}
290291

291-
static inline int ext4_handle_has_enough_credits(handle_t *handle, int needed)
292+
static inline int ext4_free_metadata_revoke_credits(struct super_block *sb,
293+
int blocks)
292294
{
293-
if (ext4_handle_valid(handle) && handle->h_buffer_credits < needed)
294-
return 0;
295-
return 1;
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);
296302
}
297303

298304
#define ext4_journal_start_sb(sb, type, nblocks) \
299-
__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))
300307

301308
#define ext4_journal_start(inode, type, nblocks) \
302-
__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))
311+
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))
303315

304-
#define ext4_journal_start_with_reserve(inode, type, blocks, rsv_blocks) \
305-
__ext4_journal_start((inode), __LINE__, (type), (blocks), (rsv_blocks))
316+
#define ext4_journal_start_with_revoke(inode, type, blocks, revoke_creds) \
317+
__ext4_journal_start((inode), __LINE__, (type), (blocks), 0, \
318+
(revoke_creds))
306319

307320
static inline handle_t *__ext4_journal_start(struct inode *inode,
308321
unsigned int line, int type,
309-
int blocks, int rsv_blocks)
322+
int blocks, int rsv_blocks,
323+
int revoke_creds)
310324
{
311325
return __ext4_journal_start_sb(inode->i_sb, line, type, blocks,
312-
rsv_blocks);
326+
rsv_blocks, revoke_creds);
313327
}
314328

315329
#define ext4_journal_stop(handle) \
@@ -332,20 +346,68 @@ static inline handle_t *ext4_journal_current_handle(void)
332346
return journal_current_handle();
333347
}
334348

335-
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)
336350
{
337351
if (ext4_handle_valid(handle))
338-
return jbd2_journal_extend(handle, nblocks);
352+
return jbd2_journal_extend(handle, nblocks, revoke);
339353
return 0;
340354
}
341355

342-
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)
343358
{
344359
if (ext4_handle_valid(handle))
345-
return jbd2_journal_restart(handle, nblocks);
360+
return jbd2__journal_restart(handle, nblocks, revoke, GFP_NOFS);
346361
return 0;
347362
}
348363

364+
int __ext4_journal_ensure_credits(handle_t *handle, int check_cred,
365+
int extend_cred, int revoke_cred);
366+
367+
368+
/*
369+
* Ensure @handle has at least @check_creds credits available. If not,
370+
* transaction will be extended or restarted to contain at least @extend_cred
371+
* credits. Before restarting transaction @fn is executed to allow for cleanup
372+
* before the transaction is restarted.
373+
*
374+
* The return value is < 0 in case of error, 0 in case the handle has enough
375+
* credits or transaction extension succeeded, 1 in case transaction had to be
376+
* restarted.
377+
*/
378+
#define ext4_journal_ensure_credits_fn(handle, check_cred, extend_cred, \
379+
revoke_cred, fn) \
380+
({ \
381+
__label__ __ensure_end; \
382+
int err = __ext4_journal_ensure_credits((handle), (check_cred), \
383+
(extend_cred), (revoke_cred)); \
384+
\
385+
if (err <= 0) \
386+
goto __ensure_end; \
387+
err = (fn); \
388+
if (err < 0) \
389+
goto __ensure_end; \
390+
err = ext4_journal_restart((handle), (extend_cred), (revoke_cred)); \
391+
if (err == 0) \
392+
err = 1; \
393+
__ensure_end: \
394+
err; \
395+
})
396+
397+
/*
398+
* Ensure given handle has at least requested amount of credits available,
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.
403+
*/
404+
static inline int ext4_journal_ensure_credits(handle_t *handle, int credits,
405+
int revoke_creds)
406+
{
407+
return ext4_journal_ensure_credits_fn(handle, credits, credits,
408+
revoke_creds, 0);
409+
}
410+
349411
static inline int ext4_journal_blocks_per_page(struct inode *inode)
350412
{
351413
if (EXT4_JOURNAL(inode) != NULL)
@@ -407,6 +469,7 @@ static inline int ext4_inode_journal_mode(struct inode *inode)
407469
return EXT4_INODE_WRITEBACK_DATA_MODE; /* writeback */
408470
/* We do not support data journalling with delayed allocation */
409471
if (!S_ISREG(inode->i_mode) ||
472+
ext4_test_inode_flag(inode, EXT4_INODE_EA_INODE) ||
410473
test_opt(inode->i_sb, DATA_FLAGS) == EXT4_MOUNT_JOURNAL_DATA ||
411474
(ext4_test_inode_flag(inode, EXT4_INODE_JOURNAL_DATA) &&
412475
!test_opt(inode->i_sb, DELALLOC))) {
@@ -437,6 +500,19 @@ static inline int ext4_should_writeback_data(struct inode *inode)
437500
return ext4_inode_journal_mode(inode) & EXT4_INODE_WRITEBACK_DATA_MODE;
438501
}
439502

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+
440516
/*
441517
* This function controls whether or not we should try to go down the
442518
* dioread_nolock code paths, which makes it safe to avoid taking

fs/ext4/extents.c

Lines changed: 57 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -100,29 +100,41 @@ static int ext4_split_extent_at(handle_t *handle,
100100
static int ext4_find_delayed_extent(struct inode *inode,
101101
struct extent_status *newes);
102102

103-
static int ext4_ext_truncate_extend_restart(handle_t *handle,
104-
struct inode *inode,
105-
int needed)
103+
static int ext4_ext_trunc_restart_fn(struct inode *inode, int *dropped)
106104
{
107-
int err;
108-
109-
if (!ext4_handle_valid(handle))
110-
return 0;
111-
if (handle->h_buffer_credits >= needed)
112-
return 0;
113105
/*
114-
* If we need to extend the journal get a few extra blocks
115-
* while we're at it for efficiency's sake.
106+
* Drop i_data_sem to avoid deadlock with ext4_map_blocks. At this
107+
* moment, get_block can be called only for blocks inside i_size since
108+
* page cache has been already dropped and writes are blocked by
109+
* i_mutex. So we can safely drop the i_data_sem here.
116110
*/
117-
needed += 3;
118-
err = ext4_journal_extend(handle, needed - handle->h_buffer_credits);
119-
if (err <= 0)
120-
return err;
121-
err = ext4_truncate_restart_trans(handle, inode, needed);
122-
if (err == 0)
123-
err = -EAGAIN;
111+
BUG_ON(EXT4_JOURNAL(inode) == NULL);
112+
ext4_discard_preallocations(inode);
113+
up_write(&EXT4_I(inode)->i_data_sem);
114+
*dropped = 1;
115+
return 0;
116+
}
124117

125-
return err;
118+
/*
119+
* Make sure 'handle' has at least 'check_cred' credits. If not, restart
120+
* transaction with 'restart_cred' credits. The function drops i_data_sem
121+
* when restarting transaction and gets it after transaction is restarted.
122+
*
123+
* The function returns 0 on success, 1 if transaction had to be restarted,
124+
* and < 0 in case of fatal error.
125+
*/
126+
int ext4_datasem_ensure_credits(handle_t *handle, struct inode *inode,
127+
int check_cred, int restart_cred,
128+
int revoke_cred)
129+
{
130+
int ret;
131+
int dropped = 0;
132+
133+
ret = ext4_journal_ensure_credits_fn(handle, check_cred, restart_cred,
134+
revoke_cred, ext4_ext_trunc_restart_fn(inode, &dropped));
135+
if (dropped)
136+
down_write(&EXT4_I(inode)->i_data_sem);
137+
return ret;
126138
}
127139

128140
/*
@@ -1840,7 +1852,8 @@ static void ext4_ext_try_to_merge_up(handle_t *handle,
18401852
* group descriptor to release the extent tree block. If we
18411853
* can't get the journal credits, give up.
18421854
*/
1843-
if (ext4_journal_extend(handle, 2))
1855+
if (ext4_journal_extend(handle, 2,
1856+
ext4_free_metadata_revoke_credits(inode->i_sb, 1)))
18441857
return;
18451858

18461859
/*
@@ -2727,7 +2740,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
27272740
{
27282741
struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
27292742
int err = 0, correct_index = 0;
2730-
int depth = ext_depth(inode), credits;
2743+
int depth = ext_depth(inode), credits, revoke_credits;
27312744
struct ext4_extent_header *eh;
27322745
ext4_lblk_t a, b;
27332746
unsigned num;
@@ -2819,10 +2832,23 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
28192832
credits += (ext_depth(inode)) + 1;
28202833
}
28212834
credits += EXT4_MAXQUOTAS_TRANS_BLOCKS(inode->i_sb);
2822-
2823-
err = ext4_ext_truncate_extend_restart(handle, inode, credits);
2824-
if (err)
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);
2844+
2845+
err = ext4_datasem_ensure_credits(handle, inode, credits,
2846+
credits, revoke_credits);
2847+
if (err) {
2848+
if (err > 0)
2849+
err = -EAGAIN;
28252850
goto out;
2851+
}
28262852

28272853
err = ext4_ext_get_access(handle, inode, path + depth);
28282854
if (err)
@@ -2948,7 +2974,9 @@ int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
29482974
ext_debug("truncate since %u to %u\n", start, end);
29492975

29502976
/* probably first extent we're gonna free will be last in block */
2951-
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));
29522980
if (IS_ERR(handle))
29532981
return PTR_ERR(handle);
29542982

@@ -5225,13 +5253,10 @@ ext4_access_path(handle_t *handle, struct inode *inode,
52255253
* descriptor) for each block group; assume two block
52265254
* groups
52275255
*/
5228-
if (handle->h_buffer_credits < 7) {
5229-
credits = ext4_writepage_trans_blocks(inode);
5230-
err = ext4_ext_truncate_extend_restart(handle, inode, credits);
5231-
/* EAGAIN is success */
5232-
if (err && err != -EAGAIN)
5233-
return err;
5234-
}
5256+
credits = ext4_writepage_trans_blocks(inode);
5257+
err = ext4_datasem_ensure_credits(handle, inode, 7, credits, 0);
5258+
if (err < 0)
5259+
return err;
52355260

52365261
err = ext4_ext_get_access(handle, inode, path);
52375262
return err;

0 commit comments

Comments
 (0)