diff -urNp --exclude CVS --exclude BitKeeper x-ref/fs/inode.c x/fs/inode.c --- x-ref/fs/inode.c 2003-07-17 06:04:34.000000000 +0200 +++ x/fs/inode.c 2003-07-17 06:04:37.000000000 +0200 @@ -146,6 +146,7 @@ void _inode_init_once(struct inode *inod INIT_LIST_HEAD(&inode->i_data.clean_pages); INIT_LIST_HEAD(&inode->i_data.dirty_pages); INIT_LIST_HEAD(&inode->i_data.locked_pages); + frlock_init(&inode->i_data.truncate_lock); INIT_LIST_HEAD(&inode->i_dentry); INIT_LIST_HEAD(&inode->i_dirty_buffers); INIT_LIST_HEAD(&inode->i_dirty_data_buffers); diff -urNp --exclude CVS --exclude BitKeeper x-ref/include/linux/fs.h x/include/linux/fs.h --- x-ref/include/linux/fs.h 2003-07-17 06:04:37.000000000 +0200 +++ x/include/linux/fs.h 2003-07-17 06:04:37.000000000 +0200 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -424,6 +425,7 @@ struct address_space { struct vm_area_struct *i_mmap; /* list of private mappings */ struct vm_area_struct *i_mmap_shared; /* list of shared mappings */ spinlock_t i_shared_lock; /* and spinlock protecting it */ + frlock_t truncate_lock; /* serialize ->nopage against truncate */ int gfp_mask; /* how to allocate the pages */ }; diff -urNp --exclude CVS --exclude BitKeeper x-ref/mm/memory.c x/mm/memory.c --- x-ref/mm/memory.c 2003-07-17 06:04:36.000000000 +0200 +++ x/mm/memory.c 2003-07-17 06:05:54.000000000 +0200 @@ -1126,6 +1126,7 @@ int vmtruncate(struct inode * inode, lof if (inode->i_size < offset) goto do_expand; i_size_write(inode, offset); + fr_write_lock(&mapping->truncate_lock); spin_lock(&mapping->i_shared_lock); if (!mapping->i_mmap && !mapping->i_mmap_shared) goto out_unlock; @@ -1139,6 +1140,7 @@ int vmtruncate(struct inode * inode, lof out_unlock: spin_unlock(&mapping->i_shared_lock); truncate_inode_pages(mapping, offset); + fr_write_unlock(&mapping->truncate_lock); goto out_truncate; do_expand: @@ -1334,12 +1336,19 @@ static int do_no_page(struct mm_struct * { struct page * new_page; pte_t entry; + unsigned truncate_sequence; + struct file *file; + struct address_space *mapping; if (!vma->vm_ops || !vma->vm_ops->nopage) return do_anonymous_page(mm, vma, page_table, pmd, write_access, address); spin_unlock(&mm->page_table_lock); pte_kunmap(page_table); + file = vma->vm_file; + mapping = file->f_dentry->d_inode->i_mapping; + truncate_sequence = fr_read_begin(&mapping->truncate_lock); + new_page = vma->vm_ops->nopage(vma, address & PAGE_MASK, 0); if (new_page == NULL) /* no page was available -- SIGBUS */ @@ -1365,6 +1374,21 @@ static int do_no_page(struct mm_struct * page_table = pte_offset_atomic(pmd, address); spin_lock(&mm->page_table_lock); + if (unlikely(truncate_sequence != fr_read_end(&mapping->truncate_lock))) { + struct inode *inode; + + spin_unlock(&mm->page_table_lock); + + /* + * Don't worthless loop here forever overloading the cpu + * until the truncate has completed. + */ + inode = mapping->host; + down_read(&inode->i_alloc_sem); + up_read(&inode->i_alloc_sem); + + goto retry; + } /* * This silly early PAGE_DIRTY setting removes a race @@ -1387,6 +1411,7 @@ static int do_no_page(struct mm_struct * set_pte(page_table, entry); } else { spin_unlock(&mm->page_table_lock); + retry: pte_kunmap(page_table); /* One of our sibling threads was faster, back out. */ page_cache_release(new_page); diff -urNp --exclude CVS --exclude BitKeeper x-ref/mm/vmscan.c x/mm/vmscan.c --- x-ref/mm/vmscan.c 2003-07-17 06:04:35.000000000 +0200 +++ x/mm/vmscan.c 2003-07-17 06:04:37.000000000 +0200 @@ -165,11 +165,10 @@ drop_pte: goto drop_pte; /* - * Anonymous buffercache pages can be left behind by + * Anonymous buffercache pages can't be left behind by * concurrent truncate and pagefault. */ - if (page->buffers) - goto preserve; + BUG_ON(page->buffers); /* * This is a dirty, swappable page. First of all, @@ -195,7 +194,6 @@ drop_pte: } /* No swap space left */ -preserve: set_pte(page_table, pte); UnlockPage(page); return 0;