diff options
author | Curt Wohlgemuth <curtw@google.com> | 2009-07-13 09:07:20 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2009-07-13 09:07:20 -0400 |
commit | e6b5d30104db5f34110678ecab14988f1f1eff63 (patch) | |
tree | 922408d70d388925f0113998649d56da475deff4 /fs/ext4 | |
parent | 62e086be5d2abef8cad854bc5707329ad345f2ec (diff) |
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>
Diffstat (limited to 'fs/ext4')
-rw-r--r-- | fs/ext4/ext4_jbd2.c | 4 | ||||
-rw-r--r-- | fs/ext4/ext4_jbd2.h | 2 | ||||
-rw-r--r-- | fs/ext4/inode.c | 6 |
3 files changed, 8 insertions, 4 deletions
diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index ad13a84644e1..eb27fd0f2ee8 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, | |||
43 | ext4_journal_abort_handle(where, __func__, bh, | 43 | ext4_journal_abort_handle(where, __func__, bh, |
44 | handle, err); | 44 | handle, err); |
45 | } | 45 | } |
46 | else | ||
47 | brelse(bh); | ||
46 | return err; | 48 | return err; |
47 | } | 49 | } |
48 | 50 | ||
@@ -57,6 +59,8 @@ int __ext4_journal_revoke(const char *where, handle_t *handle, | |||
57 | ext4_journal_abort_handle(where, __func__, bh, | 59 | ext4_journal_abort_handle(where, __func__, bh, |
58 | handle, err); | 60 | handle, err); |
59 | } | 61 | } |
62 | else | ||
63 | brelse(bh); | ||
60 | return err; | 64 | return err; |
61 | } | 65 | } |
62 | 66 | ||
diff --git a/fs/ext4/ext4_jbd2.h b/fs/ext4/ext4_jbd2.h index d574a85aca56..139fb8cb87e4 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, | |||
131 | int __ext4_journal_get_write_access(const char *where, handle_t *handle, | 131 | int __ext4_journal_get_write_access(const char *where, handle_t *handle, |
132 | struct buffer_head *bh); | 132 | struct buffer_head *bh); |
133 | 133 | ||
134 | /* When called with an invalid handle, this will still do a put on the BH */ | ||
134 | int __ext4_journal_forget(const char *where, handle_t *handle, | 135 | int __ext4_journal_forget(const char *where, handle_t *handle, |
135 | struct buffer_head *bh); | 136 | struct buffer_head *bh); |
136 | 137 | ||
138 | /* When called with an invalid handle, this will still do a put on the BH */ | ||
137 | int __ext4_journal_revoke(const char *where, handle_t *handle, | 139 | int __ext4_journal_revoke(const char *where, handle_t *handle, |
138 | ext4_fsblk_t blocknr, struct buffer_head *bh); | 140 | ext4_fsblk_t blocknr, struct buffer_head *bh); |
139 | 141 | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index c98e3afea30a..f9c642b22efa 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) | |||
78 | * but there may still be a record of it in the journal, and that record | 78 | * but there may still be a record of it in the journal, and that record |
79 | * still needs to be revoked. | 79 | * still needs to be revoked. |
80 | * | 80 | * |
81 | * If the handle isn't valid we're not journaling so there's nothing to do. | 81 | * If the handle isn't valid we're not journaling, but we still need to |
82 | * call into ext4_journal_revoke() to put the buffer head. | ||
82 | */ | 83 | */ |
83 | int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode, | 84 | int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode, |
84 | struct buffer_head *bh, ext4_fsblk_t blocknr) | 85 | struct buffer_head *bh, ext4_fsblk_t blocknr) |
85 | { | 86 | { |
86 | int err; | 87 | int err; |
87 | 88 | ||
88 | if (!ext4_handle_valid(handle)) | ||
89 | return 0; | ||
90 | |||
91 | might_sleep(); | 89 | might_sleep(); |
92 | 90 | ||
93 | BUFFER_TRACE(bh, "enter"); | 91 | BUFFER_TRACE(bh, "enter"); |