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:
Коммит
e49d68ce7c
|
@ -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);
|
||||
|
|
Загрузка…
Ссылка в новой задаче