summaryrefslogtreecommitdiff
path: root/fs/jbd2
diff options
context:
space:
mode:
Diffstat (limited to 'fs/jbd2')
-rw-r--r--fs/jbd2/checkpoint.c43
-rw-r--r--fs/jbd2/commit.c80
-rw-r--r--fs/jbd2/journal.c141
-rw-r--r--fs/jbd2/revoke.c8
-rw-r--r--fs/jbd2/transaction.c157
5 files changed, 216 insertions, 213 deletions
diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c
index de89c5bef607..513273712010 100644
--- a/fs/jbd2/checkpoint.c
+++ b/fs/jbd2/checkpoint.c
@@ -267,7 +267,15 @@ restart:
*/
BUFFER_TRACE(bh, "queue");
get_bh(bh);
- J_ASSERT_BH(bh, !buffer_jwrite(bh));
+ if (WARN_ON_ONCE(buffer_jwrite(bh))) {
+ put_bh(bh); /* drop the ref we just took */
+ spin_unlock(&journal->j_list_lock);
+ /* Clean up any previously batched buffers */
+ if (batch_count)
+ __flush_batch(journal, &batch_count);
+ jbd2_journal_abort(journal, -EFSCORRUPTED);
+ return -EFSCORRUPTED;
+ }
journal->j_chkpt_bhs[batch_count++] = bh;
transaction->t_chp_stats.cs_written++;
transaction->t_checkpoint_list = jh->b_cpnext;
@@ -325,7 +333,10 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
if (!jbd2_journal_get_log_tail(journal, &first_tid, &blocknr))
return 1;
- J_ASSERT(blocknr != 0);
+ if (WARN_ON_ONCE(blocknr == 0)) {
+ jbd2_journal_abort(journal, -EFSCORRUPTED);
+ return -EFSCORRUPTED;
+ }
/*
* We need to make sure that any blocks that were recently written out
@@ -347,15 +358,16 @@ int jbd2_cleanup_journal_tail(journal_t *journal)
/*
* journal_shrink_one_cp_list
*
- * Find all the written-back checkpoint buffers in the given list
- * and try to release them. If the whole transaction is released, set
- * the 'released' parameter. Return the number of released checkpointed
- * buffers.
+ * Find written-back checkpoint buffers in the given list and try to release
+ * them. If 'nr_to_scan' is set, scan at most that many buffers. If the whole
+ * transaction is released, set the 'released' parameter. Return the number of
+ * released checkpointed buffers.
*
* Called with j_list_lock held.
*/
static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
enum jbd2_shrink_type type,
+ unsigned long *nr_to_scan,
bool *released)
{
struct journal_head *last_jh;
@@ -364,13 +376,15 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
int ret;
*released = false;
- if (!jh)
+ if (!jh || (nr_to_scan && !*nr_to_scan))
return 0;
last_jh = jh->b_cpprev;
do {
jh = next_jh;
next_jh = jh->b_cpnext;
+ if (nr_to_scan)
+ (*nr_to_scan)--;
if (type == JBD2_SHRINK_DESTROY) {
ret = __jbd2_journal_remove_checkpoint(jh);
@@ -378,7 +392,7 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
ret = jbd2_journal_try_remove_checkpoint(jh);
if (ret < 0) {
if (type == JBD2_SHRINK_BUSY_SKIP)
- continue;
+ goto next;
break;
}
}
@@ -389,9 +403,10 @@ static unsigned long journal_shrink_one_cp_list(struct journal_head *jh,
break;
}
+next:
if (need_resched())
break;
- } while (jh != last_jh);
+ } while (jh != last_jh && (!nr_to_scan || *nr_to_scan));
return nr_freed;
}
@@ -413,7 +428,6 @@ unsigned long jbd2_journal_shrink_checkpoint_list(journal_t *journal,
tid_t first_tid = 0, last_tid = 0, next_tid = 0;
tid_t tid = 0;
unsigned long nr_freed = 0;
- unsigned long freed;
bool first_set = false;
again:
@@ -446,10 +460,9 @@ again:
next_transaction = transaction->t_cpnext;
tid = transaction->t_tid;
- freed = journal_shrink_one_cp_list(transaction->t_checkpoint_list,
- JBD2_SHRINK_BUSY_SKIP, &released);
- nr_freed += freed;
- (*nr_to_scan) -= min(*nr_to_scan, freed);
+ nr_freed += journal_shrink_one_cp_list(transaction->t_checkpoint_list,
+ JBD2_SHRINK_BUSY_SKIP,
+ nr_to_scan, &released);
if (*nr_to_scan == 0)
break;
if (need_resched() || spin_needbreak(&journal->j_list_lock))
@@ -505,7 +518,7 @@ void __jbd2_journal_clean_checkpoint_list(journal_t *journal,
transaction = next_transaction;
next_transaction = transaction->t_cpnext;
journal_shrink_one_cp_list(transaction->t_checkpoint_list,
- type, &released);
+ type, NULL, &released);
/*
* This function only frees up some memory if possible so we
* dont have an obligation to finish processing. Bail out if
diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c
index 7203d2d2624d..3029cb6f6d64 100644
--- a/fs/jbd2/commit.c
+++ b/fs/jbd2/commit.c
@@ -29,8 +29,10 @@
/*
* IO end handler for temporary buffer_heads handling writes to the journal.
*/
-static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
+static void journal_end_buffer_io_sync(struct bio *bio)
{
+ struct buffer_head *bh;
+ bool uptodate = bio_endio_bh(bio, &bh);
struct buffer_head *orig_bh = bh->b_private;
BUFFER_TRACE(bh, "");
@@ -39,9 +41,7 @@ static void journal_end_buffer_io_sync(struct buffer_head *bh, int uptodate)
else
clear_buffer_uptodate(bh);
if (orig_bh) {
- clear_bit_unlock(BH_Shadow, &orig_bh->b_state);
- smp_mb__after_atomic();
- wake_up_bit(&orig_bh->b_state, BH_Shadow);
+ clear_and_wake_up_bit(BH_Shadow, &orig_bh->b_state);
}
unlock_buffer(bh);
}
@@ -147,13 +147,12 @@ static int journal_submit_commit_record(journal_t *journal,
lock_buffer(bh);
clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
- bh->b_end_io = journal_end_buffer_io_sync;
if (journal->j_flags & JBD2_BARRIER &&
!jbd2_has_feature_async_commit(journal))
write_flags |= REQ_PREFLUSH | REQ_FUA;
- submit_bh(write_flags, bh);
+ bh_submit(bh, write_flags, journal_end_buffer_io_sync);
*cbh = bh;
return 0;
}
@@ -180,7 +179,13 @@ static int journal_wait_on_commit_record(journal_t *journal,
/* Send all the data buffers related to an inode */
int jbd2_submit_inode_data(journal_t *journal, struct jbd2_inode *jinode)
{
- if (!jinode || !(jinode->i_flags & JI_WRITE_DATA))
+ unsigned long flags;
+
+ if (!jinode)
+ return 0;
+
+ flags = READ_ONCE(jinode->i_flags);
+ if (!(flags & JI_WRITE_DATA))
return 0;
trace_jbd2_submit_inode_data(jinode->i_vfs_inode);
@@ -191,12 +196,30 @@ EXPORT_SYMBOL(jbd2_submit_inode_data);
int jbd2_wait_inode_data(journal_t *journal, struct jbd2_inode *jinode)
{
- if (!jinode || !(jinode->i_flags & JI_WAIT_DATA) ||
- !jinode->i_vfs_inode || !jinode->i_vfs_inode->i_mapping)
+ struct address_space *mapping;
+ struct inode *inode;
+ unsigned long flags;
+ loff_t start_byte, end_byte;
+
+ if (!jinode)
+ return 0;
+
+ flags = READ_ONCE(jinode->i_flags);
+ if (!(flags & JI_WAIT_DATA))
+ return 0;
+
+ inode = jinode->i_vfs_inode;
+ if (!inode)
+ return 0;
+
+ mapping = inode->i_mapping;
+ if (!mapping)
+ return 0;
+
+ if (!jbd2_jinode_get_dirty_range(jinode, &start_byte, &end_byte))
return 0;
return filemap_fdatawait_range_keep_errors(
- jinode->i_vfs_inode->i_mapping, jinode->i_dirty_start,
- jinode->i_dirty_end);
+ mapping, start_byte, end_byte);
}
EXPORT_SYMBOL(jbd2_wait_inode_data);
@@ -218,7 +241,8 @@ static int journal_submit_data_buffers(journal_t *journal,
list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) {
if (!(jinode->i_flags & JI_WRITE_DATA))
continue;
- jinode->i_flags |= JI_COMMIT_RUNNING;
+ WRITE_ONCE(jinode->i_flags,
+ jinode->i_flags | JI_COMMIT_RUNNING);
spin_unlock(&journal->j_list_lock);
/* submit the inode data buffers. */
trace_jbd2_submit_inode_data(jinode->i_vfs_inode);
@@ -229,7 +253,8 @@ static int journal_submit_data_buffers(journal_t *journal,
}
spin_lock(&journal->j_list_lock);
J_ASSERT(jinode->i_transaction == commit_transaction);
- jinode->i_flags &= ~JI_COMMIT_RUNNING;
+ WRITE_ONCE(jinode->i_flags,
+ jinode->i_flags & ~JI_COMMIT_RUNNING);
smp_mb();
wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING);
}
@@ -240,10 +265,13 @@ static int journal_submit_data_buffers(journal_t *journal,
int jbd2_journal_finish_inode_data_buffers(struct jbd2_inode *jinode)
{
struct address_space *mapping = jinode->i_vfs_inode->i_mapping;
+ loff_t start_byte, end_byte;
+
+ if (!jbd2_jinode_get_dirty_range(jinode, &start_byte, &end_byte))
+ return 0;
return filemap_fdatawait_range_keep_errors(mapping,
- jinode->i_dirty_start,
- jinode->i_dirty_end);
+ start_byte, end_byte);
}
/*
@@ -262,7 +290,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal,
list_for_each_entry(jinode, &commit_transaction->t_inode_list, i_list) {
if (!(jinode->i_flags & JI_WAIT_DATA))
continue;
- jinode->i_flags |= JI_COMMIT_RUNNING;
+ WRITE_ONCE(jinode->i_flags, jinode->i_flags | JI_COMMIT_RUNNING);
spin_unlock(&journal->j_list_lock);
/* wait for the inode data buffers writeout. */
if (journal->j_finish_inode_data_buffers) {
@@ -272,7 +300,7 @@ static int journal_finish_inode_data_buffers(journal_t *journal,
}
cond_resched();
spin_lock(&journal->j_list_lock);
- jinode->i_flags &= ~JI_COMMIT_RUNNING;
+ WRITE_ONCE(jinode->i_flags, jinode->i_flags & ~JI_COMMIT_RUNNING);
smp_mb();
wake_up_bit(&jinode->i_flags, __JI_COMMIT_RUNNING);
}
@@ -288,8 +316,8 @@ static int journal_finish_inode_data_buffers(journal_t *journal,
&jinode->i_transaction->t_inode_list);
} else {
jinode->i_transaction = NULL;
- jinode->i_dirty_start = 0;
- jinode->i_dirty_end = 0;
+ WRITE_ONCE(jinode->i_dirty_start_page, 0);
+ WRITE_ONCE(jinode->i_dirty_end_page, 0);
}
}
spin_unlock(&journal->j_list_lock);
@@ -484,10 +512,8 @@ void jbd2_journal_commit_transaction(journal_t *journal)
* leave undo-committed data.
*/
if (jh->b_committed_data) {
- struct buffer_head *bh = jh2bh(jh);
-
spin_lock(&jh->b_state_lock);
- jbd2_free(jh->b_committed_data, bh->b_size);
+ kfree(jh->b_committed_data);
jh->b_committed_data = NULL;
spin_unlock(&jh->b_state_lock);
}
@@ -722,9 +748,9 @@ start_journal_io:
lock_buffer(bh);
clear_buffer_dirty(bh);
set_buffer_uptodate(bh);
- bh->b_end_io = journal_end_buffer_io_sync;
- submit_bh(REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
- bh);
+ bh_submit(bh,
+ REQ_OP_WRITE | JBD2_JOURNAL_REQ_FLAGS,
+ journal_end_buffer_io_sync);
}
cond_resched();
@@ -948,7 +974,7 @@ restart_loop:
* its triggers if they exist, so we can clear that too.
*/
if (jh->b_committed_data) {
- jbd2_free(jh->b_committed_data, bh->b_size);
+ kfree(jh->b_committed_data);
jh->b_committed_data = NULL;
if (jh->b_frozen_data) {
jh->b_committed_data = jh->b_frozen_data;
@@ -956,7 +982,7 @@ restart_loop:
jh->b_frozen_triggers = NULL;
}
} else if (jh->b_frozen_data) {
- jbd2_free(jh->b_frozen_data, bh->b_size);
+ kfree(jh->b_frozen_data);
jh->b_frozen_data = NULL;
jh->b_frozen_triggers = NULL;
}
diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c
index cb2c529a8f1b..00f5a98f3d4f 100644
--- a/fs/jbd2/journal.c
+++ b/fs/jbd2/journal.c
@@ -94,8 +94,7 @@ EXPORT_SYMBOL(jbd2_journal_init_jbd_inode);
EXPORT_SYMBOL(jbd2_journal_release_jbd_inode);
EXPORT_SYMBOL(jbd2_journal_begin_ordered_truncate);
EXPORT_SYMBOL(jbd2_inode_cache);
-
-static int jbd2_journal_create_slab(size_t slab_size);
+EXPORT_SYMBOL(jbd2_handle_cache);
#ifdef CONFIG_JBD2_DEBUG
void __jbd2_debug(int level, const char *file, const char *func,
@@ -385,10 +384,10 @@ int jbd2_journal_write_metadata_buffer(transaction_t *transaction,
goto escape_done;
spin_unlock(&jh_in->b_state_lock);
- tmp = jbd2_alloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
+ tmp = kmalloc(bh_in->b_size, GFP_NOFS | __GFP_NOFAIL);
spin_lock(&jh_in->b_state_lock);
if (jh_in->b_frozen_data) {
- jbd2_free(tmp, bh_in->b_size);
+ kfree(tmp);
goto copy_done;
}
@@ -1677,7 +1676,7 @@ journal_t *jbd2_journal_init_inode(struct inode *inode)
return err ? ERR_PTR(err) : ERR_PTR(-EINVAL);
}
- jbd2_debug(1, "JBD2: inode %s/%ld, size %lld, bits %d, blksize %ld\n",
+ jbd2_debug(1, "JBD2: inode %s/%llu, size %lld, bits %d, blksize %ld\n",
inode->i_sb->s_id, inode->i_ino, (long long) inode->i_size,
inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize);
@@ -1689,7 +1688,7 @@ journal_t *jbd2_journal_init_inode(struct inode *inode)
journal->j_inode = inode;
snprintf(journal->j_devname, sizeof(journal->j_devname),
- "%pg-%lu", journal->j_dev, journal->j_inode->i_ino);
+ "%pg-%llu", journal->j_dev, journal->j_inode->i_ino);
strreplace(journal->j_devname, '/', '!');
jbd2_stats_proc_init(journal);
@@ -1820,9 +1819,7 @@ static int jbd2_write_superblock(journal_t *journal, blk_opf_t write_flags)
}
if (jbd2_journal_has_csum_v2or3(journal))
sb->s_checksum = jbd2_superblock_csum(sb);
- get_bh(bh);
- bh->b_end_io = end_buffer_write_sync;
- submit_bh(REQ_OP_WRITE | write_flags, bh);
+ bh_submit(bh, REQ_OP_WRITE | write_flags, bh_end_write);
wait_on_buffer(bh);
if (buffer_write_io_error(bh)) {
clear_buffer_write_io_error(bh);
@@ -2064,14 +2061,6 @@ EXPORT_SYMBOL(jbd2_journal_update_sb_errno);
int jbd2_journal_load(journal_t *journal)
{
int err;
- journal_superblock_t *sb = journal->j_superblock;
-
- /*
- * Create a slab for this blocksize
- */
- err = jbd2_journal_create_slab(be32_to_cpu(sb->s_blocksize));
- if (err)
- return err;
/* Let the recovery code check whether it needs to recover any
* data from the journal. */
@@ -2263,6 +2252,8 @@ jbd2_journal_initialize_fast_commit(journal_t *journal)
unsigned long long num_fc_blks;
num_fc_blks = jbd2_journal_get_num_fc_blks(sb);
+ if (num_fc_blks > journal->j_last)
+ return -EFSCORRUPTED;
if (journal->j_last - num_fc_blks < JBD2_MIN_JOURNAL_BLOCKS)
return -ENOSPC;
@@ -2700,108 +2691,6 @@ size_t journal_tag_bytes(journal_t *journal)
}
/*
- * JBD memory management
- *
- * These functions are used to allocate block-sized chunks of memory
- * used for making copies of buffer_head data. Very often it will be
- * page-sized chunks of data, but sometimes it will be in
- * sub-page-size chunks. (For example, 16k pages on Power systems
- * with a 4k block file system.) For blocks smaller than a page, we
- * use a SLAB allocator. There are slab caches for each block size,
- * which are allocated at mount time, if necessary, and we only free
- * (all of) the slab caches when/if the jbd2 module is unloaded. For
- * this reason we don't need to a mutex to protect access to
- * jbd2_slab[] allocating or releasing memory; only in
- * jbd2_journal_create_slab().
- */
-#define JBD2_MAX_SLABS 8
-static struct kmem_cache *jbd2_slab[JBD2_MAX_SLABS];
-
-static const char *jbd2_slab_names[JBD2_MAX_SLABS] = {
- "jbd2_1k", "jbd2_2k", "jbd2_4k", "jbd2_8k",
- "jbd2_16k", "jbd2_32k", "jbd2_64k", "jbd2_128k"
-};
-
-
-static void jbd2_journal_destroy_slabs(void)
-{
- int i;
-
- for (i = 0; i < JBD2_MAX_SLABS; i++) {
- kmem_cache_destroy(jbd2_slab[i]);
- jbd2_slab[i] = NULL;
- }
-}
-
-static int jbd2_journal_create_slab(size_t size)
-{
- static DEFINE_MUTEX(jbd2_slab_create_mutex);
- int i = order_base_2(size) - 10;
- size_t slab_size;
-
- if (size == PAGE_SIZE)
- return 0;
-
- if (i >= JBD2_MAX_SLABS)
- return -EINVAL;
-
- if (unlikely(i < 0))
- i = 0;
- mutex_lock(&jbd2_slab_create_mutex);
- if (jbd2_slab[i]) {
- mutex_unlock(&jbd2_slab_create_mutex);
- return 0; /* Already created */
- }
-
- slab_size = 1 << (i+10);
- jbd2_slab[i] = kmem_cache_create(jbd2_slab_names[i], slab_size,
- slab_size, 0, NULL);
- mutex_unlock(&jbd2_slab_create_mutex);
- if (!jbd2_slab[i]) {
- printk(KERN_EMERG "JBD2: no memory for jbd2_slab cache\n");
- return -ENOMEM;
- }
- return 0;
-}
-
-static struct kmem_cache *get_slab(size_t size)
-{
- int i = order_base_2(size) - 10;
-
- BUG_ON(i >= JBD2_MAX_SLABS);
- if (unlikely(i < 0))
- i = 0;
- BUG_ON(jbd2_slab[i] == NULL);
- return jbd2_slab[i];
-}
-
-void *jbd2_alloc(size_t size, gfp_t flags)
-{
- void *ptr;
-
- BUG_ON(size & (size-1)); /* Must be a power of 2 */
-
- if (size < PAGE_SIZE)
- ptr = kmem_cache_alloc(get_slab(size), flags);
- else
- ptr = (void *)__get_free_pages(flags, get_order(size));
-
- /* Check alignment; SLUB has gotten this wrong in the past,
- * and this can lead to user data corruption! */
- BUG_ON(((unsigned long) ptr) & (size-1));
-
- return ptr;
-}
-
-void jbd2_free(void *ptr, size_t size)
-{
- if (size < PAGE_SIZE)
- kmem_cache_free(get_slab(size), ptr);
- else
- free_pages((unsigned long)ptr, get_order(size));
-};
-
-/*
* Journal_head storage management
*/
static struct kmem_cache *jbd2_journal_head_cache;
@@ -2974,15 +2863,15 @@ static void __journal_remove_journal_head(struct buffer_head *bh)
clear_buffer_jbd(bh);
}
-static void journal_release_journal_head(struct journal_head *jh, size_t b_size)
+static void journal_release_journal_head(struct journal_head *jh)
{
if (jh->b_frozen_data) {
printk(KERN_WARNING "%s: freeing b_frozen_data\n", __func__);
- jbd2_free(jh->b_frozen_data, b_size);
+ kfree(jh->b_frozen_data);
}
if (jh->b_committed_data) {
printk(KERN_WARNING "%s: freeing b_committed_data\n", __func__);
- jbd2_free(jh->b_committed_data, b_size);
+ kfree(jh->b_committed_data);
}
journal_free_journal_head(jh);
}
@@ -3001,7 +2890,7 @@ void jbd2_journal_put_journal_head(struct journal_head *jh)
if (!jh->b_jcount) {
__journal_remove_journal_head(bh);
jbd_unlock_bh_journal_head(bh);
- journal_release_journal_head(jh, bh->b_size);
+ journal_release_journal_head(jh);
__brelse(bh);
} else {
jbd_unlock_bh_journal_head(bh);
@@ -3018,8 +2907,8 @@ void jbd2_journal_init_jbd_inode(struct jbd2_inode *jinode, struct inode *inode)
jinode->i_next_transaction = NULL;
jinode->i_vfs_inode = inode;
jinode->i_flags = 0;
- jinode->i_dirty_start = 0;
- jinode->i_dirty_end = 0;
+ jinode->i_dirty_start_page = 0;
+ jinode->i_dirty_end_page = 0;
INIT_LIST_HEAD(&jinode->i_list);
}
@@ -3143,7 +3032,6 @@ static void jbd2_journal_destroy_caches(void)
jbd2_journal_destroy_handle_cache();
jbd2_journal_destroy_inode_cache();
jbd2_journal_destroy_transaction_cache();
- jbd2_journal_destroy_slabs();
}
static int __init journal_init(void)
@@ -3176,4 +3064,3 @@ MODULE_DESCRIPTION("Generic filesystem journal-writing module");
MODULE_LICENSE("GPL");
module_init(journal_init);
module_exit(journal_exit);
-
diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c
index 9016ddb82447..e4c2fbd381f1 100644
--- a/fs/jbd2/revoke.c
+++ b/fs/jbd2/revoke.c
@@ -428,6 +428,7 @@ void jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
journal_t *journal = handle->h_transaction->t_journal;
int need_cancel;
struct buffer_head *bh = jh2bh(jh);
+ struct address_space *bh_mapping = bh->b_folio->mapping;
jbd2_debug(4, "journal_head %p, cancelling revoke\n", jh);
@@ -464,13 +465,14 @@ void jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh)
* buffer_head? If so, we'd better make sure we clear the
* revoked status on any hashed alias too, otherwise the revoke
* state machine will get very upset later on. */
- if (need_cancel) {
+ if (need_cancel && !sb_is_blkdev_sb(bh_mapping->host->i_sb)) {
struct buffer_head *bh2;
+
bh2 = __find_get_block_nonatomic(bh->b_bdev, bh->b_blocknr,
bh->b_size);
if (bh2) {
- if (bh2 != bh)
- clear_buffer_revoked(bh2);
+ WARN_ON_ONCE(bh2 == bh);
+ clear_buffer_revoked(bh2);
__brelse(bh2);
}
}
diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c
index dca4b5d8aaaa..5cc7d097b2ac 100644
--- a/fs/jbd2/transaction.c
+++ b/fs/jbd2/transaction.c
@@ -474,7 +474,8 @@ handle_t *jbd2__journal_start(journal_t *journal, int nblocks, int rsv_blocks,
return ERR_PTR(-EROFS);
if (handle) {
- J_ASSERT(handle->h_transaction->t_journal == journal);
+ if (WARN_ON_ONCE(handle->h_transaction->t_journal != journal))
+ return ERR_PTR(-EINVAL);
handle->h_ref++;
return handle;
}
@@ -1036,7 +1037,13 @@ repeat:
*/
if (!jh->b_transaction) {
JBUFFER_TRACE(jh, "no transaction");
- J_ASSERT_JH(jh, !jh->b_next_transaction);
+ if (WARN_ON_ONCE(jh->b_next_transaction)) {
+ spin_unlock(&jh->b_state_lock);
+ unlock_buffer(bh);
+ error = -EINVAL;
+ jbd2_journal_abort(journal, error);
+ goto out;
+ }
JBUFFER_TRACE(jh, "file as BJ_Reserved");
/*
* Make sure all stores to jh (b_modified, b_frozen_data) are
@@ -1069,13 +1076,27 @@ repeat:
*/
if (jh->b_frozen_data) {
JBUFFER_TRACE(jh, "has frozen data");
- J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
+ if (WARN_ON_ONCE(jh->b_next_transaction)) {
+ spin_unlock(&jh->b_state_lock);
+ error = -EINVAL;
+ jbd2_journal_abort(journal, error);
+ goto out;
+ }
goto attach_next;
}
JBUFFER_TRACE(jh, "owned by older transaction");
- J_ASSERT_JH(jh, jh->b_next_transaction == NULL);
- J_ASSERT_JH(jh, jh->b_transaction == journal->j_committing_transaction);
+ if (WARN_ON_ONCE(jh->b_next_transaction ||
+ jh->b_transaction !=
+ journal->j_committing_transaction)) {
+ pr_err("JBD2: %s: assertion failure: b_next_transaction=%p b_transaction=%p j_committing_transaction=%p\n",
+ journal->j_devname, jh->b_next_transaction,
+ jh->b_transaction, journal->j_committing_transaction);
+ spin_unlock(&jh->b_state_lock);
+ error = -EINVAL;
+ jbd2_journal_abort(journal, error);
+ goto out;
+ }
/*
* There is one case we have to be very careful about. If the
@@ -1110,7 +1131,7 @@ repeat:
if (!frozen_buffer) {
JBUFFER_TRACE(jh, "allocate memory for buffer");
spin_unlock(&jh->b_state_lock);
- frozen_buffer = jbd2_alloc(jh2bh(jh)->b_size,
+ frozen_buffer = kmalloc(jh2bh(jh)->b_size,
GFP_NOFS | __GFP_NOFAIL);
goto repeat;
}
@@ -1138,7 +1159,7 @@ done:
out:
if (unlikely(frozen_buffer)) /* It's usually NULL */
- jbd2_free(frozen_buffer, bh->b_size);
+ kfree(frozen_buffer);
JBUFFER_TRACE(jh, "exit");
return error;
@@ -1302,7 +1323,12 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh)
goto out;
}
- J_ASSERT_JH(jh, buffer_locked(jh2bh(jh)));
+ if (WARN_ON_ONCE(!buffer_locked(jh2bh(jh)))) {
+ err = -EINVAL;
+ spin_unlock(&jh->b_state_lock);
+ jbd2_journal_abort(journal, err);
+ goto out;
+ }
if (jh->b_transaction == NULL) {
/*
@@ -1398,7 +1424,7 @@ int jbd2_journal_get_undo_access(handle_t *handle, struct buffer_head *bh)
repeat:
if (!jh->b_committed_data)
- committed_data = jbd2_alloc(jh2bh(jh)->b_size,
+ committed_data = kmalloc(jh2bh(jh)->b_size,
GFP_NOFS|__GFP_NOFAIL);
spin_lock(&jh->b_state_lock);
@@ -1419,7 +1445,7 @@ repeat:
out:
jbd2_journal_put_journal_head(jh);
if (unlikely(committed_data))
- jbd2_free(committed_data, bh->b_size);
+ kfree(committed_data);
return err;
}
@@ -1490,14 +1516,19 @@ void jbd2_buffer_abort_trigger(struct journal_head *jh,
*/
int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
{
- transaction_t *transaction = handle->h_transaction;
+ transaction_t *transaction;
journal_t *journal;
struct journal_head *jh;
int ret = 0;
+ if (is_handle_aborted(handle))
+ return -EROFS;
if (!buffer_jbd(bh))
return -EUCLEAN;
+ transaction = handle->h_transaction;
+ journal = transaction->t_journal;
+
/*
* We don't grab jh reference here since the buffer must be part
* of the running transaction.
@@ -1515,8 +1546,14 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
if (data_race(jh->b_transaction != transaction &&
jh->b_next_transaction != transaction)) {
spin_lock(&jh->b_state_lock);
- J_ASSERT_JH(jh, jh->b_transaction == transaction ||
- jh->b_next_transaction == transaction);
+ if (WARN_ON_ONCE(jh->b_transaction != transaction &&
+ jh->b_next_transaction != transaction)) {
+ pr_err("JBD2: %s: assertion failure: b_transaction=%p transaction=%p b_next_transaction=%p\n",
+ journal->j_devname, jh->b_transaction,
+ transaction, jh->b_next_transaction);
+ ret = -EINVAL;
+ goto out_unlock_bh;
+ }
spin_unlock(&jh->b_state_lock);
}
if (data_race(jh->b_modified == 1)) {
@@ -1524,15 +1561,15 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
if (data_race(jh->b_transaction == transaction &&
jh->b_jlist != BJ_Metadata)) {
spin_lock(&jh->b_state_lock);
- if (jh->b_transaction == transaction &&
- jh->b_jlist != BJ_Metadata)
- pr_err("JBD2: assertion failure: h_type=%u "
- "h_line_no=%u block_no=%llu jlist=%u\n",
+ if (WARN_ON_ONCE(jh->b_transaction == transaction &&
+ jh->b_jlist != BJ_Metadata)) {
+ pr_err("JBD2: assertion failure: h_type=%u h_line_no=%u block_no=%llu jlist=%u\n",
handle->h_type, handle->h_line_no,
(unsigned long long) bh->b_blocknr,
jh->b_jlist);
- J_ASSERT_JH(jh, jh->b_transaction != transaction ||
- jh->b_jlist == BJ_Metadata);
+ ret = -EINVAL;
+ goto out_unlock_bh;
+ }
spin_unlock(&jh->b_state_lock);
}
goto out;
@@ -1552,8 +1589,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
goto out_unlock_bh;
}
- journal = transaction->t_journal;
-
if (jh->b_modified == 0) {
/*
* This buffer's got modified and becoming part
@@ -1631,7 +1666,10 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh)
}
/* That test should have eliminated the following case: */
- J_ASSERT_JH(jh, jh->b_frozen_data == NULL);
+ if (WARN_ON_ONCE(jh->b_frozen_data)) {
+ ret = -EINVAL;
+ goto out_unlock_bh;
+ }
JBUFFER_TRACE(jh, "file as BJ_Metadata");
spin_lock(&journal->j_list_lock);
@@ -1670,6 +1708,7 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh)
int err = 0;
int was_modified = 0;
int wait_for_writeback = 0;
+ int abort_journal = 0;
if (is_handle_aborted(handle))
return -EROFS;
@@ -1703,7 +1742,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh)
jh->b_modified = 0;
if (jh->b_transaction == transaction) {
- J_ASSERT_JH(jh, !jh->b_frozen_data);
+ if (WARN_ON_ONCE(jh->b_frozen_data)) {
+ err = -EINVAL;
+ abort_journal = 1;
+ goto drop;
+ }
/* If we are forgetting a buffer which is already part
* of this transaction, then we can just drop it from
@@ -1742,8 +1785,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh)
}
spin_unlock(&journal->j_list_lock);
} else if (jh->b_transaction) {
- J_ASSERT_JH(jh, (jh->b_transaction ==
- journal->j_committing_transaction));
+ if (WARN_ON_ONCE(jh->b_transaction != journal->j_committing_transaction)) {
+ err = -EINVAL;
+ abort_journal = 1;
+ goto drop;
+ }
/* However, if the buffer is still owned by a prior
* (committing) transaction, we can't drop it yet... */
JBUFFER_TRACE(jh, "belongs to older transaction");
@@ -1761,7 +1807,11 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh)
jh->b_next_transaction = transaction;
spin_unlock(&journal->j_list_lock);
} else {
- J_ASSERT(jh->b_next_transaction == transaction);
+ if (WARN_ON_ONCE(jh->b_next_transaction != transaction)) {
+ err = -EINVAL;
+ abort_journal = 1;
+ goto drop;
+ }
/*
* only drop a reference if this transaction modified
@@ -1807,6 +1857,8 @@ int jbd2_journal_forget(handle_t *handle, struct buffer_head *bh)
drop:
__brelse(bh);
spin_unlock(&jh->b_state_lock);
+ if (abort_journal)
+ jbd2_journal_abort(journal, err);
if (wait_for_writeback)
wait_on_buffer(bh);
jbd2_journal_put_journal_head(jh);
@@ -2131,7 +2183,8 @@ bool jbd2_journal_try_to_free_buffers(journal_t *journal, struct folio *folio)
struct buffer_head *bh;
bool ret = false;
- J_ASSERT(folio_test_locked(folio));
+ if (WARN_ON_ONCE(!folio_test_locked(folio)))
+ return false;
head = folio_buffers(folio);
bh = head;
@@ -2646,23 +2699,32 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode,
{
transaction_t *transaction = handle->h_transaction;
journal_t *journal;
+ pgoff_t start_page, end_page;
+ int err = 0;
+ int abort_transaction = 0;
if (is_handle_aborted(handle))
return -EROFS;
journal = transaction->t_journal;
- jbd2_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino,
+ jbd2_debug(4, "Adding inode %llu, tid:%d\n", jinode->i_vfs_inode->i_ino,
transaction->t_tid);
+ start_page = (pgoff_t)(start_byte >> PAGE_SHIFT);
+ end_page = (pgoff_t)(end_byte >> PAGE_SHIFT) + 1;
+
spin_lock(&journal->j_list_lock);
- jinode->i_flags |= flags;
+ WRITE_ONCE(jinode->i_flags, jinode->i_flags | flags);
- if (jinode->i_dirty_end) {
- jinode->i_dirty_start = min(jinode->i_dirty_start, start_byte);
- jinode->i_dirty_end = max(jinode->i_dirty_end, end_byte);
+ if (jinode->i_dirty_start_page != jinode->i_dirty_end_page) {
+ WRITE_ONCE(jinode->i_dirty_start_page,
+ min(jinode->i_dirty_start_page, start_page));
+ WRITE_ONCE(jinode->i_dirty_end_page,
+ max(jinode->i_dirty_end_page, end_page));
} else {
- jinode->i_dirty_start = start_byte;
- jinode->i_dirty_end = end_byte;
+ /* Publish a new non-empty range by making end visible first. */
+ WRITE_ONCE(jinode->i_dirty_end_page, end_page);
+ WRITE_ONCE(jinode->i_dirty_start_page, start_page);
}
/* Is inode already attached where we need it? */
@@ -2680,20 +2742,33 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode,
/* On some different transaction's list - should be
* the committing one */
if (jinode->i_transaction) {
- J_ASSERT(jinode->i_next_transaction == NULL);
- J_ASSERT(jinode->i_transaction ==
- journal->j_committing_transaction);
+ if (WARN_ON_ONCE(jinode->i_next_transaction ||
+ jinode->i_transaction !=
+ journal->j_committing_transaction)) {
+ pr_err("JBD2: %s: assertion failure: i_next_transaction=%p i_transaction=%p j_committing_transaction=%p\n",
+ journal->j_devname, jinode->i_next_transaction,
+ jinode->i_transaction,
+ journal->j_committing_transaction);
+ err = -EINVAL;
+ abort_transaction = 1;
+ goto done;
+ }
jinode->i_next_transaction = transaction;
goto done;
}
/* Not on any transaction list... */
- J_ASSERT(!jinode->i_next_transaction);
+ if (WARN_ON_ONCE(jinode->i_next_transaction)) {
+ err = -EINVAL;
+ abort_transaction = 1;
+ goto done;
+ }
jinode->i_transaction = transaction;
list_add(&jinode->i_list, &transaction->t_inode_list);
done:
spin_unlock(&journal->j_list_lock);
-
- return 0;
+ if (abort_transaction)
+ jbd2_journal_abort(journal, err);
+ return err;
}
int jbd2_journal_inode_ranged_write(handle_t *handle,
@@ -2739,7 +2814,7 @@ int jbd2_journal_begin_ordered_truncate(journal_t *journal,
int ret = 0;
/* This is a quick check to avoid locking if not necessary */
- if (!jinode->i_transaction)
+ if (!READ_ONCE(jinode->i_transaction))
goto out;
/* Locks are here just to force reading of recent values, it is
* enough that the transaction was not committing before we started