]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
holepunch: fix disconnected pages after second truncate
authorHugh Dickins <hugh@veritas.com>
Fri, 13 Apr 2007 17:27:10 +0000 (18:27 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 2 May 2007 00:05:55 +0000 (17:05 -0700)
shmem_truncate_range has its own truncate_inode_pages_range, to free any
pages racily instantiated while it was in progress: a SHMEM_PAGEIN flag
is set when this might have happened.  But holepunching gets no chance
to clear that flag at the start of vmtruncate_range, so it's always set
(unless a truncate came just before), so holepunch almost always does
this second truncate_inode_pages_range.

shmem holepunch has unlikely swap<->file races hereabouts whatever we do
(without a fuller rework than is fit for this release): I was going to
skip the second truncate in the punch_hole case, but Miklos points out
that would make holepunch correctness more vulnerable to swapoff.  So
keep the second truncate, but follow it by an unmap_mapping_range to
eliminate the disconnected pages (freed from pagecache while still
mapped in userspace) that it might have left behind.

Signed-off-by: Hugh Dickins <hugh@veritas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
mm/shmem.c

index 265b41617a7db71e376c611c5ee783518b114b5b..fc30c59aa7d05ed65b210e7e74d7c1c5e4712aad 100644 (file)
@@ -674,8 +674,16 @@ done2:
                 * generic_delete_inode did it, before we lowered next_index.
                 * Also, though shmem_getpage checks i_size before adding to
                 * cache, no recheck after: so fix the narrow window there too.
+                *
+                * Recalling truncate_inode_pages_range and unmap_mapping_range
+                * every time for punch_hole (which never got a chance to clear
+                * SHMEM_PAGEIN at the start of vmtruncate_range) is expensive,
+                * yet hardly ever necessary: try to optimize them out later.
                 */
                truncate_inode_pages_range(inode->i_mapping, start, end);
+               if (punch_hole)
+                       unmap_mapping_range(inode->i_mapping, start,
+                                                       end - start, 1);
        }
 
        spin_lock(&info->lock);