aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorJosef Bacik <jbacik@redhat.com>2008-04-17 10:38:59 -0400
committerTheodore Ts'o <tytso@mit.edu>2008-04-17 10:38:59 -0400
commit1dfc3220d963385a317264b11154c462a83596ed (patch)
tree8c35f3233c3d10110b3b48d8f476ddf9404d7192 /fs
parent9fc7c63a1d6e9920038ced782390a54888ed70a6 (diff)
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: <linux-ext4@vger.kernel.org> Cc: Jan Kara <jack@ucw.cz> Signed-off-by: Josef Bacik <jbacik@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs')
-rw-r--r--fs/jbd2/commit.c3
-rw-r--r--fs/jbd2/transaction.c21
2 files changed, 21 insertions, 3 deletions
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)
568 stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits; 568 stats.u.run.rs_blocks = commit_transaction->t_outstanding_credits;
569 stats.u.run.rs_blocks_logged = 0; 569 stats.u.run.rs_blocks_logged = 0;
570 570
571 J_ASSERT(commit_transaction->t_nr_buffers <=
572 commit_transaction->t_outstanding_credits);
573
571 descriptor = NULL; 574 descriptor = NULL;
572 bufs = 0; 575 bufs = 0;
573 while (commit_transaction->t_buffers) { 576 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)
1243 struct journal_head *jh; 1243 struct journal_head *jh;
1244 int drop_reserve = 0; 1244 int drop_reserve = 0;
1245 int err = 0; 1245 int err = 0;
1246 int was_modified = 0;
1246 1247
1247 BUFFER_TRACE(bh, "entry"); 1248 BUFFER_TRACE(bh, "entry");
1248 1249
@@ -1261,6 +1262,9 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
1261 goto not_jbd; 1262 goto not_jbd;
1262 } 1263 }
1263 1264
1265 /* keep track of wether or not this transaction modified us */
1266 was_modified = jh->b_modified;
1267
1264 /* 1268 /*
1265 * The buffer's going from the transaction, we must drop 1269 * The buffer's going from the transaction, we must drop
1266 * all references -bzzz 1270 * all references -bzzz
@@ -1278,7 +1282,12 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
1278 1282
1279 JBUFFER_TRACE(jh, "belongs to current transaction: unfile"); 1283 JBUFFER_TRACE(jh, "belongs to current transaction: unfile");
1280 1284
1281 drop_reserve = 1; 1285 /*
1286 * we only want to drop a reference if this transaction
1287 * modified the buffer
1288 */
1289 if (was_modified)
1290 drop_reserve = 1;
1282 1291
1283 /* 1292 /*
1284 * We are no longer going to journal this buffer. 1293 * We are no longer going to journal this buffer.
@@ -1318,7 +1327,13 @@ int jbd2_journal_forget (handle_t *handle, struct buffer_head *bh)
1318 if (jh->b_next_transaction) { 1327 if (jh->b_next_transaction) {
1319 J_ASSERT(jh->b_next_transaction == transaction); 1328 J_ASSERT(jh->b_next_transaction == transaction);
1320 jh->b_next_transaction = NULL; 1329 jh->b_next_transaction = NULL;
1321 drop_reserve = 1; 1330
1331 /*
1332 * only drop a reference if this transaction modified
1333 * the buffer
1334 */
1335 if (was_modified)
1336 drop_reserve = 1;
1322 } 1337 }
1323 } 1338 }
1324 1339
@@ -2090,7 +2105,7 @@ void __jbd2_journal_refile_buffer(struct journal_head *jh)
2090 jh->b_transaction = jh->b_next_transaction; 2105 jh->b_transaction = jh->b_next_transaction;
2091 jh->b_next_transaction = NULL; 2106 jh->b_next_transaction = NULL;
2092 __jbd2_journal_file_buffer(jh, jh->b_transaction, 2107 __jbd2_journal_file_buffer(jh, jh->b_transaction,
2093 was_dirty ? BJ_Metadata : BJ_Reserved); 2108 jh->b_modified ? BJ_Metadata : BJ_Reserved);
2094 J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING); 2109 J_ASSERT_JH(jh, jh->b_transaction->t_state == T_RUNNING);
2095 2110
2096 if (was_dirty) 2111 if (was_dirty)