Commit e49d68ce authored by Linus Torvalds's avatar Linus Torvalds

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"
parents 47a7ce62 0705e8d1
...@@ -327,6 +327,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line, ...@@ -327,6 +327,7 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
set_buffer_meta(bh); set_buffer_meta(bh);
set_buffer_prio(bh); set_buffer_prio(bh);
set_buffer_uptodate(bh);
if (ext4_handle_valid(handle)) { if (ext4_handle_valid(handle)) {
err = jbd2_journal_dirty_metadata(handle, bh); err = jbd2_journal_dirty_metadata(handle, bh);
/* Errors can only happen due to aborted journal or a nasty bug */ /* 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, ...@@ -355,7 +356,6 @@ int __ext4_handle_dirty_metadata(const char *where, unsigned int line,
err); err);
} }
} else { } else {
set_buffer_uptodate(bh);
if (inode) if (inode)
mark_buffer_dirty_inode(bh, inode); mark_buffer_dirty_inode(bh, inode);
else else
......
...@@ -692,6 +692,13 @@ static long ext4_ioctl_group_add(struct file *file, ...@@ -692,6 +692,13 @@ static long ext4_ioctl_group_add(struct file *file,
if (err) if (err)
return 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); err = mnt_want_write_file(file);
if (err) if (err)
goto group_add_out; goto group_add_out;
...@@ -816,7 +823,7 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg) ...@@ -816,7 +823,7 @@ static int ext4_ioctl_checkpoint(struct file *filp, unsigned long arg)
if (!EXT4_SB(sb)->s_journal) if (!EXT4_SB(sb)->s_journal)
return -ENODEV; return -ENODEV;
if (flags & ~JBD2_JOURNAL_FLUSH_VALID) if (flags & ~EXT4_IOC_CHECKPOINT_FLAG_VALID)
return -EINVAL; return -EINVAL;
q = bdev_get_queue(EXT4_SB(sb)->s_journal->j_dev); q = bdev_get_queue(EXT4_SB(sb)->s_journal->j_dev);
...@@ -914,6 +921,13 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -914,6 +921,13 @@ static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
goto group_extend_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); err = mnt_want_write_file(filp);
if (err) if (err)
goto group_extend_out; goto group_extend_out;
......
...@@ -1909,10 +1909,11 @@ static int mb_find_extent(struct ext4_buddy *e4b, int block, ...@@ -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)) { if (ex->fe_start + ex->fe_len > EXT4_CLUSTERS_PER_GROUP(e4b->bd_sb)) {
/* Should never happen! (but apparently sometimes does?!?) */ /* Should never happen! (but apparently sometimes does?!?) */
WARN_ON(1); WARN_ON(1);
ext4_error(e4b->bd_sb, "corruption or bug in mb_find_extent " ext4_grp_locked_error(e4b->bd_sb, e4b->bd_group, 0, 0,
"block=%d, order=%d needed=%d ex=%u/%d/%d@%u", "corruption or bug in mb_find_extent "
block, order, needed, ex->fe_group, ex->fe_start, "block=%d, order=%d needed=%d ex=%u/%d/%d@%u",
ex->fe_len, ex->fe_logical); block, order, needed, ex->fe_group, ex->fe_start,
ex->fe_len, ex->fe_logical);
ex->fe_len = 0; ex->fe_len = 0;
ex->fe_start = 0; ex->fe_start = 0;
ex->fe_group = 0; ex->fe_group = 0;
......
...@@ -156,7 +156,12 @@ static int kmmpd(void *data) ...@@ -156,7 +156,12 @@ static int kmmpd(void *data)
memcpy(mmp->mmp_nodename, init_utsname()->nodename, memcpy(mmp->mmp_nodename, init_utsname()->nodename,
sizeof(mmp->mmp_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) if (++seq > EXT4_MMP_SEQ_MAX)
seq = 1; seq = 1;
...@@ -177,16 +182,6 @@ static int kmmpd(void *data) ...@@ -177,16 +182,6 @@ static int kmmpd(void *data)
failed_writes++; 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; diff = jiffies - last_update_time;
if (diff < mmp_update_interval * HZ) if (diff < mmp_update_interval * HZ)
schedule_timeout_interruptible(mmp_update_interval * schedule_timeout_interruptible(mmp_update_interval *
...@@ -207,7 +202,7 @@ static int kmmpd(void *data) ...@@ -207,7 +202,7 @@ static int kmmpd(void *data)
ext4_error_err(sb, -retval, ext4_error_err(sb, -retval,
"error reading MMP data: %d", "error reading MMP data: %d",
retval); retval);
goto exit_thread; goto wait_to_exit;
} }
mmp_check = (struct mmp_struct *)(bh_check->b_data); mmp_check = (struct mmp_struct *)(bh_check->b_data);
...@@ -221,7 +216,7 @@ static int kmmpd(void *data) ...@@ -221,7 +216,7 @@ static int kmmpd(void *data)
ext4_error_err(sb, EBUSY, "abort"); ext4_error_err(sb, EBUSY, "abort");
put_bh(bh_check); put_bh(bh_check);
retval = -EBUSY; retval = -EBUSY;
goto exit_thread; goto wait_to_exit;
} }
put_bh(bh_check); put_bh(bh_check);
} }
...@@ -244,7 +239,13 @@ static int kmmpd(void *data) ...@@ -244,7 +239,13 @@ static int kmmpd(void *data)
retval = write_mmp_block(sb, bh); 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; return retval;
} }
...@@ -391,5 +392,3 @@ int ext4_multi_mount_protect(struct super_block *sb, ...@@ -391,5 +392,3 @@ int ext4_multi_mount_protect(struct super_block *sb,
brelse(bh); brelse(bh);
return 1; return 1;
} }
...@@ -74,10 +74,6 @@ int ext4_resize_begin(struct super_block *sb) ...@@ -74,10 +74,6 @@ int ext4_resize_begin(struct super_block *sb)
return -EPERM; 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)) { if (ext4_has_feature_sparse_super2(sb)) {
ext4_msg(sb, KERN_ERR, "Online resizing not supported with sparse_super2"); ext4_msg(sb, KERN_ERR, "Online resizing not supported with sparse_super2");
return -EOPNOTSUPP; return -EOPNOTSUPP;
......
...@@ -705,15 +705,23 @@ static void flush_stashed_error_work(struct work_struct *work) ...@@ -705,15 +705,23 @@ static void flush_stashed_error_work(struct work_struct *work)
* ext4 error handling code during handling of previous errors. * ext4 error handling code during handling of previous errors.
*/ */
if (!sb_rdonly(sbi->s_sb) && journal) { if (!sb_rdonly(sbi->s_sb) && journal) {
struct buffer_head *sbh = sbi->s_sbh;
handle = jbd2_journal_start(journal, 1); handle = jbd2_journal_start(journal, 1);
if (IS_ERR(handle)) if (IS_ERR(handle))
goto write_directly; 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); jbd2_journal_stop(handle);
goto write_directly; goto write_directly;
} }
ext4_update_super(sbi->s_sb); 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); jbd2_journal_stop(handle);
goto write_directly; goto write_directly;
} }
...@@ -1176,7 +1184,6 @@ static void ext4_put_super(struct super_block *sb) ...@@ -1176,7 +1184,6 @@ static void ext4_put_super(struct super_block *sb)
ext4_unregister_sysfs(sb); ext4_unregister_sysfs(sb);
if (sbi->s_journal) { if (sbi->s_journal) {
jbd2_journal_unregister_shrinker(sbi->s_journal);
aborted = is_journal_aborted(sbi->s_journal); aborted = is_journal_aborted(sbi->s_journal);
err = jbd2_journal_destroy(sbi->s_journal); err = jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL; sbi->s_journal = NULL;
...@@ -5168,7 +5175,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent) ...@@ -5168,7 +5175,6 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
sbi->s_ea_block_cache = NULL; sbi->s_ea_block_cache = NULL;
if (sbi->s_journal) { if (sbi->s_journal) {
jbd2_journal_unregister_shrinker(sbi->s_journal);
jbd2_journal_destroy(sbi->s_journal); jbd2_journal_destroy(sbi->s_journal);
sbi->s_journal = NULL; sbi->s_journal = NULL;
} }
...@@ -5494,12 +5500,6 @@ static int ext4_load_journal(struct super_block *sb, ...@@ -5494,12 +5500,6 @@ static int ext4_load_journal(struct super_block *sb,
ext4_commit_super(sb); ext4_commit_super(sb);
} }
err = jbd2_journal_register_shrinker(journal);
if (err) {
EXT4_SB(sb)->s_journal = NULL;
goto err_out;
}
return 0; return 0;
err_out: err_out:
...@@ -5985,7 +5985,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -5985,7 +5985,6 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
*/ */
ext4_mark_recovery_complete(sb, es); ext4_mark_recovery_complete(sb, es);
} }
ext4_stop_mmpd(sbi);
} else { } else {
/* Make sure we can mount this feature set readwrite */ /* Make sure we can mount this feature set readwrite */
if (ext4_has_feature_readonly(sb) || if (ext4_has_feature_readonly(sb) ||
...@@ -6099,6 +6098,9 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -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) if (!test_opt(sb, BLOCK_VALIDITY) && sbi->s_system_blks)
ext4_release_system_zone(sb); 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 * 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 * either way we need to make sure it matches in both *flags and
...@@ -6132,6 +6134,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data) ...@@ -6132,6 +6134,8 @@ static int ext4_remount(struct super_block *sb, int *flags, char *data)
for (i = 0; i < EXT4_MAXQUOTAS; i++) for (i = 0; i < EXT4_MAXQUOTAS; i++)
kfree(to_free[i]); kfree(to_free[i]);
#endif #endif
if (!ext4_has_feature_mmp(sb) || sb_rdonly(sb))
ext4_stop_mmpd(sbi);
kfree(orig_data); kfree(orig_data);
return err; return err;
} }
......
...@@ -701,7 +701,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) ...@@ -701,7 +701,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh)
__buffer_unlink(jh); __buffer_unlink(jh);
jh->b_cp_transaction = NULL; 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); jbd2_journal_put_journal_head(jh);
/* Is this transaction empty? */ /* Is this transaction empty? */
...@@ -764,7 +764,7 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh, ...@@ -764,7 +764,7 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh,
jh->b_cpnext->b_cpprev = jh; jh->b_cpnext->b_cpprev = jh;
} }
transaction->t_checkpoint_list = 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) ...@@ -1283,6 +1283,48 @@ static int jbd2_min_tag_size(void)
return sizeof(journal_block_tag_t) - 4; 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 * Management for journal control blocks: functions to create and
* destroy journal_t structures, and to initialise and read existing * destroy journal_t structures, and to initialise and read existing
...@@ -1361,9 +1403,23 @@ static journal_t *journal_init_common(struct block_device *bdev, ...@@ -1361,9 +1403,23 @@ static journal_t *journal_init_common(struct block_device *bdev,
journal->j_sb_buffer = bh; journal->j_sb_buffer = bh;
journal->j_superblock = (journal_superblock_t *)bh->b_data; 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; return journal;
err_cleanup: err_cleanup:
brelse(journal->j_sb_buffer);
kfree(journal->j_wbuf); kfree(journal->j_wbuf);
jbd2_journal_destroy_revoke(journal); jbd2_journal_destroy_revoke(journal);
kfree(journal); kfree(journal);
...@@ -2050,93 +2106,6 @@ int jbd2_journal_load(journal_t *journal) ...@@ -2050,93 +2106,6 @@ int jbd2_journal_load(journal_t *journal)
return -EIO; 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. * jbd2_journal_destroy() - Release a journal_t structure.
* @journal: Journal to act on. * @journal: Journal to act on.
...@@ -2209,8 +2178,10 @@ int jbd2_journal_destroy(journal_t *journal) ...@@ -2209,8 +2178,10 @@ int jbd2_journal_destroy(journal_t *journal)
brelse(journal->j_sb_buffer); 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) if (journal->j_proc_entry)
jbd2_stats_proc_exit(journal); jbd2_stats_proc_exit(journal);
iput(journal->j_inode); iput(journal->j_inode);
......
...@@ -918,11 +918,11 @@ struct journal_s ...@@ -918,11 +918,11 @@ struct journal_s
struct shrinker j_shrinker; struct shrinker j_shrinker;
/** /**
* @j_jh_shrink_count: * @j_checkpoint_jh_count:
* *
* Number of journal buffers on the checkpoint list. [j_list_lock] * 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: * @j_shrink_transaction:
...@@ -1556,8 +1556,6 @@ extern int jbd2_journal_set_features ...@@ -1556,8 +1556,6 @@ extern int jbd2_journal_set_features
(journal_t *, unsigned long, unsigned long, unsigned long); (journal_t *, unsigned long, unsigned long, unsigned long);
extern void jbd2_journal_clear_features extern void jbd2_journal_clear_features
(journal_t *, unsigned long, unsigned long, unsigned long); (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_load (journal_t *journal);
extern int jbd2_journal_destroy (journal_t *); extern int jbd2_journal_destroy (journal_t *);
extern int jbd2_journal_recover (journal_t *journal); extern int jbd2_journal_recover (journal_t *journal);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment