Commit a09677ae authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] tmpfs: swapoff-truncate race

From: Hugh Dickins <hugh@veritas.com>

Dissatisfied with earlier fix to race where swapoff sneaks page into tmpfs
page cache after truncate_inode_pages cleaned it: calling it a second time
can be too heavy, instead fix shmem_unuse_inode to check i_size.
(Actually, one part of this fix is in the previous patch: shmem_file_write
now has a hold on the page when it raises i_size.)
parent dca33729
...@@ -488,16 +488,6 @@ static void shmem_truncate(struct inode *inode) ...@@ -488,16 +488,6 @@ static void shmem_truncate(struct inode *inode)
} }
done2: done2:
BUG_ON(info->swapped > info->next_index); BUG_ON(info->swapped > info->next_index);
if (inode->i_mapping->nrpages) {
/*
* Call truncate_inode_pages again: racing shmem_unuse_inode
* may have swizzled a page in from swap since vmtruncate or
* generic_delete_inode did it, before we lowered next_index.
*/
spin_unlock(&info->lock);
truncate_inode_pages(inode->i_mapping, inode->i_size);
spin_lock(&info->lock);
}
shmem_recalc_inode(inode); shmem_recalc_inode(inode);
spin_unlock(&info->lock); spin_unlock(&info->lock);
} }
...@@ -579,6 +569,7 @@ static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_ ...@@ -579,6 +569,7 @@ static inline int shmem_find_swp(swp_entry_t entry, swp_entry_t *dir, swp_entry_
static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page) static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, struct page *page)
{ {
struct inode *inode;
unsigned long idx; unsigned long idx;
unsigned long size; unsigned long size;
unsigned long limit; unsigned long limit;
...@@ -643,8 +634,15 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s ...@@ -643,8 +634,15 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
spin_unlock(&info->lock); spin_unlock(&info->lock);
return 0; return 0;
found: found:
if (move_from_swap_cache(page, idx + offset, idx += offset;
info->vfs_inode.i_mapping) == 0) inode = &info->vfs_inode;
/* Racing against delete or truncate? Must leave out of page cache */
limit = (inode->i_state & I_FREEING)? 0:
(inode->i_size + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
if (idx >= limit ||
move_from_swap_cache(page, idx, inode->i_mapping) == 0)
shmem_swp_set(info, ptr + offset, 0); shmem_swp_set(info, ptr + offset, 0);
shmem_swp_unmap(ptr); shmem_swp_unmap(ptr);
spin_unlock(&info->lock); spin_unlock(&info->lock);
...@@ -653,7 +651,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s ...@@ -653,7 +651,7 @@ static int shmem_unuse_inode(struct shmem_inode_info *info, swp_entry_t entry, s
* try_to_unuse will skip over mms, then reincrement count. * try_to_unuse will skip over mms, then reincrement count.
*/ */
swap_free(entry); swap_free(entry);
return 1; return idx < limit;
} }
/* /*
......
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