From: Zhang Yi yi.zhang@huawei.com
hulk inclusion category: bugfix bugzilla: 50788 CVE: NA ---------------------------
fcf37549ae19 ("jbd2: ensure abort the journal if detect IO error when writing original buffer back") add 'j_atomic_flags', and 4ba3fcdde7e3 ("jbd2,ext4: add a shrinker to release checkpointed buffers") add j_shrinker_*, which can lead lots of kabi broken on jbd2 module.
Fix it by add a wrapper.
Signed-off-by: Zhang Yi yi.zhang@huawei.com Reviewed-by: Yang Erkun yangerkun@huawei.com Signed-off-by: Yang Yingliang yangyingliang@huawei.com --- fs/jbd2/checkpoint.c | 26 ++++++++++++----- fs/jbd2/journal.c | 55 +++++++++++++++++++++-------------- include/linux/jbd2.h | 69 ++++++++++++++++++++++++++------------------ 3 files changed, 92 insertions(+), 58 deletions(-)
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 035a1fe46e065..fdf50dbad6edd 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -509,6 +509,8 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal, tid_t tid = 0; unsigned long nr_freed = 0; unsigned long nr_scanned = *nr_to_scan; + journal_wrapper_t *journal_wrapper = container_of(journal, + journal_wrapper_t, jw_journal);
again: spin_lock(&journal->j_list_lock); @@ -523,8 +525,8 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal, * from the checkpoint list, we ignore saved j_shrink_transaction * and start over unconditionally. */ - if (journal->j_shrink_transaction) - transaction = journal->j_shrink_transaction; + if (journal_wrapper->j_shrink_transaction) + transaction = journal_wrapper->j_shrink_transaction; else transaction = journal->j_checkpoint_transactions;
@@ -557,10 +559,10 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal, } while (transaction != last_transaction);
if (transaction != last_transaction) { - journal->j_shrink_transaction = next_transaction; + journal_wrapper->j_shrink_transaction = next_transaction; next_tid = next_transaction->t_tid; } else { - journal->j_shrink_transaction = NULL; + journal_wrapper->j_shrink_transaction = NULL; next_tid = 0; }
@@ -674,6 +676,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) struct transaction_chp_stats_s *stats; transaction_t *transaction; journal_t *journal; + journal_wrapper_t *journal_wrapper; struct buffer_head *bh = jh2bh(jh);
JBUFFER_TRACE(jh, "entry"); @@ -684,6 +687,7 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) return 0; } journal = transaction->t_journal; + journal_wrapper = container_of(journal, journal_wrapper_t, jw_journal);
JBUFFER_TRACE(jh, "removing from transaction");
@@ -695,11 +699,11 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) * journal here and we abort the journal later from a better context. */ if (buffer_write_io_error(bh)) - set_bit(JBD2_CHECKPOINT_IO_ERROR, &journal->j_atomic_flags); + set_bit(JBD2_CHECKPOINT_IO_ERROR, &journal_wrapper->j_atomic_flags);
__buffer_unlink(jh); jh->b_cp_transaction = NULL; - percpu_counter_dec(&journal->j_checkpoint_jh_count); + percpu_counter_dec(&journal_wrapper->j_checkpoint_jh_count); jbd2_journal_put_journal_head(jh);
/* Is this transaction empty? */ @@ -745,6 +749,8 @@ int __jbd2_journal_remove_checkpoint(struct journal_head *jh) void __jbd2_journal_insert_checkpoint(struct journal_head *jh, transaction_t *transaction) { + journal_wrapper_t *journal_wrapper; + JBUFFER_TRACE(jh, "entry"); J_ASSERT_JH(jh, buffer_dirty(jh2bh(jh)) || buffer_jbddirty(jh2bh(jh))); J_ASSERT_JH(jh, jh->b_cp_transaction == NULL); @@ -762,7 +768,9 @@ 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_checkpoint_jh_count); + + journal_wrapper = container_of(transaction->t_journal, journal_wrapper_t, jw_journal); + percpu_counter_inc(&journal_wrapper->j_checkpoint_jh_count); }
/* @@ -777,9 +785,11 @@ void __jbd2_journal_insert_checkpoint(struct journal_head *jh,
void __jbd2_journal_drop_transaction(journal_t *journal, transaction_t *transaction) { + journal_wrapper_t *journal_wrapper = container_of(journal, + journal_wrapper_t, jw_journal); assert_spin_locked(&journal->j_list_lock);
- journal->j_shrink_transaction = NULL; + journal_wrapper->j_shrink_transaction = NULL; if (transaction->t_cpnext) { transaction->t_cpnext->t_cpprev = transaction->t_cpprev; transaction->t_cpprev->t_cpnext = transaction->t_cpnext; diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 7984a918e635e..d0bc80c3e330f 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1123,17 +1123,19 @@ static int jbd2_min_tag_size(void) static unsigned long jbd2_journal_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) { - journal_t *journal = container_of(shrink, journal_t, j_shrinker); + journal_wrapper_t *journal_wrapper = container_of(shrink, + journal_wrapper_t, j_shrinker); + journal_t *journal = &journal_wrapper->jw_journal; 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); + count = percpu_counter_read_positive(&journal_wrapper->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); + count = percpu_counter_read_positive(&journal_wrapper->j_checkpoint_jh_count); trace_jbd2_shrink_scan_exit(journal, nr_to_scan, nr_shrunk, count);
return nr_shrunk; @@ -1147,10 +1149,12 @@ static unsigned long jbd2_journal_shrink_scan(struct shrinker *shrink, static unsigned long jbd2_journal_shrink_count(struct shrinker *shrink, struct shrink_control *sc) { - journal_t *journal = container_of(shrink, journal_t, j_shrinker); + journal_wrapper_t *journal_wrapper = container_of(shrink, + journal_wrapper_t, j_shrinker); + journal_t *journal = &journal_wrapper->jw_journal; unsigned long count;
- count = percpu_counter_read_positive(&journal->j_checkpoint_jh_count); + count = percpu_counter_read_positive(&journal_wrapper->j_checkpoint_jh_count); trace_jbd2_shrink_count(journal, sc->nr_to_scan, count);
return count; @@ -1171,14 +1175,17 @@ static journal_t *journal_init_common(struct block_device *bdev, { static struct lock_class_key jbd2_trans_commit_key; journal_t *journal; + journal_wrapper_t *journal_wrapper; int err; struct buffer_head *bh; int n;
- journal = kzalloc(sizeof(*journal), GFP_KERNEL); - if (!journal) + journal_wrapper = kzalloc(sizeof(*journal_wrapper), GFP_KERNEL); + if (!journal_wrapper) return NULL;
+ journal = &(journal_wrapper->jw_journal); + init_waitqueue_head(&journal->j_wait_transaction_locked); init_waitqueue_head(&journal->j_wait_done_commit); init_waitqueue_head(&journal->j_wait_commit); @@ -1232,17 +1239,17 @@ 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; + journal_wrapper->j_shrink_transaction = NULL; + journal_wrapper->j_shrinker.scan_objects = jbd2_journal_shrink_scan; + journal_wrapper->j_shrinker.count_objects = jbd2_journal_shrink_count; + journal_wrapper->j_shrinker.seeks = DEFAULT_SEEKS; + journal_wrapper->j_shrinker.batch = journal->j_max_transaction_buffers;
- if (percpu_counter_init(&journal->j_checkpoint_jh_count, 0, GFP_KERNEL)) + if (percpu_counter_init(&journal_wrapper->j_checkpoint_jh_count, 0, GFP_KERNEL)) goto err_cleanup;
- if (register_shrinker(&journal->j_shrinker)) { - percpu_counter_destroy(&journal->j_checkpoint_jh_count); + if (register_shrinker(&journal_wrapper->j_shrinker)) { + percpu_counter_destroy(&journal_wrapper->j_checkpoint_jh_count); goto err_cleanup; } return journal; @@ -1251,7 +1258,7 @@ static journal_t *journal_init_common(struct block_device *bdev, brelse(journal->j_sb_buffer); kfree(journal->j_wbuf); jbd2_journal_destroy_revoke(journal); - kfree(journal); + kfree(journal_wrapper); return NULL; }
@@ -1481,11 +1488,13 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid, unsigned long tail_block, int write_op) { journal_superblock_t *sb = journal->j_superblock; + journal_wrapper_t *journal_wrapper = container_of(journal, + journal_wrapper_t, jw_journal); int ret;
if (is_journal_aborted(journal)) return -EIO; - if (test_bit(JBD2_CHECKPOINT_IO_ERROR, &journal->j_atomic_flags)) { + if (test_bit(JBD2_CHECKPOINT_IO_ERROR, &journal_wrapper->j_atomic_flags)) { jbd2_journal_abort(journal, -EIO); return -EIO; } @@ -1810,6 +1819,8 @@ int jbd2_journal_load(journal_t *journal) int jbd2_journal_destroy(journal_t *journal) { int err = 0; + journal_wrapper_t *journal_wrapper = container_of(journal, + journal_wrapper_t, jw_journal);
/* Wait for the commit thread to wake up and die. */ journal_kill_thread(journal); @@ -1851,7 +1862,7 @@ int jbd2_journal_destroy(journal_t *journal) * may become inconsistent. */ if (!is_journal_aborted(journal) && - test_bit(JBD2_CHECKPOINT_IO_ERROR, &journal->j_atomic_flags)) + test_bit(JBD2_CHECKPOINT_IO_ERROR, &journal_wrapper->j_atomic_flags)) jbd2_journal_abort(journal, -EIO);
if (journal->j_sb_buffer) { @@ -1871,9 +1882,9 @@ int jbd2_journal_destroy(journal_t *journal) brelse(journal->j_sb_buffer); }
- if (journal->j_shrinker.flags & SHRINKER_REGISTERED) { - percpu_counter_destroy(&journal->j_checkpoint_jh_count); - unregister_shrinker(&journal->j_shrinker); + if (journal_wrapper->j_shrinker.flags & SHRINKER_REGISTERED) { + percpu_counter_destroy(&journal_wrapper->j_checkpoint_jh_count); + unregister_shrinker(&journal_wrapper->j_shrinker); } if (journal->j_proc_entry) jbd2_stats_proc_exit(journal); @@ -1883,7 +1894,7 @@ int jbd2_journal_destroy(journal_t *journal) if (journal->j_chksum_driver) crypto_free_shash(journal->j_chksum_driver); kfree(journal->j_wbuf); - kfree(journal); + kfree(journal_wrapper);
return err; } diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index 5ecab235ed6a6..2192a10e97758 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -105,6 +105,8 @@ typedef struct jbd2_journal_handle handle_t; /* Atomic operation type */ * This is an opaque datatype. **/ typedef struct journal_s journal_t; /* Journal control structure */ + +typedef struct journal_wrapper_s journal_wrapper_t; #endif
/* @@ -780,11 +782,6 @@ struct journal_s */ unsigned long j_flags;
- /** - * @j_atomic_flags: Atomic journaling state flags. - */ - unsigned long j_atomic_flags; - /** * @j_errno: * @@ -901,29 +898,6 @@ struct journal_s */ struct buffer_head *j_chkpt_bhs[JBD2_NR_BATCH];
- /** - * @j_shrinker: - * - * Journal head shrinker, reclaim buffer's journal head which - * has been written back. - */ - struct shrinker j_shrinker; - - /** - * @j_checkpoint_jh_count: - * - * Number of journal buffers on the checkpoint list. [j_list_lock] - */ - struct percpu_counter j_checkpoint_jh_count; - - /** - * @j_shrink_transaction: - * - * Record next transaction will shrink on the checkpoint list. - * [j_list_lock] - */ - transaction_t *j_shrink_transaction; - /** * @j_head: * @@ -1222,6 +1196,45 @@ struct journal_s #endif };
+/** + * struct journal_wrapper_s - The wrapper of journal_s to fix KABI. + */ +struct journal_wrapper_s +{ + /** + * @jw_journal: real journal. + */ + journal_t jw_journal; + + /** + * @j_atomic_flags: Atomic journaling state flags. + */ + unsigned long j_atomic_flags; + + /** + * @j_shrinker: + * + * Journal head shrinker, reclaim buffer's journal head which + * has been written back. + */ + struct shrinker j_shrinker; + + /** + * @j_checkpoint_jh_count: + * + * Number of journal buffers on the checkpoint list. [j_list_lock] + */ + struct percpu_counter j_checkpoint_jh_count; + + /** + * @j_shrink_transaction: + * + * Record next transaction will shrink on the checkpoint list. + * [j_list_lock] + */ + transaction_t *j_shrink_transaction; +}; + #define jbd2_might_wait_for_commit(j) \ do { \ rwsem_acquire(&j->j_trans_commit_map, 0, 0, _THIS_IP_); \