| From bc23f0c8d7ccd8d924c4e70ce311288cb3e61ea8 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Tue, 24 Nov 2015 15:34:35 -0500 |
| Subject: jbd2: Fix unreclaimed pages after truncate in data=journal mode |
| |
| commit bc23f0c8d7ccd8d924c4e70ce311288cb3e61ea8 upstream. |
| |
| Ted and Namjae have reported that truncated pages don't get timely |
| reclaimed after being truncated in data=journal mode. The following test |
| triggers the issue easily: |
| |
| for (i = 0; i < 1000; i++) { |
| pwrite(fd, buf, 1024*1024, 0); |
| fsync(fd); |
| fsync(fd); |
| ftruncate(fd, 0); |
| } |
| |
| The reason is that journal_unmap_buffer() finds that truncated buffers |
| are not journalled (jh->b_transaction == NULL), they are part of |
| checkpoint list of a transaction (jh->b_cp_transaction != NULL) and have |
| been already written out (!buffer_dirty(bh)). We clean such buffers but |
| we leave them in the checkpoint list. Since checkpoint transaction holds |
| a reference to the journal head, these buffers cannot be released until |
| the checkpoint transaction is cleaned up. And at that point we don't |
| call release_buffer_page() anymore so pages detached from mapping are |
| lingering in the system waiting for reclaim to find them and free them. |
| |
| Fix the problem by removing buffers from transaction checkpoint lists |
| when journal_unmap_buffer() finds out they don't have to be there |
| anymore. |
| |
| Reported-and-tested-by: Namjae Jeon <namjae.jeon@samsung.com> |
| Fixes: de1b794130b130e77ffa975bb58cb843744f9ae5 |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Signed-off-by: Theodore Ts'o <tytso@mit.edu> |
| Signed-off-by: Zefan Li <lizefan@huawei.com> |
| --- |
| fs/jbd2/transaction.c | 2 ++ |
| 1 file changed, 2 insertions(+) |
| |
| --- a/fs/jbd2/transaction.c |
| +++ b/fs/jbd2/transaction.c |
| @@ -1904,6 +1904,7 @@ static int journal_unmap_buffer(journal_ |
| |
| if (!buffer_dirty(bh)) { |
| /* bdflush has written it. We can drop it now */ |
| + __jbd2_journal_remove_checkpoint(jh); |
| goto zap_buffer; |
| } |
| |
| @@ -1941,6 +1942,7 @@ static int journal_unmap_buffer(journal_ |
| /* The orphan record's transaction has |
| * committed. We can cleanse this buffer */ |
| clear_buffer_jbddirty(bh); |
| + __jbd2_journal_remove_checkpoint(jh); |
| goto zap_buffer; |
| } |
| } |