aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2017-10-13 12:47:45 -0400
committerDarrick J. Wong <darrick.wong@oracle.com>2017-10-16 15:11:56 -0400
commit793d7dbe6d82a50b9d14bf992b9eaacb70a11ce6 (patch)
treeb8aa09684dd49335da3ac453c6a7fa4fc6fcd4b9
parent33d930e59a98fa10a0db9f56c7fa2f21a4aef9b9 (diff)
xfs: cancel dirty pages on invalidation
Recently we've had warnings arise from the vm handing us pages without bufferheads attached to them. This should not ever occur in XFS, but we don't defend against it properly if it does. The only place where we remove bufferheads from a page is in xfs_vm_releasepage(), but we can't tell the difference here between "page is dirty so don't release" and "page is dirty but is being invalidated so release it". In some places that are invalidating pages ask for pages to be released and follow up afterward calling ->releasepage by checking whether the page was dirty and then aborting the invalidation. This is a possible vector for releasing buffers from a page but then leaving it in the mapping, so we really do need to avoid dirty pages in xfs_vm_releasepage(). To differentiate between invalidated pages and normal pages, we need to clear the page dirty flag when invalidating the pages. This can be done through xfs_vm_invalidatepage(), and will result xfs_vm_releasepage() seeing the page as clean which matches the bufferhead state on the page after calling block_invalidatepage(). Hence we can re-add the page dirty check in xfs_vm_releasepage to catch the case where we might be releasing a page that is actually dirty and so should not have the bufferheads on it removed. This will remove one possible vector of "dirty page with no bufferheads" and so help narrow down the search for the root cause of that problem. Signed-Off-By: Dave Chinner <dchinner@redhat.com> Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com> Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
-rw-r--r--fs/xfs/xfs_aops.c34
1 files changed, 22 insertions, 12 deletions
diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
index f18e5932aec4..067284d84d9e 100644
--- a/fs/xfs/xfs_aops.c
+++ b/fs/xfs/xfs_aops.c
@@ -735,6 +735,14 @@ xfs_vm_invalidatepage(
735{ 735{
736 trace_xfs_invalidatepage(page->mapping->host, page, offset, 736 trace_xfs_invalidatepage(page->mapping->host, page, offset,
737 length); 737 length);
738
739 /*
740 * If we are invalidating the entire page, clear the dirty state from it
741 * so that we can check for attempts to release dirty cached pages in
742 * xfs_vm_releasepage().
743 */
744 if (offset == 0 && length >= PAGE_SIZE)
745 cancel_dirty_page(page);
738 block_invalidatepage(page, offset, length); 746 block_invalidatepage(page, offset, length);
739} 747}
740 748
@@ -1190,25 +1198,27 @@ xfs_vm_releasepage(
1190 * mm accommodates an old ext3 case where clean pages might not have had 1198 * mm accommodates an old ext3 case where clean pages might not have had
1191 * the dirty bit cleared. Thus, it can send actual dirty pages to 1199 * the dirty bit cleared. Thus, it can send actual dirty pages to
1192 * ->releasepage() via shrink_active_list(). Conversely, 1200 * ->releasepage() via shrink_active_list(). Conversely,
1193 * block_invalidatepage() can send pages that are still marked dirty 1201 * block_invalidatepage() can send pages that are still marked dirty but
1194 * but otherwise have invalidated buffers. 1202 * otherwise have invalidated buffers.
1195 * 1203 *
1196 * We want to release the latter to avoid unnecessary buildup of the 1204 * We want to release the latter to avoid unnecessary buildup of the
1197 * LRU, skip the former and warn if we've left any lingering 1205 * LRU, so xfs_vm_invalidatepage() clears the page dirty flag on pages
1198 * delalloc/unwritten buffers on clean pages. Skip pages with delalloc 1206 * that are entirely invalidated and need to be released. Hence the
1199 * or unwritten buffers and warn if the page is not dirty. Otherwise 1207 * only time we should get dirty pages here is through
1200 * try to release the buffers. 1208 * shrink_active_list() and so we can simply skip those now.
1209 *
1210 * warn if we've left any lingering delalloc/unwritten buffers on clean
1211 * or invalidated pages we are about to release.
1201 */ 1212 */
1213 if (PageDirty(page))
1214 return 0;
1215
1202 xfs_count_page_state(page, &delalloc, &unwritten); 1216 xfs_count_page_state(page, &delalloc, &unwritten);
1203 1217
1204 if (delalloc) { 1218 if (WARN_ON_ONCE(delalloc))
1205 WARN_ON_ONCE(!PageDirty(page));
1206 return 0; 1219 return 0;
1207 } 1220 if (WARN_ON_ONCE(unwritten))
1208 if (unwritten) {
1209 WARN_ON_ONCE(!PageDirty(page));
1210 return 0; 1221 return 0;
1211 }
1212 1222
1213 return try_to_free_buffers(page); 1223 return try_to_free_buffers(page);
1214} 1224}