Commit 686e9e72 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] tmpfs 4/7 getpage/truncate race fix

From: Hugh Dickins <hugh@veritas.com>

Extend use of that SHMEM_PAGEIN flag to where shmem_getpage adds a page
to the cache.  It couldn't have caused a BUG_ON(inode->i_blocks), but if
i_size is reduced (from another cpu) the instant after shmem_swp_alloc
checks it, shmem_getpage could insert a page into the cache just after
truncate_inode_pages has passed through cleaning it, leaving stale data
(which may mysteriously reappear if the file is later extended).

Easily fixed for tmpfs, using the mechanism just added for swapoff; and
probably more important there, since its read from swap can insert non-0
data.  But is there not a similar issue, a tiny window, in filemap.c?
if truncate_inode_pages comes in between checking i_size and adding new
page to cache.  Not worth getting excited, but something to beware of.
parent eaec8c0c
...@@ -52,7 +52,7 @@ ...@@ -52,7 +52,7 @@
#define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT) #define VM_ACCT(size) (PAGE_CACHE_ALIGN(size) >> PAGE_SHIFT)
/* info->flags needs a VM_flag to handle swapoff/truncate races efficiently */ /* info->flags needs a VM_flag to handle pagein/truncate races efficiently */
#define SHMEM_PAGEIN VM_READ #define SHMEM_PAGEIN VM_READ
/* Pretend that each entry is of this size in directory's i_size */ /* Pretend that each entry is of this size in directory's i_size */
...@@ -498,6 +498,8 @@ static void shmem_truncate(struct inode *inode) ...@@ -498,6 +498,8 @@ static void shmem_truncate(struct inode *inode)
* Call truncate_inode_pages again: racing shmem_unuse_inode * Call truncate_inode_pages again: racing shmem_unuse_inode
* may have swizzled a page in from swap since vmtruncate or * may have swizzled a page in from swap since vmtruncate or
* generic_delete_inode did it, before we lowered next_index. * 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.
*/ */
spin_unlock(&info->lock); spin_unlock(&info->lock);
truncate_inode_pages(inode->i_mapping, inode->i_size); truncate_inode_pages(inode->i_mapping, inode->i_size);
...@@ -863,6 +865,7 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, struct page **p ...@@ -863,6 +865,7 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, struct page **p
swap_free(swap); swap_free(swap);
} else if (!(error = move_from_swap_cache( } else if (!(error = move_from_swap_cache(
swappage, idx, mapping))) { swappage, idx, mapping))) {
info->flags |= SHMEM_PAGEIN;
shmem_swp_set(info, entry, 0); shmem_swp_set(info, entry, 0);
shmem_swp_unmap(entry); shmem_swp_unmap(entry);
spin_unlock(&info->lock); spin_unlock(&info->lock);
...@@ -932,6 +935,7 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, struct page **p ...@@ -932,6 +935,7 @@ static int shmem_getpage(struct inode *inode, unsigned long idx, struct page **p
goto failed; goto failed;
goto repeat; goto repeat;
} }
info->flags |= SHMEM_PAGEIN;
} }
info->alloced++; info->alloced++;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment