Commit 5136181d authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

[PATCH] install_page vs. vmtruncate

BK is still missing one piece for Oleg's install_page/vmtruncate races. 
Oleg didn't explicitly ACK this, but I think he did implicitly: Oleg?

The previous patch to install_page, returning an error if !page_mapping
once page_table_lock is held, is not enough to guard against vmtruncate.

When unmap_mapping_range already did this vma, but truncate_inode_pages has
not yet done this page, page->mapping will still be set, but we must now
refrain from inserting the page into the page table.

Could check truncate_count, but that would need caller to read and pass it
down.  Instead, recheck page->index against i_size, which is updated before
unmap_mapping_range.  Better check page->mapping too: not really necessary,
but it's accidental that index is left when mapping is reset.

Also, callers are expecting -EINVAL for beyond end of file, not -EAGAIN.
Signed-off-by: default avatarHugh Dickins <hugh@veritas.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent ddd443e4
...@@ -55,6 +55,8 @@ static inline void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma, ...@@ -55,6 +55,8 @@ static inline void zap_pte(struct mm_struct *mm, struct vm_area_struct *vma,
int install_page(struct mm_struct *mm, struct vm_area_struct *vma, int install_page(struct mm_struct *mm, struct vm_area_struct *vma,
unsigned long addr, struct page *page, pgprot_t prot) unsigned long addr, struct page *page, pgprot_t prot)
{ {
struct inode *inode;
pgoff_t size;
int err = -ENOMEM; int err = -ENOMEM;
pte_t *pte; pte_t *pte;
pgd_t *pgd; pgd_t *pgd;
...@@ -76,8 +78,10 @@ int install_page(struct mm_struct *mm, struct vm_area_struct *vma, ...@@ -76,8 +78,10 @@ int install_page(struct mm_struct *mm, struct vm_area_struct *vma,
* This page may have been truncated. Tell the * This page may have been truncated. Tell the
* caller about it. * caller about it.
*/ */
err = -EAGAIN; err = -EINVAL;
if (!page_mapping(page)) inode = vma->vm_file->f_mapping->host;
size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
if (!page->mapping || page->index >= size)
goto err_unlock; goto err_unlock;
zap_pte(mm, vma, addr, pte); zap_pte(mm, vma, addr, pte);
......
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