| From d4e1593a4759737394977ee86b260cfdb15392c7 Mon Sep 17 00:00:00 2001 |
| From: Jan Kara <jack@suse.cz> |
| Date: Wed, 11 Jan 2017 10:20:04 -0800 |
| Subject: [PATCH] xfs: Timely free truncated dirty pages |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| commit 0a417b8dc1f10b03e8f558b8a831f07ec4c23795 upstream. |
| |
| Commit 99579ccec4e2 "xfs: skip dirty pages in ->releasepage()" started |
| to skip dirty pages in xfs_vm_releasepage() which also has the effect |
| that if a dirty page is truncated, it does not get freed by |
| block_invalidatepage() and is lingering in LRU list waiting for reclaim. |
| So a simple loop like: |
| |
| while true; do |
| dd if=/dev/zero of=file bs=1M count=100 |
| rm file |
| done |
| |
| will keep using more and more memory until we hit low watermarks and |
| start pagecache reclaim which will eventually reclaim also the truncate |
| pages. Keeping these truncated (and thus never usable) pages in memory |
| is just a waste of memory, is unnecessarily stressing page cache |
| reclaim, and reportedly also leads to anonymous mmap(2) returning ENOMEM |
| prematurely. |
| |
| So instead of just skipping dirty pages in xfs_vm_releasepage(), return |
| to old behavior of skipping them only if they have delalloc or unwritten |
| buffers and fix the spurious warnings by warning only if the page is |
| clean. |
| |
| CC: stable@vger.kernel.org |
| CC: Brian Foster <bfoster@redhat.com> |
| CC: Vlastimil Babka <vbabka@suse.cz> |
| Reported-by: Petr Tůma <petr.tuma@d3s.mff.cuni.cz> |
| Fixes: 99579ccec4e271c3d4d4e7c946058766812afdab |
| Signed-off-by: Jan Kara <jack@suse.cz> |
| Reviewed-by: Brian Foster <bfoster@redhat.com> |
| Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> |
| Signed-off-by: Paul Gortmaker <paul.gortmaker@windriver.com> |
| |
| diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c |
| index c7ebaf172950..fa15390d9692 100644 |
| --- a/fs/xfs/xfs_aops.c |
| +++ b/fs/xfs/xfs_aops.c |
| @@ -1057,19 +1057,22 @@ xfs_vm_releasepage( |
| * block_invalidatepage() can send pages that are still marked dirty |
| * but otherwise have invalidated buffers. |
| * |
| - * We've historically freed buffers on the latter. Instead, quietly |
| - * filter out all dirty pages to avoid spurious buffer state warnings. |
| - * This can likely be removed once shrink_active_list() is fixed. |
| + * We want to release the latter to avoid unnecessary buildup of the |
| + * LRU, skip the former and warn if we've left any lingering |
| + * delalloc/unwritten buffers on clean pages. Skip pages with delalloc |
| + * or unwritten buffers and warn if the page is not dirty. Otherwise |
| + * try to release the buffers. |
| */ |
| - if (PageDirty(page)) |
| - return 0; |
| - |
| xfs_count_page_state(page, &delalloc, &unwritten); |
| |
| - if (WARN_ON_ONCE(delalloc)) |
| + if (delalloc) { |
| + WARN_ON_ONCE(!PageDirty(page)); |
| return 0; |
| - if (WARN_ON_ONCE(unwritten)) |
| + } |
| + if (unwritten) { |
| + WARN_ON_ONCE(!PageDirty(page)); |
| return 0; |
| + } |
| |
| return try_to_free_buffers(page); |
| } |
| -- |
| 2.10.1 |
| |