Ext4 regression and bug fixes for v5.14-rc1

-----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEEK2m5VNv+CHkogTfJ8vlZVpUNgaMFAmDnU2AACgkQ8vlZVpUN
 gaOBIAgApIAIeGbppf7aFjRN4h4wxfRpr7w6lux3GVmz7D+6djRi21X5dT5xq01m
 u6DkLAcKrCATIidyP6qHlvBbxxcPt2PX1FcQbruj9WcnSng1Ngl7RW8BEqp/eIRo
 Nb7MY0pg8HIJVMEniWQcdEjFWKDL3ksWR9+X3V3nhSzp+0kXFF1ySjk+TWi/ZGSn
 T/Q1sEyeUOiVfV75cIW5JbKoJEgvCvrclFvGJLYVcIAYeqJfQKQ0+tlkhDeYnWfQ
 nZgh1UU350bO629LGIhbRAkLbAloEb0d57mOQCrATo0JFrAZ52+0ZCkrTXtIyoOF
 TUILVf3zsqgdO8HLDkbH1G+lGn9WOA==
 =qU+W
 -----END PGP SIGNATURE-----

Merge tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Pull ext4 updates from Ted Ts'o:
 "Ext4 regression and bug fixes"

* tag 'ext4_for_linus_stable' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4:
  ext4: inline jbd2_journal_[un]register_shrinker()
  ext4: fix flags validity checking for EXT4_IOC_CHECKPOINT
  ext4: fix possible UAF when remounting r/o a mmp-protected file system
  ext4: use ext4_grp_locked_error in mb_find_extent
  ext4: fix WARN_ON_ONCE(!buffer_uptodate) after an error writing the superblock
  Revert "ext4: consolidate checks for resize of bigalloc into ext4_resize_begin"
This commit is contained in:
Linus Torvalds 2021-07-09 09:57:27 -07:00
Родитель 47a7ce6288 0705e8d1e2
Коммит e49d68ce7c
9 изменённых файлов: 115 добавлений и 132 удалений

Просмотреть файл

@ -327,6 +327,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
set_buffer_meta(bh);
set_buffer_prio(bh);
set_buffer_uptodate(bh);
if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh);
/* Errors can only happen due to aborted journal or a nasty bug */
@ -355,7 +356,6 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
err);
}
} else {
set_buffer_uptodate(bh);
if (inode)
mark_buffer_dirty_inode(bh, inode);
else

Просмотреть файл

@ -692,6 +692,13 @@ static long ext4_ioctl_group_add(struct file *file,
if (err)
return err;
if (ext4_has_feature_bigalloc(sb)) {
ext4_msg(sb, KERN_ERR,
"Online resizing not supported with bigalloc");
err = -EOPNOTSUPP;
goto group_add_out;
}
err = mnt_want_write_file(file);
if (err)
goto group_add_out;
@ -816,7 +823,7 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg)
if (!EXT4_SB(sb)->s_journal)
return -ENODEV;
if (flags & ~JBD2_JOURNAL_FLUSH_VALID)
if (flags & ~EXT4_IOC_CHECKPOINT_FLAG_VALID)
return -EINVAL;
q = bdev_get_queue(EXT4_SB(sb)->s_journal->j_dev);
@ -914,6 +921,13 @@ setversion_out:
goto group_extend_out;
}
if (ext4_has_feature_bigalloc(sb)) {
ext4_msg(sb, KERN_ERR,
"Online resizing not supported with bigalloc");
err = -EOPNOTSUPP;
goto group_extend_out;
}
err = mnt_want_write_file(filp);
if (err)
goto group_extend_out;

Просмотреть файл

@ -1909,10 +1909,11 @@ static int mb_find_extent(struct ext4_buddy *e4b, int block,
if (ex->fe_start + ex->fe_len > EXT4_CLUSTERS_PER_GROUP(e4b->bd_sb)) {
/* Should never happen! (but apparently sometimes does?!?) */
WARN_ON(1);
ext4_error(e4b->bd_sb, "corruption or bug in mb_find_extent "
"block=%d, order=%d needed=%d ex=%u/%d/%d@%u",
block, order, needed, ex->fe_group, ex->fe_start,
ex->fe_len, ex->fe_logical);
ext4_grp_locked_error(e4b->bd_sb, e4b->bd_group, 0, 0,
"corruption or bug in mb_find_extent "
"block=%d, order=%d needed=%d ex=%u/%d/%d@%u",
block, order, needed, ex->fe_group, ex->fe_start,
ex->fe_len, ex->fe_logical);
ex->fe_len = 0;
ex->fe_start = 0;
ex->fe_group = 0;

Просмотреть файл

@ -156,7 +156,12 @@ static int kmmpd(void *data)
memcpy(mmp->mmp_nodename, init_utsname()->nodename,
sizeof(mmp->mmp_nodename));
while (!kthread_should_stop()) {
while (!kthread_should_stop() && !sb_rdonly(sb)) {
if (!ext4_has_feature_mmp(sb)) {
ext4_warning(sb, "kmmpd being stopped since MMP feature"
" has been disabled.");
goto wait_to_exit;
}
if (++seq > EXT4_MMP_SEQ_MAX)
seq = 1;
@ -177,16 +182,6 @@ static int kmmpd(void *data)
failed_writes++;
}
if (!(le32_to_cpu(es->s_feature_incompat) &
EXT4_FEATURE_INCOMPAT_MMP)) {
ext4_warning(sb, "kmmpd being stopped since MMP feature"
" has been disabled.");
goto exit_thread;
}
if (sb_rdonly(sb))
break;
diff = jiffies - last_update_time;
if (diff < mmp_update_interval * HZ)
schedule_timeout_interruptible(mmp_update_interval *
@ -207,7 +202,7 @@ static int kmmpd(void *data)
ext4_error_err(sb, -retval,
"error reading MMP data: %d",
retval);
goto exit_thread;
goto wait_to_exit;
}
mmp_check = (struct mmp_struct *)(bh_check->b_data);
@ -221,7 +216,7 @@ static int kmmpd(void *data)
ext4_error_err(sb, EBUSY, "abort");
put_bh(bh_check);
retval = -EBUSY;
goto exit_thread;
goto wait_to_exit;
}
put_bh(bh_check);
}
@ -244,7 +239,13 @@ static int kmmpd(void *data)
retval = write_mmp_block(sb, bh);
exit_thread:
wait_to_exit:
while (!kthread_should_stop()) {
set_current_state(TASK_INTERRUPTIBLE);
if (!kthread_should_stop())
schedule();
}
set_current_state(TASK_RUNNING);
return retval;
}
@ -391,5 +392,3 @@ failed:
brelse(bh);
return 1;
}

Просмотреть файл

@ -74,10 +74,6 @@ int ext4_resize_begin(struct super_block *sb)
return -EPERM;
}
if (ext4_has_feature_bigalloc(sb)) {
ext4_msg(sb, KERN_ERR, "Online resizing not supported with bigalloc");
return -EOPNOTSUPP;
}
if (ext4_has_feature_sparse_super2(sb)) {
ext4_msg(sb, KERN_ERR, "Online resizing not supported with sparse_super2");
return -EOPNOTSUPP;

Просмотреть файл

@ -705,15 +705,23 @@ static void flush_stashed_error_work(struct work_struct *work)
* ext4 error handling code during handling of previous errors.
*/
if (!sb_rdonly(sbi->s_sb) && journal) {
struct buffer_head *sbh = sbi->s_sbh;
handle = jbd2_journal_start(journal, 1);
if (IS_ERR(handle))
goto write_directly;
if (jbd2_journal_get_write_access(handle, sbi->s_sbh)) {
if (jbd2_journal_get_write_access(handle, sbh)) {
jbd2_journal_stop(handle);
goto write_directly;
}
ext4_update_super(sbi->s_sb);
if (jbd2_journal_dirty_metadata(handle, sbi->s_sbh)) {
if (buffer_write_io_error(sbh) || !buffer_uptodate(sbh)) {
ext4_msg(sbi->s_sb, KERN_ERR, "previous I/O error to "
"superblock detected");
clear_buffer_write_io_error(sbh);
set_buffer_uptodate(sbh);
}
if (jbd2_journal_dirty_metadata(handle, sbh)) {
jbd2_journal_stop(handle);
goto write_directly;
}
@ -1176,7 +1184,6 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_sysfs(sb);
if (sbi->s_journal) {
jbd2_journal_unregister_shrinker(sbi->s_journal);
aborted = is_journal_aborted(sbi->s_journal);
err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
@ -5168,7 +5175,6 @@ failed_mount_wq:
sbi->s_ea_block_cache = NULL;
if (sbi->s_journal) {
jbd2_journal_unregister_shrinker(sbi->s_journal);
jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL;
}
@ -5494,12 +5500,6 @@ static int ext4_load_journal(struct super_block *sb,
ext4_commit_super(sb);
}
err = jbd2_journal_register_shrinker(journal);
if (err) {
EXT4_SB(sb)->s_journal = NULL;
goto err_out;
}
return 0;
err_out:
@ -5985,7 +5985,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
*/
ext4_mark_recovery_complete(sb, es);
}
ext4_stop_mmpd(sbi);
} else {
/* Make sure we can mount this feature set readwrite */
if (ext4_has_feature_readonly(sb) ||
@ -6099,6 +6098,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
if (!test_opt(sb, BLOCK_VALIDITY) && sbi->s_system_blks)
ext4_release_system_zone(sb);
if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
ext4_stop_mmpd(sbi);
/*
* Some options can be enabled by ext4 and/or by VFS mount flag
* either way we need to make sure it matches in both *flags and
@ -6132,6 +6134,8 @@ restore_opts:
for (i = 0; i < EXT4_MAXQUOTAS; i++)
kfree(to_free[i]);
#endif
if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
ext4_stop_mmpd(sbi);
kfree(orig_data);
return err;
}

Просмотреть файл

@ -701,7 +701,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh)
__buffer_unlink(jh);
jh->b_cp_transaction = NULL;
percpu_counter_dec(&journal->j_jh_shrink_count);
percpu_counter_dec(&journal->j_checkpoint_jh_count);
jbd2_journal_put_journal_head(jh);
/* Is this transaction empty? */
@ -764,7 +764,7 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh,
jh->b_cpnext->b_cpprev = jh;
}
transaction->t_checkpoint_list = jh;
percpu_counter_inc(&transaction->t_journal->j_jh_shrink_count);
percpu_counter_inc(&transaction->t_journal->j_checkpoint_jh_count);
}
/*

Просмотреть файл

@ -1283,6 +1283,48 @@ static int jbd2_min_tag_size(void)
return sizeof(journal_block_tag_t) - 4;
}
/**
* jbd2_journal_shrink_scan()
*
* Scan the checkpointed buffer on the checkpoint list and release the
* journal_head.
*/
static unsigned long jbd2_journal_shrink_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
journal_t *journal = container_of(shrink, journal_t, j_shrinker);
unsigned long nr_to_scan = sc->nr_to_scan;
unsigned long nr_shrunk;
unsigned long count;
count = percpu_counter_read_positive(&journal->j_checkpoint_jh_count);
trace_jbd2_shrink_scan_enter(journal, sc->nr_to_scan, count);
nr_shrunk = jbd2_journal_shrink_checkpoint_list(journal, &nr_to_scan);
count = percpu_counter_read_positive(&journal->j_checkpoint_jh_count);
trace_jbd2_shrink_scan_exit(journal, nr_to_scan, nr_shrunk, count);
return nr_shrunk;
}
/**
* jbd2_journal_shrink_count()
*
* Count the number of checkpoint buffers on the checkpoint list.
*/
static unsigned long jbd2_journal_shrink_count(struct shrinker *shrink,
struct shrink_control *sc)
{
journal_t *journal = container_of(shrink, journal_t, j_shrinker);
unsigned long count;
count = percpu_counter_read_positive(&journal->j_checkpoint_jh_count);
trace_jbd2_shrink_count(journal, sc->nr_to_scan, count);
return count;
}
/*
* Management for journal control blocks: functions to create and
* destroy journal_t structures, and to initialise and read existing
@ -1361,9 +1403,23 @@ static journal_t *journal_init_common(struct block_device *bdev,
journal->j_sb_buffer = bh;
journal->j_superblock = (journal_superblock_t *)bh->b_data;
journal->j_shrink_transaction = NULL;
journal->j_shrinker.scan_objects = jbd2_journal_shrink_scan;
journal->j_shrinker.count_objects = jbd2_journal_shrink_count;
journal->j_shrinker.seeks = DEFAULT_SEEKS;
journal->j_shrinker.batch = journal->j_max_transaction_buffers;
if (percpu_counter_init(&journal->j_checkpoint_jh_count, 0, GFP_KERNEL))
goto err_cleanup;
if (register_shrinker(&journal->j_shrinker)) {
percpu_counter_destroy(&journal->j_checkpoint_jh_count);
goto err_cleanup;
}
return journal;
err_cleanup:
brelse(journal->j_sb_buffer);
kfree(journal->j_wbuf);
jbd2_journal_destroy_revoke(journal);
kfree(journal);
@ -2050,93 +2106,6 @@ recovery_error:
return -EIO;
}
/**
* jbd2_journal_shrink_scan()
*
* Scan the checkpointed buffer on the checkpoint list and release the
* journal_head.
*/
static unsigned long jbd2_journal_shrink_scan(struct shrinker *shrink,
struct shrink_control *sc)
{
journal_t *journal = container_of(shrink, journal_t, j_shrinker);
unsigned long nr_to_scan = sc->nr_to_scan;
unsigned long nr_shrunk;
unsigned long count;
count = percpu_counter_read_positive(&journal->j_jh_shrink_count);
trace_jbd2_shrink_scan_enter(journal, sc->nr_to_scan, count);
nr_shrunk = jbd2_journal_shrink_checkpoint_list(journal, &nr_to_scan);
count = percpu_counter_read_positive(&journal->j_jh_shrink_count);
trace_jbd2_shrink_scan_exit(journal, nr_to_scan, nr_shrunk, count);
return nr_shrunk;
}
/**
* jbd2_journal_shrink_count()
*
* Count the number of checkpoint buffers on the checkpoint list.
*/
static unsigned long jbd2_journal_shrink_count(struct shrinker *shrink,
struct shrink_control *sc)
{
journal_t *journal = container_of(shrink, journal_t, j_shrinker);
unsigned long count;
count = percpu_counter_read_positive(&journal->j_jh_shrink_count);
trace_jbd2_shrink_count(journal, sc->nr_to_scan, count);
return count;
}
/**
* jbd2_journal_register_shrinker()
* @journal: Journal to act on.
*
* Init a percpu counter to record the checkpointed buffers on the checkpoint
* list and register a shrinker to release their journal_head.
*/
int jbd2_journal_register_shrinker(journal_t *journal)
{
int err;
journal->j_shrink_transaction = NULL;
err = percpu_counter_init(&journal->j_jh_shrink_count, 0, GFP_KERNEL);
if (err)
return err;
journal->j_shrinker.scan_objects = jbd2_journal_shrink_scan;
journal->j_shrinker.count_objects = jbd2_journal_shrink_count;
journal->j_shrinker.seeks = DEFAULT_SEEKS;
journal->j_shrinker.batch = journal->j_max_transaction_buffers;
err = register_shrinker(&journal->j_shrinker);
if (err) {
percpu_counter_destroy(&journal->j_jh_shrink_count);
return err;
}
return 0;
}
EXPORT_SYMBOL(jbd2_journal_register_shrinker);
/**
* jbd2_journal_unregister_shrinker()
* @journal: Journal to act on.
*
* Unregister the checkpointed buffer shrinker and destroy the percpu counter.
*/
void jbd2_journal_unregister_shrinker(journal_t *journal)
{
percpu_counter_destroy(&journal->j_jh_shrink_count);
unregister_shrinker(&journal->j_shrinker);
}
EXPORT_SYMBOL(jbd2_journal_unregister_shrinker);
/**
* jbd2_journal_destroy() - Release a journal_t structure.
* @journal: Journal to act on.
@ -2209,8 +2178,10 @@ int jbd2_journal_destroy(journal_t *journal)
brelse(journal->j_sb_buffer);
}
jbd2_journal_unregister_shrinker(journal);
if (journal->j_shrinker.flags & SHRINKER_REGISTERED) {
percpu_counter_destroy(&journal->j_checkpoint_jh_count);
unregister_shrinker(&journal->j_shrinker);
}
if (journal->j_proc_entry)
jbd2_stats_proc_exit(journal);
iput(journal->j_inode);

Просмотреть файл

@ -918,11 +918,11 @@ struct journal_s
struct shrinker j_shrinker;
/**
* @j_jh_shrink_count:
* @j_checkpoint_jh_count:
*
* Number of journal buffers on the checkpoint list. [j_list_lock]
*/
struct percpu_counter j_jh_shrink_count;
struct percpu_counter j_checkpoint_jh_count;
/**
* @j_shrink_transaction:
@ -1556,8 +1556,6 @@ extern int jbd2_journal_set_features
(journal_t *, unsigned long, unsigned long, unsigned long);
extern void jbd2_journal_clear_features
(journal_t *, unsigned long, unsigned long, unsigned long);
extern int jbd2_journal_register_shrinker(journal_t *journal);
extern void jbd2_journal_unregister_shrinker(journal_t *journal);
extern int jbd2_journal_load (journal_t *journal);
extern int jbd2_journal_destroy (journal_t *);
extern int jbd2_journal_recover (journal_t *journal);