Skip to content

Commit d7f5f1b

Browse files
committed
Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4
Pull ext4 fixes from Ted Ts'o: "Miscellaneous ext4 bug fixes for v5.12" * tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4: ext4: initialize ret to suppress smatch warning ext4: stop inode update before return ext4: fix rename whiteout with fast commit ext4: fix timer use-after-free on failed mount ext4: fix potential error in ext4_do_update_inode ext4: do not try to set xattr into ea_inode if value is empty ext4: do not iput inode under running transaction in ext4_rename() ext4: find old entry again if failed to rename whiteout ext4: fix error handling in ext4_end_enable_verity() ext4: fix bh ref count on error paths fs/ext4: fix integer overflow in s_log_groups_per_flex ext4: add reclaim checks to xattr code ext4: shrink race window in ext4_should_retry_alloc()
2 parents 2c41fab + 64395d9 commit d7f5f1b

File tree

11 files changed

+168
-72
lines changed

11 files changed

+168
-72
lines changed

fs/ext4/balloc.c

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -626,27 +626,41 @@ int ext4_claim_free_clusters(struct ext4_sb_info *sbi,
626626

627627
/**
628628
* ext4_should_retry_alloc() - check if a block allocation should be retried
629-
* @sb: super block
630-
* @retries: number of attemps has been made
629+
* @sb: superblock
630+
* @retries: number of retry attempts made so far
631631
*
632-
* ext4_should_retry_alloc() is called when ENOSPC is returned, and if
633-
* it is profitable to retry the operation, this function will wait
634-
* for the current or committing transaction to complete, and then
635-
* return TRUE. We will only retry once.
632+
* ext4_should_retry_alloc() is called when ENOSPC is returned while
633+
* attempting to allocate blocks. If there's an indication that a pending
634+
* journal transaction might free some space and allow another attempt to
635+
* succeed, this function will wait for the current or committing transaction
636+
* to complete and then return TRUE.
636637
*/
637638
int ext4_should_retry_alloc(struct super_block *sb, int *retries)
638639
{
639-
if (!ext4_has_free_clusters(EXT4_SB(sb), 1, 0) ||
640-
(*retries)++ > 1 ||
641-
!EXT4_SB(sb)->s_journal)
640+
struct ext4_sb_info *sbi = EXT4_SB(sb);
641+
642+
if (!sbi->s_journal)
642643
return 0;
643644

644-
smp_mb();
645-
if (EXT4_SB(sb)->s_mb_free_pending == 0)
645+
if (++(*retries) > 3) {
646+
percpu_counter_inc(&sbi->s_sra_exceeded_retry_limit);
646647
return 0;
648+
}
647649

650+
/*
651+
* if there's no indication that blocks are about to be freed it's
652+
* possible we just missed a transaction commit that did so
653+
*/
654+
smp_mb();
655+
if (sbi->s_mb_free_pending == 0)
656+
return ext4_has_free_clusters(sbi, 1, 0);
657+
658+
/*
659+
* it's possible we've just missed a transaction commit here,
660+
* so ignore the returned status
661+
*/
648662
jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id);
649-
jbd2_journal_force_commit_nested(EXT4_SB(sb)->s_journal);
663+
(void) jbd2_journal_force_commit_nested(sbi->s_journal);
650664
return 1;
651665
}
652666

fs/ext4/ext4.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,7 @@ struct ext4_sb_info {
14841484
struct percpu_counter s_freeinodes_counter;
14851485
struct percpu_counter s_dirs_counter;
14861486
struct percpu_counter s_dirtyclusters_counter;
1487+
struct percpu_counter s_sra_exceeded_retry_limit;
14871488
struct blockgroup_lock *s_blockgroup_lock;
14881489
struct proc_dir_entry *s_proc;
14891490
struct kobject s_kobj;
@@ -2793,6 +2794,8 @@ void __ext4_fc_track_link(handle_t *handle, struct inode *inode,
27932794
struct dentry *dentry);
27942795
void ext4_fc_track_unlink(handle_t *handle, struct dentry *dentry);
27952796
void ext4_fc_track_link(handle_t *handle, struct dentry *dentry);
2797+
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
2798+
struct dentry *dentry);
27962799
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry);
27972800
void ext4_fc_track_inode(handle_t *handle, struct inode *inode);
27982801
void ext4_fc_mark_ineligible(struct super_block *sb, int reason);

fs/ext4/extents.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4382,7 +4382,7 @@ static int ext4_alloc_file_blocks(struct file *file, ext4_lblk_t offset,
43824382
{
43834383
struct inode *inode = file_inode(file);
43844384
handle_t *handle;
4385-
int ret, ret2 = 0, ret3 = 0;
4385+
int ret = 0, ret2 = 0, ret3 = 0;
43864386
int retries = 0;
43874387
int depth = 0;
43884388
struct ext4_map_blocks map;

fs/ext4/fast_commit.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -513,10 +513,10 @@ void ext4_fc_track_link(handle_t *handle, struct dentry *dentry)
513513
__ext4_fc_track_link(handle, d_inode(dentry), dentry);
514514
}
515515

516-
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
516+
void __ext4_fc_track_create(handle_t *handle, struct inode *inode,
517+
struct dentry *dentry)
517518
{
518519
struct __track_dentry_update_args args;
519-
struct inode *inode = d_inode(dentry);
520520
int ret;
521521

522522
args.dentry = dentry;
@@ -527,6 +527,11 @@ void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
527527
trace_ext4_fc_track_create(inode, dentry, ret);
528528
}
529529

530+
void ext4_fc_track_create(handle_t *handle, struct dentry *dentry)
531+
{
532+
__ext4_fc_track_create(handle, d_inode(dentry), dentry);
533+
}
534+
530535
/* __track_fn for inode tracking */
531536
static int __track_inode(struct inode *inode, void *arg, bool update)
532537
{

fs/ext4/inode.c

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1938,13 +1938,13 @@ static int __ext4_journalled_writepage(struct page *page,
19381938
if (!ret)
19391939
ret = err;
19401940

1941-
if (!ext4_has_inline_data(inode))
1942-
ext4_walk_page_buffers(NULL, page_bufs, 0, len,
1943-
NULL, bput_one);
19441941
ext4_set_inode_state(inode, EXT4_STATE_JDATA);
19451942
out:
19461943
unlock_page(page);
19471944
out_no_pagelock:
1945+
if (!inline_data && page_bufs)
1946+
ext4_walk_page_buffers(NULL, page_bufs, 0, len,
1947+
NULL, bput_one);
19481948
brelse(inode_bh);
19491949
return ret;
19501950
}
@@ -5026,7 +5026,7 @@ static int ext4_do_update_inode(handle_t *handle,
50265026
struct ext4_inode_info *ei = EXT4_I(inode);
50275027
struct buffer_head *bh = iloc->bh;
50285028
struct super_block *sb = inode->i_sb;
5029-
int err = 0, rc, block;
5029+
int err = 0, block;
50305030
int need_datasync = 0, set_large_file = 0;
50315031
uid_t i_uid;
50325032
gid_t i_gid;
@@ -5138,9 +5138,9 @@ static int ext4_do_update_inode(handle_t *handle,
51385138
bh->b_data);
51395139

51405140
BUFFER_TRACE(bh, "call ext4_handle_dirty_metadata");
5141-
rc = ext4_handle_dirty_metadata(handle, NULL, bh);
5142-
if (!err)
5143-
err = rc;
5141+
err = ext4_handle_dirty_metadata(handle, NULL, bh);
5142+
if (err)
5143+
goto out_brelse;
51445144
ext4_clear_inode_state(inode, EXT4_STATE_NEW);
51455145
if (set_large_file) {
51465146
BUFFER_TRACE(EXT4_SB(sb)->s_sbh, "get write access");
@@ -5387,8 +5387,10 @@ int ext4_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
53875387
inode->i_gid = attr->ia_gid;
53885388
error = ext4_mark_inode_dirty(handle, inode);
53895389
ext4_journal_stop(handle);
5390-
if (unlikely(error))
5390+
if (unlikely(error)) {
5391+
ext4_fc_stop_update(inode);
53915392
return error;
5393+
}
53925394
}
53935395

53945396
if (attr->ia_valid & ATTR_SIZE) {

fs/ext4/mballoc.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2709,8 +2709,15 @@ static int ext4_mb_init_backend(struct super_block *sb)
27092709
}
27102710

27112711
if (ext4_has_feature_flex_bg(sb)) {
2712-
/* a single flex group is supposed to be read by a single IO */
2713-
sbi->s_mb_prefetch = min(1 << sbi->s_es->s_log_groups_per_flex,
2712+
/* a single flex group is supposed to be read by a single IO.
2713+
* 2 ^ s_log_groups_per_flex != UINT_MAX as s_mb_prefetch is
2714+
* unsigned integer, so the maximum shift is 32.
2715+
*/
2716+
if (sbi->s_es->s_log_groups_per_flex >= 32) {
2717+
ext4_msg(sb, KERN_ERR, "too many log groups per flexible block group");
2718+
goto err_freesgi;
2719+
}
2720+
sbi->s_mb_prefetch = min_t(uint, 1 << sbi->s_es->s_log_groups_per_flex,
27142721
BLK_MAX_SEGMENT_SIZE >> (sb->s_blocksize_bits - 9));
27152722
sbi->s_mb_prefetch *= 8; /* 8 prefetch IOs in flight at most */
27162723
} else {

fs/ext4/namei.c

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3613,6 +3613,31 @@ static int ext4_setent(handle_t *handle, struct ext4_renament *ent,
36133613
return retval;
36143614
}
36153615

3616+
static void ext4_resetent(handle_t *handle, struct ext4_renament *ent,
3617+
unsigned ino, unsigned file_type)
3618+
{
3619+
struct ext4_renament old = *ent;
3620+
int retval = 0;
3621+
3622+
/*
3623+
* old->de could have moved from under us during make indexed dir,
3624+
* so the old->de may no longer valid and need to find it again
3625+
* before reset old inode info.
3626+
*/
3627+
old.bh = ext4_find_entry(old.dir, &old.dentry->d_name, &old.de, NULL);
3628+
if (IS_ERR(old.bh))
3629+
retval = PTR_ERR(old.bh);
3630+
if (!old.bh)
3631+
retval = -ENOENT;
3632+
if (retval) {
3633+
ext4_std_error(old.dir->i_sb, retval);
3634+
return;
3635+
}
3636+
3637+
ext4_setent(handle, &old, ino, file_type);
3638+
brelse(old.bh);
3639+
}
3640+
36163641
static int ext4_find_delete_entry(handle_t *handle, struct inode *dir,
36173642
const struct qstr *d_name)
36183643
{
@@ -3774,14 +3799,14 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
37743799
*/
37753800
retval = -ENOENT;
37763801
if (!old.bh || le32_to_cpu(old.de->inode) != old.inode->i_ino)
3777-
goto end_rename;
3802+
goto release_bh;
37783803

37793804
new.bh = ext4_find_entry(new.dir, &new.dentry->d_name,
37803805
&new.de, &new.inlined);
37813806
if (IS_ERR(new.bh)) {
37823807
retval = PTR_ERR(new.bh);
37833808
new.bh = NULL;
3784-
goto end_rename;
3809+
goto release_bh;
37853810
}
37863811
if (new.bh) {
37873812
if (!new.inode) {
@@ -3798,15 +3823,13 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
37983823
handle = ext4_journal_start(old.dir, EXT4_HT_DIR, credits);
37993824
if (IS_ERR(handle)) {
38003825
retval = PTR_ERR(handle);
3801-
handle = NULL;
3802-
goto end_rename;
3826+
goto release_bh;
38033827
}
38043828
} else {
38053829
whiteout = ext4_whiteout_for_rename(mnt_userns, &old, credits, &handle);
38063830
if (IS_ERR(whiteout)) {
38073831
retval = PTR_ERR(whiteout);
3808-
whiteout = NULL;
3809-
goto end_rename;
3832+
goto release_bh;
38103833
}
38113834
}
38123835

@@ -3850,6 +3873,7 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
38503873
retval = ext4_mark_inode_dirty(handle, whiteout);
38513874
if (unlikely(retval))
38523875
goto end_rename;
3876+
38533877
}
38543878
if (!new.bh) {
38553879
retval = ext4_add_entry(handle, new.dentry, old.inode);
@@ -3923,6 +3947,8 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
39233947
ext4_fc_track_unlink(handle, new.dentry);
39243948
__ext4_fc_track_link(handle, old.inode, new.dentry);
39253949
__ext4_fc_track_unlink(handle, old.inode, old.dentry);
3950+
if (whiteout)
3951+
__ext4_fc_track_create(handle, whiteout, old.dentry);
39263952
}
39273953

39283954
if (new.inode) {
@@ -3937,19 +3963,21 @@ static int ext4_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
39373963
end_rename:
39383964
if (whiteout) {
39393965
if (retval) {
3940-
ext4_setent(handle, &old,
3941-
old.inode->i_ino, old_file_type);
3966+
ext4_resetent(handle, &old,
3967+
old.inode->i_ino, old_file_type);
39423968
drop_nlink(whiteout);
3969+
ext4_orphan_add(handle, whiteout);
39433970
}
39443971
unlock_new_inode(whiteout);
3972+
ext4_journal_stop(handle);
39453973
iput(whiteout);
3946-
3974+
} else {
3975+
ext4_journal_stop(handle);
39473976
}
3977+
release_bh:
39483978
brelse(old.dir_bh);
39493979
brelse(old.bh);
39503980
brelse(new.bh);
3951-
if (handle)
3952-
ext4_journal_stop(handle);
39533981
return retval;
39543982
}
39553983

fs/ext4/super.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1210,6 +1210,7 @@ static void ext4_put_super(struct super_block *sb)
12101210
percpu_counter_destroy(&sbi->s_freeinodes_counter);
12111211
percpu_counter_destroy(&sbi->s_dirs_counter);
12121212
percpu_counter_destroy(&sbi->s_dirtyclusters_counter);
1213+
percpu_counter_destroy(&sbi->s_sra_exceeded_retry_limit);
12131214
percpu_free_rwsem(&sbi->s_writepages_rwsem);
12141215
#ifdef CONFIG_QUOTA
12151216
for (i = 0; i < EXT4_MAXQUOTAS; i++)
@@ -5011,6 +5012,9 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
50115012
if (!err)
50125013
err = percpu_counter_init(&sbi->s_dirtyclusters_counter, 0,
50135014
GFP_KERNEL);
5015+
if (!err)
5016+
err = percpu_counter_init(&sbi->s_sra_exceeded_retry_limit, 0,
5017+
GFP_KERNEL);
50145018
if (!err)
50155019
err = percpu_init_rwsem(&sbi->s_writepages_rwsem);
50165020

@@ -5124,6 +5128,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
51245128
percpu_counter_destroy(&sbi->s_freeinodes_counter);
51255129
percpu_counter_destroy(&sbi->s_dirs_counter);
51265130
percpu_counter_destroy(&sbi->s_dirtyclusters_counter);
5131+
percpu_counter_destroy(&sbi->s_sra_exceeded_retry_limit);
51275132
percpu_free_rwsem(&sbi->s_writepages_rwsem);
51285133
failed_mount5:
51295134
ext4_ext_release(sb);
@@ -5149,8 +5154,8 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
51495154
failed_mount3a:
51505155
ext4_es_unregister_shrinker(sbi);
51515156
failed_mount3:
5152-
del_timer_sync(&sbi->s_err_report);
51535157
flush_work(&sbi->s_error_work);
5158+
del_timer_sync(&sbi->s_err_report);
51545159
if (sbi->s_mmp_tsk)
51555160
kthread_stop(sbi->s_mmp_tsk);
51565161
failed_mount2:

fs/ext4/sysfs.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ typedef enum {
2424
attr_session_write_kbytes,
2525
attr_lifetime_write_kbytes,
2626
attr_reserved_clusters,
27+
attr_sra_exceeded_retry_limit,
2728
attr_inode_readahead,
2829
attr_trigger_test_error,
2930
attr_first_error_time,
@@ -202,6 +203,7 @@ EXT4_ATTR_FUNC(delayed_allocation_blocks, 0444);
202203
EXT4_ATTR_FUNC(session_write_kbytes, 0444);
203204
EXT4_ATTR_FUNC(lifetime_write_kbytes, 0444);
204205
EXT4_ATTR_FUNC(reserved_clusters, 0644);
206+
EXT4_ATTR_FUNC(sra_exceeded_retry_limit, 0444);
205207

206208
EXT4_ATTR_OFFSET(inode_readahead_blks, 0644, inode_readahead,
207209
ext4_sb_info, s_inode_readahead_blks);
@@ -251,6 +253,7 @@ static struct attribute *ext4_attrs[] = {
251253
ATTR_LIST(session_write_kbytes),
252254
ATTR_LIST(lifetime_write_kbytes),
253255
ATTR_LIST(reserved_clusters),
256+
ATTR_LIST(sra_exceeded_retry_limit),
254257
ATTR_LIST(inode_readahead_blks),
255258
ATTR_LIST(inode_goal),
256259
ATTR_LIST(mb_stats),
@@ -374,6 +377,10 @@ static ssize_t ext4_attr_show(struct kobject *kobj,
374377
return snprintf(buf, PAGE_SIZE, "%llu\n",
375378
(unsigned long long)
376379
atomic64_read(&sbi->s_resv_clusters));
380+
case attr_sra_exceeded_retry_limit:
381+
return snprintf(buf, PAGE_SIZE, "%llu\n",
382+
(unsigned long long)
383+
percpu_counter_sum(&sbi->s_sra_exceeded_retry_limit));
377384
case attr_inode_readahead:
378385
case attr_pointer_ui:
379386
if (!ptr)

0 commit comments

Comments
 (0)