Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions fs/ext4/ext4.h
Original file line number Diff line number Diff line change
Expand Up @@ -1727,10 +1727,13 @@ struct ext4_sb_info {
const char *s_last_error_func;
time64_t s_last_error_time;
/*
* If we are in a context where we cannot update error information in
* the on-disk superblock, we queue this work to do it.
* If we are in a context where we cannot update the on-disk
* superblock, we queue the work here. This is used to update
* the error information in the superblock, and for periodic
* updates of the superblock called from the commit callback
* function.
*/
struct work_struct s_error_work;
struct work_struct s_sb_upd_work;

/* Ext4 fast commit sub transaction ID */
atomic_t s_fc_subtid;
Expand Down Expand Up @@ -1800,7 +1803,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
enum {
EXT4_MF_MNTDIR_SAMPLED,
EXT4_MF_FS_ABORTED, /* Fatal error detected */
EXT4_MF_FC_INELIGIBLE /* Fast commit ineligible */
EXT4_MF_FC_INELIGIBLE, /* Fast commit ineligible */
EXT4_MF_JOURNAL_DESTROY /* Journal is in process of destroying */
};

static inline void ext4_set_mount_flag(struct super_block *sb, int bit)
Expand Down
29 changes: 29 additions & 0 deletions fs/ext4/ext4_jbd2.h
Original file line number Diff line number Diff line change
Expand Up @@ -513,4 +513,33 @@ static inline int ext4_should_dioread_nolock(struct inode *inode)
return 1;
}

/*
* Pass journal explicitly as it may not be cached in the sbi->s_journal in some
* cases
*/
static inline int ext4_journal_destroy(struct ext4_sb_info *sbi, journal_t *journal)
{
int err = 0;

/*
* At this point only two things can be operating on the journal.
* JBD2 thread performing transaction commit and s_sb_upd_work
* issuing sb update through the journal. Once we set
* EXT4_JOURNAL_DESTROY, new ext4_handle_error() calls will not
* queue s_sb_upd_work and ext4_force_commit() makes sure any
* ext4_handle_error() calls from the running transaction commit are
* finished. Hence no new s_sb_upd_work can be queued after we
* flush it here.
*/
ext4_set_mount_flag(sbi->s_sb, EXT4_MF_JOURNAL_DESTROY);

ext4_force_commit(sbi->s_sb);
flush_work(&sbi->s_sb_upd_work);

err = jbd2_journal_destroy(journal);
sbi->s_journal = NULL;

return err;
}

#endif /* _EXT4_JBD2_H */
1 change: 0 additions & 1 deletion fs/ext4/inline.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,6 @@ int ext4_find_inline_data_nolock(struct inode *inode)
(void *)ext4_raw_inode(&is.iloc));
EXT4_I(inode)->i_inline_size = EXT4_MIN_INLINE_DATA_SIZE +
le32_to_cpu(is.s.here->e_value_size);
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
}
out:
brelse(is.iloc.bh);
Expand Down
12 changes: 11 additions & 1 deletion fs/ext4/inode.c
Original file line number Diff line number Diff line change
Expand Up @@ -4683,8 +4683,18 @@ static inline int ext4_iget_extra_inode(struct inode *inode,

if (EXT4_INODE_HAS_XATTR_SPACE(inode) &&
*magic == cpu_to_le32(EXT4_XATTR_MAGIC)) {
int err;

err = xattr_check_inode(inode, IHDR(inode, raw_inode),
ITAIL(inode, raw_inode));
if (err)
return err;

ext4_set_inode_state(inode, EXT4_STATE_XATTR);
return ext4_find_inline_data_nolock(inode);
err = ext4_find_inline_data_nolock(inode);
if (!err && ext4_has_inline_data(inode))
ext4_set_inode_state(inode, EXT4_STATE_MAY_INLINE_DATA);
return err;
} else
EXT4_I(inode)->i_inline_off = 0;
return 0;
Expand Down
52 changes: 24 additions & 28 deletions fs/ext4/super.c
Original file line number Diff line number Diff line change
Expand Up @@ -668,10 +668,14 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
* In case the fs should keep running, we need to writeout
* superblock through the journal. Due to lock ordering
* constraints, it may not be safe to do it right here so we
* defer superblock flushing to a workqueue.
* defer superblock flushing to a workqueue. We just need to be
* careful when the journal is already shutting down. If we get
* here in that case, just update the sb directly as the last
* transaction won't commit anyway.
*/
if (continue_fs && journal)
schedule_work(&EXT4_SB(sb)->s_error_work);
if (continue_fs && journal &&
!ext4_test_mount_flag(sb, EXT4_MF_JOURNAL_DESTROY))
schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
else
ext4_commit_super(sb);
}
Expand All @@ -698,10 +702,10 @@ static void ext4_handle_error(struct super_block *sb, bool force_ro, int error,
sb->s_flags |= SB_RDONLY;
}

static void flush_stashed_error_work(struct work_struct *work)
static void update_super_work(struct work_struct *work)
{
struct ext4_sb_info *sbi = container_of(work, struct ext4_sb_info,
s_error_work);
s_sb_upd_work);
journal_t *journal = sbi->s_journal;
handle_t *handle;

Expand Down Expand Up @@ -1011,7 +1015,7 @@ __acquires(bitlock)
if (!bdev_read_only(sb->s_bdev)) {
save_error_info(sb, EFSCORRUPTED, ino, block, function,
line);
schedule_work(&EXT4_SB(sb)->s_error_work);
schedule_work(&EXT4_SB(sb)->s_sb_upd_work);
}
return;
}
Expand Down Expand Up @@ -1189,10 +1193,10 @@ static void ext4_put_super(struct super_block *sb)
* Unregister sysfs before destroying jbd2 journal.
* Since we could still access attr_journal_task attribute via sysfs
* path which could have sbi->s_journal->j_task as NULL
* Unregister sysfs before flush sbi->s_error_work.
* Unregister sysfs before flush sbi->s_sb_upd_work.
* Since user may read /proc/fs/ext4/xx/mb_groups during umount, If
* read metadata verify failed then will queue error work.
* flush_stashed_error_work will call start_this_handle may trigger
* update_super_work will call start_this_handle may trigger
* BUG_ON.
*/
ext4_unregister_sysfs(sb);
Expand All @@ -1203,18 +1207,17 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_li_request(sb);
ext4_quota_off_umount(sb);

flush_work(&sbi->s_error_work);
destroy_workqueue(sbi->rsv_conversion_wq);
ext4_release_orphan_info(sb);

if (sbi->s_journal) {
aborted = is_journal_aborted(sbi->s_journal);
err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
err = ext4_journal_destroy(sbi, sbi->s_journal);
if ((err < 0) && !aborted) {
ext4_abort(sb, -err, "Couldn't clean up the journal");
}
}
} else
flush_work(&sbi->s_sb_upd_work);

ext4_es_unregister_shrinker(sbi);
del_timer_sync(&sbi->s_err_report);
Expand Down Expand Up @@ -4892,10 +4895,7 @@ static int ext4_load_and_init_journal(struct super_block *sb,
return 0;

out:
/* flush s_error_work before journal destroy. */
flush_work(&sbi->s_error_work);
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
ext4_journal_destroy(sbi, sbi->s_journal);
return -EINVAL;
}

Expand Down Expand Up @@ -5241,7 +5241,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)

timer_setup(&sbi->s_err_report, print_daily_error_info, 0);
spin_lock_init(&sbi->s_error_lock);
INIT_WORK(&sbi->s_error_work, flush_stashed_error_work);
INIT_WORK(&sbi->s_sb_upd_work, update_super_work);

/* Register extent status tree shrinker */
if (ext4_es_register_shrinker(sbi))
Expand Down Expand Up @@ -5601,16 +5601,13 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
sbi->s_ea_block_cache = NULL;

if (sbi->s_journal) {
/* flush s_error_work before journal destroy. */
flush_work(&sbi->s_error_work);
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
ext4_journal_destroy(sbi, sbi->s_journal);
}
failed_mount3a:
ext4_es_unregister_shrinker(sbi);
failed_mount3:
/* flush s_error_work before sbi destroy */
flush_work(&sbi->s_error_work);
/* flush s_sb_upd_work before sbi destroy */
flush_work(&sbi->s_sb_upd_work);
del_timer_sync(&sbi->s_err_report);
ext4_stop_mmpd(sbi);
ext4_group_desc_free(sbi);
Expand Down Expand Up @@ -5858,7 +5855,7 @@ static journal_t *ext4_get_dev_journal(struct super_block *sb,
return journal;

out_journal:
jbd2_journal_destroy(journal);
ext4_journal_destroy(EXT4_SB(sb), journal);
out_bdev:
ext4_blkdev_put(bdev);
return NULL;
Expand Down Expand Up @@ -5958,8 +5955,7 @@ static int ext4_load_journal(struct super_block *sb,
EXT4_SB(sb)->s_journal = journal;
err = ext4_clear_journal_err(sb, es);
if (err) {
EXT4_SB(sb)->s_journal = NULL;
jbd2_journal_destroy(journal);
ext4_journal_destroy(EXT4_SB(sb), journal);
return err;
}

Expand All @@ -5974,7 +5970,7 @@ static int ext4_load_journal(struct super_block *sb,
return 0;

err_out:
jbd2_journal_destroy(journal);
ext4_journal_destroy(EXT4_SB(sb), journal);
return err;
}

Expand Down Expand Up @@ -6426,7 +6422,7 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
}

/* Flush outstanding errors before changing fs state */
flush_work(&sbi->s_error_work);
flush_work(&sbi->s_sb_upd_work);

if ((bool)(fc->sb_flags & SB_RDONLY) != sb_rdonly(sb)) {
if (ext4_test_mount_flag(sb, EXT4_MF_FS_ABORTED)) {
Expand Down
Loading