Skip to content

Commit e85c81b

Browse files
Xin Yintytso
authored andcommitted
ext4: fast commit may not fallback for ineligible commit
For the follow scenario: 1. jbd start commit transaction n 2. task A get new handle for transaction n+1 3. task A do some ineligible actions and mark FC_INELIGIBLE 4. jbd complete transaction n and clean FC_INELIGIBLE 5. task A call fsync In this case fast commit will not fallback to full commit and transaction n+1 also not handled by jbd. Make ext4_fc_mark_ineligible() also record transaction tid for latest ineligible case, when call ext4_fc_cleanup() check current transaction tid, if small than latest ineligible tid do not clear the EXT4_MF_FC_INELIGIBLE. Reported-by: kernel test robot <[email protected]> Reported-by: Dan Carpenter <[email protected]> Reported-by: Ritesh Harjani <[email protected]> Suggested-by: Harshad Shirwadkar <[email protected]> Signed-off-by: Xin Yin <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Theodore Ts'o <[email protected]> Cc: [email protected]
1 parent 31a074a commit e85c81b

File tree

11 files changed

+42
-23
lines changed

11 files changed

+42
-23
lines changed

fs/ext4/ext4.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,7 @@ struct ext4_sb_info {
17491749
spinlock_t s_fc_lock;
17501750
struct buffer_head *s_fc_bh;
17511751
struct ext4_fc_stats s_fc_stats;
1752+
tid_t s_fc_ineligible_tid;
17521753
#ifdef CONFIG_EXT4_DEBUG
17531754
int s_fc_debug_max_replay;
17541755
#endif
@@ -2925,7 +2926,7 @@ void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
29252926
struct dentry *dentry);
29262927
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
29272928
void ext4_fc_track_inode(handle_t *handle, struct inode *inode);
2928-
void ext4_fc_mark_ineligible(struct super_block *sb, int reason);
2929+
void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handle);
29292930
void ext4_fc_start_update(struct inode *inode);
29302931
void ext4_fc_stop_update(struct inode *inode);
29312932
void ext4_fc_del(struct inode *inode);

fs/ext4/extents.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5336,7 +5336,7 @@ static int ext4_collapse_range(struct inode *inode, loff_t offset, loff_t len)
53365336
ret = PTR_ERR(handle);
53375337
goto out_mmap;
53385338
}
5339-
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
5339+
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE, handle);
53405340

53415341
down_write(&EXT4_I(inode)->i_data_sem);
53425342
ext4_discard_preallocations(inode, 0);
@@ -5476,7 +5476,7 @@ static int ext4_insert_range(struct inode *inode, loff_t offset, loff_t len)
54765476
ret = PTR_ERR(handle);
54775477
goto out_mmap;
54785478
}
5479-
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE);
5479+
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_FALLOC_RANGE, handle);
54805480

54815481
/* Expand file to avoid data loss if there is error while shifting */
54825482
inode->i_size += len;

fs/ext4/fast_commit.c

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -300,18 +300,32 @@ void ext4_fc_del(struct inode *inode)
300300
}
301301

302302
/*
303-
* Mark file system as fast commit ineligible. This means that next commit
304-
* operation would result in a full jbd2 commit.
303+
* Mark file system as fast commit ineligible, and record latest
304+
* ineligible transaction tid. This means until the recorded
305+
* transaction, commit operation would result in a full jbd2 commit.
305306
*/
306-
void ext4_fc_mark_ineligible(struct super_block *sb, int reason)
307+
void ext4_fc_mark_ineligible(struct super_block *sb, int reason, handle_t *handle)
307308
{
308309
struct ext4_sb_info *sbi = EXT4_SB(sb);
310+
tid_t tid;
309311

310312
if (!test_opt2(sb, JOURNAL_FAST_COMMIT) ||
311313
(EXT4_SB(sb)->s_mount_state & EXT4_FC_REPLAY))
312314
return;
313315

314316
ext4_set_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
317+
if (handle && !IS_ERR(handle))
318+
tid = handle->h_transaction->t_tid;
319+
else {
320+
read_lock(&sbi->s_journal->j_state_lock);
321+
tid = sbi->s_journal->j_running_transaction ?
322+
sbi->s_journal->j_running_transaction->t_tid : 0;
323+
read_unlock(&sbi->s_journal->j_state_lock);
324+
}
325+
spin_lock(&sbi->s_fc_lock);
326+
if (sbi->s_fc_ineligible_tid < tid)
327+
sbi->s_fc_ineligible_tid = tid;
328+
spin_unlock(&sbi->s_fc_lock);
315329
WARN_ON(reason >= EXT4_FC_REASON_MAX);
316330
sbi->s_fc_stats.fc_ineligible_reason_count[reason]++;
317331
}
@@ -387,7 +401,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
387401
mutex_unlock(&ei->i_fc_lock);
388402
node = kmem_cache_alloc(ext4_fc_dentry_cachep, GFP_NOFS);
389403
if (!node) {
390-
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM);
404+
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM, NULL);
391405
mutex_lock(&ei->i_fc_lock);
392406
return -ENOMEM;
393407
}
@@ -400,7 +414,7 @@ static int __track_dentry_update(struct inode *inode, void *arg, bool update)
400414
if (!node->fcd_name.name) {
401415
kmem_cache_free(ext4_fc_dentry_cachep, node);
402416
ext4_fc_mark_ineligible(inode->i_sb,
403-
EXT4_FC_REASON_NOMEM);
417+
EXT4_FC_REASON_NOMEM, NULL);
404418
mutex_lock(&ei->i_fc_lock);
405419
return -ENOMEM;
406420
}
@@ -502,7 +516,7 @@ void ext4_fc_track_inode(handle_t *handle, struct inode *inode)
502516

503517
if (ext4_should_journal_data(inode)) {
504518
ext4_fc_mark_ineligible(inode->i_sb,
505-
EXT4_FC_REASON_INODE_JOURNAL_DATA);
519+
EXT4_FC_REASON_INODE_JOURNAL_DATA, handle);
506520
return;
507521
}
508522

@@ -1179,7 +1193,7 @@ int ext4_fc_commit(journal_t *journal, tid_t commit_tid)
11791193
* Fast commit cleanup routine. This is called after every fast commit and
11801194
* full commit. full is true if we are called after a full commit.
11811195
*/
1182-
static void ext4_fc_cleanup(journal_t *journal, int full)
1196+
static void ext4_fc_cleanup(journal_t *journal, int full, tid_t tid)
11831197
{
11841198
struct super_block *sb = journal->j_private;
11851199
struct ext4_sb_info *sbi = EXT4_SB(sb);
@@ -1227,7 +1241,10 @@ static void ext4_fc_cleanup(journal_t *journal, int full)
12271241
&sbi->s_fc_q[FC_Q_MAIN]);
12281242

12291243
ext4_clear_mount_flag(sb, EXT4_MF_FC_COMMITTING);
1230-
ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
1244+
if (tid >= sbi->s_fc_ineligible_tid) {
1245+
sbi->s_fc_ineligible_tid = 0;
1246+
ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
1247+
}
12311248

12321249
if (full)
12331250
sbi->s_fc_bytes = 0;

fs/ext4/inode.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ void ext4_evict_inode(struct inode *inode)
337337
return;
338338
no_delete:
339339
if (!list_empty(&EXT4_I(inode)->i_fc_list))
340-
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM);
340+
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_NOMEM, NULL);
341341
ext4_clear_inode(inode); /* We must guarantee clearing of inode... */
342342
}
343343

@@ -5976,7 +5976,7 @@ int ext4_change_inode_journal_flag(struct inode *inode, int val)
59765976
return PTR_ERR(handle);
59775977

59785978
ext4_fc_mark_ineligible(inode->i_sb,
5979-
EXT4_FC_REASON_JOURNAL_FLAG_CHANGE);
5979+
EXT4_FC_REASON_JOURNAL_FLAG_CHANGE, handle);
59805980
err = ext4_mark_inode_dirty(handle, inode);
59815981
ext4_handle_sync(handle);
59825982
ext4_journal_stop(handle);

fs/ext4/ioctl.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ static long swap_inode_boot_loader(struct super_block *sb,
411411
err = -EINVAL;
412412
goto err_out;
413413
}
414-
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT);
414+
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_SWAP_BOOT, handle);
415415

416416
/* Protect extent tree against block allocations via delalloc */
417417
ext4_double_down_write_data_sem(inode, inode_bl);
@@ -1373,7 +1373,7 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
13731373

13741374
err = ext4_resize_fs(sb, n_blocks_count);
13751375
if (EXT4_SB(sb)->s_journal) {
1376-
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE);
1376+
ext4_fc_mark_ineligible(sb, EXT4_FC_REASON_RESIZE, NULL);
13771377
jbd2_journal_lock_updates(EXT4_SB(sb)->s_journal);
13781378
err2 = jbd2_journal_flush(EXT4_SB(sb)->s_journal, 0);
13791379
jbd2_journal_unlock_updates(EXT4_SB(sb)->s_journal);

fs/ext4/namei.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3889,7 +3889,7 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
38893889
* dirents in directories.
38903890
*/
38913891
ext4_fc_mark_ineligible(old.inode->i_sb,
3892-
EXT4_FC_REASON_RENAME_DIR);
3892+
EXT4_FC_REASON_RENAME_DIR, handle);
38933893
} else {
38943894
if (new.inode)
38953895
ext4_fc_track_unlink(handle, new.dentry);
@@ -4049,7 +4049,7 @@ static int ext4_cross_rename(struct inode *old_dir, struct dentry *old_dentry,
40494049
if (unlikely(retval))
40504050
goto end_rename;
40514051
ext4_fc_mark_ineligible(new.inode->i_sb,
4052-
EXT4_FC_REASON_CROSS_RENAME);
4052+
EXT4_FC_REASON_CROSS_RENAME, handle);
40534053
if (old.dir_bh) {
40544054
retval = ext4_rename_dir_finish(handle, &old, new.dir->i_ino);
40554055
if (retval)

fs/ext4/super.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5084,6 +5084,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
50845084
sbi->s_fc_bytes = 0;
50855085
ext4_clear_mount_flag(sb, EXT4_MF_FC_INELIGIBLE);
50865086
ext4_clear_mount_flag(sb, EXT4_MF_FC_COMMITTING);
5087+
sbi->s_fc_ineligible_tid = 0;
50875088
spin_lock_init(&sbi->s_fc_lock);
50885089
memset(&sbi->s_fc_stats, 0, sizeof(sbi->s_fc_stats));
50895090
sbi->s_fc_replay_state.fc_regions = NULL;

fs/ext4/xattr.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2408,7 +2408,7 @@ ext4_xattr_set_handle(handle_t *handle, struct inode *inode, int name_index,
24082408
if (IS_SYNC(inode))
24092409
ext4_handle_sync(handle);
24102410
}
2411-
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
2411+
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, handle);
24122412

24132413
cleanup:
24142414
brelse(is.iloc.bh);
@@ -2486,7 +2486,7 @@ ext4_xattr_set(struct inode *inode, int name_index, const char *name,
24862486
if (error == 0)
24872487
error = error2;
24882488
}
2489-
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
2489+
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, NULL);
24902490

24912491
return error;
24922492
}
@@ -2920,7 +2920,7 @@ int ext4_xattr_delete_inode(handle_t *handle, struct inode *inode,
29202920
error);
29212921
goto cleanup;
29222922
}
2923-
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR);
2923+
ext4_fc_mark_ineligible(inode->i_sb, EXT4_FC_REASON_XATTR, handle);
29242924
}
29252925
error = 0;
29262926
cleanup:

fs/jbd2/commit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1170,7 +1170,7 @@ void jbd2_journal_commit_transaction(journal_t *journal)
11701170
if (journal->j_commit_callback)
11711171
journal->j_commit_callback(journal, commit_transaction);
11721172
if (journal->j_fc_cleanup_callback)
1173-
journal->j_fc_cleanup_callback(journal, 1);
1173+
journal->j_fc_cleanup_callback(journal, 1, commit_transaction->t_tid);
11741174

11751175
trace_jbd2_end_commit(journal, commit_transaction);
11761176
jbd_debug(1, "JBD2: commit %d complete, head %d\n",

fs/jbd2/journal.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -771,7 +771,7 @@ static int __jbd2_fc_end_commit(journal_t *journal, tid_t tid, bool fallback)
771771
{
772772
jbd2_journal_unlock_updates(journal);
773773
if (journal->j_fc_cleanup_callback)
774-
journal->j_fc_cleanup_callback(journal, 0);
774+
journal->j_fc_cleanup_callback(journal, 0, tid);
775775
write_lock(&journal->j_state_lock);
776776
journal->j_flags &= ~JBD2_FAST_COMMIT_ONGOING;
777777
if (fallback)

include/linux/jbd2.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1295,7 +1295,7 @@ struct journal_s
12951295
* Clean-up after fast commit or full commit. JBD2 calls this function
12961296
* after every commit operation.
12971297
*/
1298-
void (*j_fc_cleanup_callback)(struct journal_s *journal, int);
1298+
void (*j_fc_cleanup_callback)(struct journal_s *journal, int full, tid_t tid);
12991299

13001300
/**
13011301
* @j_fc_replay_callback:

0 commit comments

Comments
 (0)