summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCurt Wohlgemuth <curtw@google.com>2009-07-13 09:07:20 -0400
committerTheodore Ts'o <tytso@mit.edu>2009-07-13 09:07:20 -0400
commite6b5d30104db5f34110678ecab14988f1f1eff63 (patch)
tree922408d70d388925f0113998649d56da475deff4
parent62e086be5d2abef8cad854bc5707329ad345f2ec (diff)
downloadlinux-3.10-e6b5d30104db5f34110678ecab14988f1f1eff63.tar.gz
linux-3.10-e6b5d30104db5f34110678ecab14988f1f1eff63.tar.bz2
linux-3.10-e6b5d30104db5f34110678ecab14988f1f1eff63.zip
ext4: Fix buffer head reference leak in no-journal mode
We found a problem with buffer head reference leaks when using an ext4 partition without a journal. In particular, calls to ext4_forget() would not to a brelse() on the input buffer head, which will cause pages they belong to to not be reclaimable. Further investigation showed that all places where ext4_journal_forget() and ext4_journal_revoke() are called are subject to the same problem. The patch below changes __ext4_journal_forget/__ext4_journal_revoke to do an explicit release of the buffer head when the journal handle isn't valid. Signed-off-by: Curt Wohlgemuth <curtw@google.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/ext4_jbd2.c4
-rw-r--r--fs/ext4/ext4_jbd2.h2
-rw-r--r--fs/ext4/inode.c6
3 files changed, 8 insertions, 4 deletions
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c
index ad13a84644e..eb27fd0f2ee 100644
--- a/fs/ext4/ext4_jbd2.c
+++ b/fs/ext4/ext4_jbd2.c
@@ -43,6 +43,8 @@ int __ext4_journal_forget(const char *where, handle_t *handle,
ext4_journal_abort_handle(where, __func__, bh,
handle, err);
}
+ else
+ brelse(bh);
return err;
}
@@ -57,6 +59,8 @@ int __ext4_journal_revoke(const char *where, handle_t *handle,
ext4_journal_abort_handle(where, __func__, bh,
handle, err);
}
+ else
+ brelse(bh);
return err;
}
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h
index d574a85aca5..139fb8cb87e 100644
--- a/fs/ext4/ext4_jbd2.h
+++ b/fs/ext4/ext4_jbd2.h
@@ -131,9 +131,11 @@ int __ext4_journal_get_undo_access(const char *where, handle_t *handle,
int __ext4_journal_get_write_access(const char *where, handle_t *handle,
struct buffer_head *bh);
+/* When called with an invalid handle, this will still do a put on the BH */
int __ext4_journal_forget(const char *where, handle_t *handle,
struct buffer_head *bh);
+/* When called with an invalid handle, this will still do a put on the BH */
int __ext4_journal_revoke(const char *where, handle_t *handle,
ext4_fsblk_t blocknr, struct buffer_head *bh);
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c
index c98e3afea30..f9c642b22ef 100644
--- a/fs/ext4/inode.c
+++ b/fs/ext4/inode.c
@@ -78,16 +78,14 @@ static int ext4_inode_is_fast_symlink(struct inode *inode)
* but there may still be a record of it in the journal, and that record
* still needs to be revoked.
*
- * If the handle isn't valid we're not journaling so there's nothing to do.
+ * If the handle isn't valid we're not journaling, but we still need to
+ * call into ext4_journal_revoke() to put the buffer head.
*/
int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode,
struct buffer_head *bh, ext4_fsblk_t blocknr)
{
int err;
- if (!ext4_handle_valid(handle))
- return 0;
-
might_sleep();
BUFFER_TRACE(bh, "enter");