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>

authored by Curt Wohlgemuth and committed by Theodore Ts'o e6b5d301 62e086be

+8 -4
+4
fs/ext4/ext4_jbd2.c
··· 43 43 ext4_journal_abort_handle(where, __func__, bh, 44 44 handle, err); 45 45 } 46 + else 47 + brelse(bh); 46 48 return err; 47 49 } 48 50 ··· 59 57 ext4_journal_abort_handle(where, __func__, bh, 60 58 handle, err); 61 59 } 60 + else 61 + brelse(bh); 62 62 return err; 63 63 } 64 64
+2
fs/ext4/ext4_jbd2.h
··· 131 131 int __ext4_journal_get_write_access(const char *where, handle_t *handle, 132 132 struct buffer_head *bh); 133 133 134 + /* When called with an invalid handle, this will still do a put on the BH */ 134 135 int __ext4_journal_forget(const char *where, handle_t *handle, 135 136 struct buffer_head *bh); 136 137 138 + /* When called with an invalid handle, this will still do a put on the BH */ 137 139 int __ext4_journal_revoke(const char *where, handle_t *handle, 138 140 ext4_fsblk_t blocknr, struct buffer_head *bh); 139 141
+2 -4
fs/ext4/inode.c
··· 78 78 * but there may still be a record of it in the journal, and that record 79 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 84 int ext4_forget(handle_t *handle, int is_metadata, struct inode *inode, 84 85 struct buffer_head *bh, ext4_fsblk_t blocknr) 85 86 { 86 87 int err; 87 - 88 - if (!ext4_handle_valid(handle)) 89 - return 0; 90 88 91 89 might_sleep(); 92 90