aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@redhat.com>2008-04-28 05:16:12 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-28 11:58:44 -0400
commit5b9a499d77e9dd39c9e6611ea10c56a31604f274 (patch)
treea3a2099c3abe67623aa52b803889d65855b28414 /fs
parent5bc833feaa8b2236265764e7e81f44937be46eda (diff)
jbd: 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. Thank you, Signed-off-by: Josef Bacik <jbacik@redhat.com> Cc: <linux-ext4@vger.kernel.org> Acked-by: Jan Kara <jack@ucw.cz> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs')
-rw-r--r--fs/jbd/commit.c3
-rw-r--r--fs/jbd/transaction.c21
2 files changed, 21 insertions, 3 deletions
diff --git a/fs/jbd/commit.c b/fs/jbd/commit.c
index d2f652e6e22e..cd931ef1f000 100644
--- a/fs/jbd/commit.c
+++ b/fs/jbd/commit.c
@@ -472,6 +472,9 @@ void journal_commit_transaction(journal_t *journal)
472 */ 472 */
473 commit_transaction->t_state = T_COMMIT; 473 commit_transaction->t_state = T_COMMIT;
474 474
475 J_ASSERT(commit_transaction->t_nr_buffers <=
476 commit_transaction->t_outstanding_credits);
477
475 descriptor = NULL; 478 descriptor = NULL;
476 bufs = 0; 479 bufs = 0;
477 while (commit_transaction->t_buffers) { 480 while (commit_transaction->t_buffers) {
diff --git a/fs/jbd/transaction.c b/fs/jbd/transaction.c
index 2e9670e2237e..d8dd3d915fd9 100644
--- a/fs/jbd/transaction.c
+++ b/fs/jbd/transaction.c
@@ -1235,6 +1235,7 @@ int journal_forget (handle_t *handle, struct buffer_head *bh)
1235 struct journal_head *jh; 1235 struct journal_head *jh;
1236 int drop_reserve = 0; 1236 int drop_reserve = 0;
1237 int err = 0; 1237 int err = 0;
1238 int was_modified = 0;
1238 1239
1239 BUFFER_TRACE(bh, "entry"); 1240 BUFFER_TRACE(bh, "entry");
1240 1241
@@ -1253,6 +1254,9 @@ int journal_forget (handle_t *handle, struct buffer_head *bh)
1253 goto not_jbd; 1254 goto not_jbd;
1254 } 1255 }
1255 1256
1257 /* keep track of wether or not this transaction modified us */
1258 was_modified = jh->b_modified;
1259
1256 /* 1260 /*
1257 * The buffer's going from the transaction, we must drop 1261 * The buffer's going from the transaction, we must drop
1258 * all references -bzzz 1262 * all references -bzzz
@@ -1270,7 +1274,12 @@ int journal_forget (handle_t *handle, struct buffer_head *bh)
1270 1274
1271 JBUFFER_TRACE(jh, "belongs to current transaction: unfile"); 1275 JBUFFER_TRACE(jh, "belongs to current transaction: unfile");
1272 1276
1273 drop_reserve = 1; 1277 /*
1278 * we only want to drop a reference if this transaction
1279 * modified the buffer
1280 */
1281 if (was_modified)
1282 drop_reserve = 1;
1274 1283
1275 /* 1284 /*
1276 * We are no longer going to journal this buffer. 1285 * We are no longer going to journal this buffer.
@@ -1310,7 +1319,13 @@ int journal_forget (handle_t *handle, struct buffer_head *bh)
1310 if (jh->b_next_transaction) { 1319 if (jh->b_next_transaction) {
1311 J_ASSERT(jh->b_next_transaction == transaction); 1320 J_ASSERT(jh->b_next_transaction == transaction);
1312 jh->b_next_transaction = NULL; 1321 jh->b_next_transaction = NULL;
1313 drop_reserve = 1; 1322
1323 /*
1324 * only drop a reference if this transaction modified
1325 * the buffer
1326 */
1327 if (was_modified)
1328 drop_reserve = 1;
1314 } 1329 }
1315 } 1330 }
1316 1331
@@ -2082,7 +2097,7 @@ void __journal_refile_buffer(struct journal_head *jh)
2082 jh->b_transaction = jh->b_next_transaction; 2097 jh->b_transaction = jh->b_next_transaction;
2083 jh->b_next_transaction = NULL; 2098 jh->b_next_transaction = NULL;
2084 __journal_file_buffer(jh, jh->b_transaction, 2099 __journal_file_buffer(jh, jh->b_transaction,
2085 was_dirty ? BJ_Metadata : BJ_Reserved); 2100 jh->b_modified ? BJ_Metadata : BJ_Reserved);
2086 J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING); 2101 J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
2087 2102
2088 if (was_dirty) 2103 if (was_dirty)