From 9fc7c63a1d6e9920038ced782390a54888ed70a6 Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 17 Apr 2008 10:38:59 -0400 Subject: jbd2: fix the way the b_modified flag is cleared Currently at the start of a journal commit we loop through all of the buffers on the committing transaction and clear the b_modified flag (the flag that is set when a transaction modifies the buffer) under the j_list_lock. The problem is that everywhere else this flag is modified only under the jbd2 lock buffer flag, so it will race with a running transaction who could potentially set it, and have it unset by the committing transaction. This is also a big waste, you can have several thousands of buffers that you are clearing the modified flag on when you may not need to. This patch removes this code and instead clears the b_modified flag upon entering do_get_write_access/journal_get_create_access, so if that transaction does indeed use the buffer then it will be accounted for properly, and if it does not then we know we didn't use it. That will be important for the next patch in this series. Tested thoroughly by myself using postmark/iozone/bonnie++. Cc: Cc: Jan Kara Signed-off-by: Josef Bacik Signed-off-by: Andrew Morton Signed-off-by: "Theodore Ts'o" --- fs/jbd2/transaction.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'fs/jbd2/transaction.c') diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index b9b0b6f899b9..9dc71a6b62e6 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -617,6 +617,12 @@ repeat: jh->b_next_transaction == transaction) goto done; + /* + * this is the first time this transaction is touching this buffer, + * reset the modified flag + */ + jh->b_modified = 0; + /* * If there is already a copy-out version of this buffer, then we don't * need to make another one @@ -829,9 +835,16 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) if (jh->b_transaction == NULL) { jh->b_transaction = transaction; + + /* first access by this transaction */ + jh->b_modified = 0; + JBUFFER_TRACE(jh, "file as BJ_Reserved"); __jbd2_journal_file_buffer(jh, transaction, BJ_Reserved); } else if (jh->b_transaction == journal->j_committing_transaction) { + /* first access by this transaction */ + jh->b_modified = 0; + JBUFFER_TRACE(jh, "set next transaction"); jh->b_next_transaction = transaction; } -- cgit v1.2.3-59-g8ed1b From 1dfc3220d963385a317264b11154c462a83596ed Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 17 Apr 2008 10:38:59 -0400 Subject: jbd2: fix possible journal overflow issues There are several cases where the running transaction can get buffers added to its BJ_Metadata list which it never dirtied, which makes its t_nr_buffers counter end up larger than its t_outstanding_credits counter. This will cause issues when starting new transactions as while we are logging buffers we decrement t_outstanding_buffers, so when t_outstanding_buffers goes negative, we will report that we need less space in the journal than we actually need, so transactions will be started even though there may not be enough room for them. In the worst case scenario (which admittedly is almost impossible to reproduce) this will result in the journal running out of space. The fix is to only refile buffers from the committing transaction to the running transactions BJ_Modified list when b_modified is set on that journal, which is the only way to be sure if the running transaction has modified that buffer. This patch also fixes an accounting error in journal_forget, it is possible that we can call journal_forget on a buffer without having modified it, only gotten write access to it, so instead of freeing a credit, we only do so if the buffer was modified. The assert will help catch if this problem occurs. Without these two patches I could hit this assert within minutes of running postmark, with them this issue no longer arises. Cc: Cc: Jan Kara Signed-off-by: Josef Bacik Signed-off-by: Andrew Morton Signed-off-by: "Theodore Ts'o" --- fs/jbd2/commit.c | 3 +++ fs/jbd2/transaction.c | 21 ++++++++++++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) (limited to 'fs/jbd2/transaction.c') diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 988fbec1143c..e0139786f717 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -568,6 +568,9 @@ void jbd2_journal_commit_transaction(journal_t *journal) stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits; stats.u.run.rs_blocks_logged = 0; + J_ASSERT(commit_transaction->t_nr_buffers <= + commit_transaction->t_outstanding_credits); + descriptor = NULL; bufs = 0; while (commit_transaction->t_buffers) { diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 9dc71a6b62e6..70245d6638b7 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1243,6 +1243,7 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) struct journal_head *jh; int drop_reserve = 0; int err = 0; + int was_modified = 0; BUFFER_TRACE(bh, "entry"); @@ -1261,6 +1262,9 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) goto not_jbd; } + /* keep track of wether or not this transaction modified us */ + was_modified = jh->b_modified; + /* * The buffer's going from the transaction, we must drop * all references -bzzz @@ -1278,7 +1282,12 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) JBUFFER_TRACE(jh, "belongs to current transaction: unfile"); - drop_reserve = 1; + /* + * we only want to drop a reference if this transaction + * modified the buffer + */ + if (was_modified) + drop_reserve = 1; /* * We are no longer going to journal this buffer. @@ -1318,7 +1327,13 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh) if (jh->b_next_transaction) { J_ASSERT(jh->b_next_transaction == transaction); jh->b_next_transaction = NULL; - drop_reserve = 1; + + /* + * only drop a reference if this transaction modified + * the buffer + */ + if (was_modified) + drop_reserve = 1; } } @@ -2090,7 +2105,7 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh) jh->b_transaction = jh->b_next_transaction; jh->b_next_transaction = NULL; __jbd2_journal_file_buffer(jh, jh->b_transaction, - was_dirty ? BJ_Metadata : BJ_Reserved); + jh->b_modified ? BJ_Metadata : BJ_Reserved); J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING); if (was_dirty) -- cgit v1.2.3-59-g8ed1b From 5648ba5b2dc0d07a8108fabc7b9100962e9e1d88 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 17 Apr 2008 10:38:59 -0400 Subject: jbd2: fix kernel-doc notation Fix kernel-doc notation in jbd2. Signed-off-by: Randy Dunlap Signed-off-by: Mingming Cao Signed-off-by: Andrew Morton Signed-off-by: "Theodore Ts'o" --- fs/jbd2/journal.c | 5 +++-- fs/jbd2/transaction.c | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'fs/jbd2/transaction.c') diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 2dbf23e82c6f..d707a219e21b 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -997,13 +997,14 @@ fail: */ /** - * journal_t * jbd2_journal_init_dev() - creates an initialises a journal structure + * journal_t * jbd2_journal_init_dev() - creates and initialises a journal structure * @bdev: Block device on which to create the journal * @fs_dev: Device which hold journalled filesystem for this journal. * @start: Block nr Start of journal. * @len: Length of the journal in blocks. * @blocksize: blocksize of journalling device - * @returns: a newly created journal_t * + * + * Returns: a newly created journal_t * * * jbd2_journal_init_dev creates a journal which maps a fixed contiguous * range of blocks on an arbitrary block device. diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 70245d6638b7..401bf9d23656 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -1462,7 +1462,8 @@ int jbd2_journal_stop(handle_t *handle) return err; } -/**int jbd2_journal_force_commit() - force any uncommitted transactions +/** + * int jbd2_journal_force_commit() - force any uncommitted transactions * @journal: journal to force * * For synchronous operations: force any uncommitted transactions -- cgit v1.2.3-59-g8ed1b From 329d291f50d53f77d15769051f3eb494a9fd54b7 Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Thu, 17 Apr 2008 10:38:59 -0400 Subject: jdb2: replace remaining __FUNCTION__ occurrences __FUNCTION__ is gcc-specific, use __func__ Signed-off-by: Harvey Harrison Cc: Signed-off-by: Andrew Morton Signed-off-by: "Theodore Ts'o" --- fs/jbd2/journal.c | 18 +++++++++--------- fs/jbd2/revoke.c | 2 +- fs/jbd2/transaction.c | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'fs/jbd2/transaction.c') diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 64356e85a10f..53632e3e8457 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -534,7 +534,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) if (!tid_geq(journal->j_commit_request, tid)) { printk(KERN_EMERG "%s: error: j_commit_request=%d, tid=%d\n", - __FUNCTION__, journal->j_commit_request, tid); + __func__, journal->j_commit_request, tid); } spin_unlock(&journal->j_state_lock); #endif @@ -599,7 +599,7 @@ int jbd2_journal_bmap(journal_t *journal, unsigned long blocknr, printk(KERN_ALERT "%s: journal block not found " "at offset %lu on %s\n", - __FUNCTION__, + __func__, blocknr, bdevname(journal->j_dev, b)); err = -EIO; @@ -1028,7 +1028,7 @@ journal_t * jbd2_journal_init_dev(struct block_device *bdev, journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL); if (!journal->j_wbuf) { printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n", - __FUNCTION__); + __func__); kfree(journal); journal = NULL; goto out; @@ -1084,7 +1084,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) journal->j_wbuf = kmalloc(n * sizeof(struct buffer_head*), GFP_KERNEL); if (!journal->j_wbuf) { printk(KERN_ERR "%s: Cant allocate bhs for commit thread\n", - __FUNCTION__); + __func__); kfree(journal); return NULL; } @@ -1093,7 +1093,7 @@ journal_t * jbd2_journal_init_inode (struct inode *inode) /* If that failed, give up */ if (err) { printk(KERN_ERR "%s: Cannnot locate journal superblock\n", - __FUNCTION__); + __func__); kfree(journal); return NULL; } @@ -1179,7 +1179,7 @@ int jbd2_journal_create(journal_t *journal) */ printk(KERN_EMERG "%s: creation of journal on external device!\n", - __FUNCTION__); + __func__); BUG(); } @@ -1999,7 +1999,7 @@ static struct journal_head *journal_alloc_journal_head(void) jbd_debug(1, "out of memory for journal_head\n"); if (time_after(jiffies, last_warning + 5*HZ)) { printk(KERN_NOTICE "ENOMEM in %s, retrying.\n", - __FUNCTION__); + __func__); last_warning = jiffies; } while (!ret) { @@ -2136,13 +2136,13 @@ static void __journal_remove_journal_head(struct buffer_head *bh) if (jh->b_frozen_data) { printk(KERN_WARNING "%s: freeing " "b_frozen_data\n", - __FUNCTION__); + __func__); jbd2_free(jh->b_frozen_data, bh->b_size); } if (jh->b_committed_data) { printk(KERN_WARNING "%s: freeing " "b_committed_data\n", - __FUNCTION__); + __func__); jbd2_free(jh->b_committed_data, bh->b_size); } bh->b_private = NULL; diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index 144a2065f03e..257ff2625765 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -139,7 +139,7 @@ repeat: oom: if (!journal_oom_retry) return -ENOMEM; - jbd_debug(1, "ENOMEM in %s, retrying\n", __FUNCTION__); + jbd_debug(1, "ENOMEM in %s, retrying\n", __func__); yield(); goto repeat; } diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 401bf9d23656..d6e006e67804 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -696,7 +696,7 @@ repeat: if (!frozen_buffer) { printk(KERN_EMERG "%s: OOM for frozen_buffer\n", - __FUNCTION__); + __func__); JBUFFER_TRACE(jh, "oom!"); error = -ENOMEM; jbd_lock_bh_state(bh); @@ -914,7 +914,7 @@ repeat: committed_data = jbd2_alloc(jh2bh(jh)->b_size, GFP_NOFS); if (!committed_data) { printk(KERN_EMERG "%s: No memory for committed data\n", - __FUNCTION__); + __func__); err = -ENOMEM; goto out; } -- cgit v1.2.3-59-g8ed1b