diff -urNp --exclude CVS --exclude BitKeeper xx-ref/fs/inode.c xx/fs/inode.c --- xx-ref/fs/inode.c 2003-05-27 04:54:24.000000000 +0200 +++ xx/fs/inode.c 2003-05-27 04:54:30.000000000 +0200 @@ -147,6 +147,7 @@ void inode_init_once(struct inode *inode 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 xx-ref/include/linux/fs.h xx/include/linux/fs.h --- xx-ref/include/linux/fs.h 2003-05-27 04:54:28.000000000 +0200 +++ xx/include/linux/fs.h 2003-05-27 04:54:52.000000000 +0200 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -422,6 +423,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 xx-ref/mm/memory.c xx/mm/memory.c --- xx-ref/mm/memory.c 2003-05-27 04:54:28.000000000 +0200 +++ xx/mm/memory.c 2003-05-27 04:54:30.000000000 +0200 @@ -1127,6 +1127,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; @@ -1140,6 +1141,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: @@ -1335,12 +1337,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 */ @@ -1366,6 +1375,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(&inode->i_sem); + up(&inode->i_sem); + + goto retry; + } /* * This silly early PAGE_DIRTY setting removes a race @@ -1388,6 +1412,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 xx-ref/mm/vmscan.c xx/mm/vmscan.c --- xx-ref/mm/vmscan.c 2003-05-27 04:54:25.000000000 +0200 +++ xx/mm/vmscan.c 2003-05-27 04:54:30.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,