fault vs truncate/invalidate race fix
Subsystems (10)
cover fault vs truncate/invalidate race fix
The following set of patches fix the fault vs invalidate and fault vs truncate_range race for filemap_nopage mappings, plus those and fault vs truncate race for nonlinear mappings.
Hasn't changed since I last submitted it, when it was rejected because it made one of the buffered write deadlocks easier to hit. I'll try again.
Patches based on 2.6.20-rc4. Comments?
Thanks,
Nick
--
SuSE Labs
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 1/7 mm: debug check for the fault vs invalidate race
Add a bugcheck for Andrea's pagefault vs invalidate race. This is triggerable for both linear and nonlinear pages with a userspace test harness (using direct IO and truncate, respectively).
mm/filemap.c +2 -0
@@ -120,6 +120,8 @@ void __remove_from_page_cache(struct pag page->mapping =NULL ; mapping->nrpages--;__dec_zone_page_state (page,NR_FILE_PAGES ); + +BUG_ON (page_mapped (page)); }void remove_from_page_cache (struct page *page)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 2/7 mm: simplify filemap_nopage
In reply to Nick PigginIdentical block is duplicated twice: contrary to the comment, we have been re-reading the page *twice* in filemap_nopage rather than once.
If any retry logic or anything is needed, it belongs in lower levels anyway. Only retry once. Linus agrees.
mm/filemap.c +0 -24
@@ -1468,30 +1468,6 @@ page_not_uptodate: majmin =VM_FAULT_MAJOR ;count_vm_event (PGMAJFAULT ); } -lock_page (page); - -/* Did it get unhashed while we waited for it? */ -if (!page->mapping) { -unlock_page (page); -page_cache_release (page); - goto retry_all; - } - -/* Did somebody else get it up-to-date? */ -if (PageUptodate (page)) { -unlock_page (page); - goto success; - } - - error = mapping->a_ops->readpage (file, page); -if (!error) { -wait_on_page_locked (page); -if (PageUptodate (page)) - goto success; - }else if (error ==AOP_TRUNCATED_PAGE ) { -page_cache_release (page); - goto retry_find; - }/* * Umm, take care of errors if the page isn't up-to-date.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 3/7 mm: fix fault vs invalidate race for linear mappings
In reply to Nick PigginFix the race between invalidate_inode_pages and do_no_page.
Andrea Arcangeli identified a subtle race between invalidation of pages from pagecache with userspace mappings, and do_no_page.
The issue is that invalidation has to shoot down all mappings to the page, before it can be discarded from the pagecache. Between shooting down ptes to a particular page, and actually dropping the struct page from the pagecache, do_no_page from any process might fault on that page and establish a new mapping to the page just before it gets discarded from the pagecache.
The most common case where such invalidation is used is in file truncation. This case was catered for by doing a sort of open-coded seqlock between the file's i_size, and its truncate_count.
Truncation will decrease i_size, then increment truncate_count before unmapping userspace pages; do_no_page will read truncate_count, then find the page if it is within i_size, and then check truncate_count under the page table lock and back out and retry if it had subsequently been changed (ptl will serialise against unmapping, and ensure a potentially updated truncate_count is actually visible).
Complexity and documentation issues aside, the locking protocol fails in the case where we would like to invalidate pagecache inside i_size. do_no_page can come in anytime and filemap_nopage is not aware of the invalidation in progress (as it is when it is outside i_size). The end result is that dangling (->mapping == NULL) pages that appear to be from a particular file may be mapped into userspace with nonsense data. Valid mappings to the same place will see a different page.
Andrea implemented two working fixes, one using a real seqlock, another using a page->flags bit. He also proposed using the page lock in do_no_page, but that was initially considered too heavyweight. However, it is not a global or per-file lock, and the page cacheline is modified in do_no_page to increment _count and _mapcount anyway, so a further modification should not be a large performance hit. Scalability is not an issue.
This patch implements this latter approach. ->nopage implementations return with the page locked if it is possible for their underlying file to be invalidated (in that case, they must set a special vm_flags bit to indicate so). do_no_page only unlocks the page after setting up the mapping completely. invalidation is excluded because it holds the page lock during invalidation of each page (and ensures that the page is not mapped while holding the lock).
This also allows significant simplifications in do_no_page, because we have the page locked in the right place in the pagecache from the start.
include/linux/mm.h +5 -0
@@ -168,6 +168,11 @@ extern unsigned int kobjsize(const void#define VM_NONLINEAR 0x00800000/* Is non-linear (remap_file_pages) */ #define VM_MAPPED_COPY 0x01000000/* T if mapped copy of data (nommu mmap) */ #define VM_INSERTPAGE 0x02000000/* The vma has had "vm_insert_page()" done on it */ +#define VM_CAN_INVALIDATE 0x04000000/* The mapping may be invalidated, +* eg. truncate or invalidate_inode_*. +* In this case, do_no_page must +* return with the page locked. +*/ #ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */ #define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
mm/filemap.c +20 -33
105 lines not on this page · raw message
mm/memory.c +61 -78
224 lines not on this page · raw message
mm/shmem.c +9 -2
@@ -82,6 +82,7 @@ enum sgp_type {SGP_READ ,/* don't exceed i_size, don't allocate page */ SGP_CACHE ,/* don't exceed i_size, may allocate page */ SGP_WRITE ,/* may exceed i_size, may allocate page */ + SGP_NOPAGE, /* same as SGP_CACHE, return with page locked */ }; static int shmem_getpage(struct inode *inode, unsigned long idx,
@@ -1215,8 +1216,10 @@ repeat: } done:if (*pagep != filepage) { -unlock_page (filepage); *pagep = filepage; +if (sgp !=SGP_NOPAGE ) +unlock_page (filepage); + }return 0 ;
@@ -1235,13 +1238,15 @@ struct page *shmem_nopage(struct vm_areaunsigned long idx;int error; +BUG_ON (!(vma->vm_flags &VM_CAN_INVALIDATE )); + idx = (address - vma->vm_start) >>PAGE_SHIFT ; idx += vma->vm_pgoff; idx >>=PAGE_CACHE_SHIFT -PAGE_SHIFT ;if (((loff_t ) idx <<PAGE_CACHE_SHIFT ) >=i_size_read (inode))return NOPAGE_SIGBUS ; - error =shmem_getpage (inode, idx, &page,SGP_CACHE , type); + error =shmem_getpage (inode, idx, &page,SGP_NOPAGE , type);if (error)return (error == -ENOMEM )?NOPAGE_OOM :NOPAGE_SIGBUS ;
@@ -1339,6 +1344,7 @@ int shmem_mmap(struct file *file, struct {file_accessed (file); vma->vm_ops = &shmem_vm_ops; + vma->vm_flags |=VM_CAN_INVALIDATE ;return 0 ; }
@@ -2532,5 +2538,6 @@ int shmem_zero_setup(struct vm_area_strufput (vma->vm_file); vma->vm_file = file; vma->vm_ops = &shmem_vm_ops; + vma->vm_flags |=VM_CAN_INVALIDATE ;return 0 ; }
fs/ncpfs/mmap.c +1 -0
@@ -123,6 +123,7 @@ int ncp_mmap(struct file *file, struct vreturn -EFBIG ; vma->vm_ops = &ncp_file_mmap; + vma->vm_flags |=VM_CAN_INVALIDATE ;file_accessed (file);return 0 ; }
fs/ocfs2/mmap.c +1 -0
@@ -104,6 +104,7 @@ int ocfs2_mmap(struct file *file, structocfs2_meta_unlock (file->f_dentry->d_inode, lock_level); out: vma->vm_ops = &ocfs2_file_vm_ops; + vma->vm_flags |=VM_CAN_INVALIDATE ;return 0 ; }
fs/xfs/linux-2.6/xfs_file.c +1 -0
@@ -343,6 +343,7 @@ xfs_file_mmap( struct vm_area_struct *vma) { vma->vm_ops = &xfs_file_vm_ops; + vma->vm_flags |=VM_CAN_INVALIDATE ;#ifdef CONFIG_XFS_DMAPI if (vn_from_inode (filp->f_path.dentry->d_inode)->v_vfsp->vfs_flag &VFS_DMI )
ipc/shm.c +1 -0
@@ -231,6 +231,7 @@ static int shm_mmap(struct file * file, ret =shmem_mmap (file, vma);if (ret ==0 ) { vma->vm_ops = &shm_vm_ops; + vma->vm_flags |=VM_CAN_INVALIDATE ;if (!(vma->vm_flags &VM_WRITE )) vma->vm_flags &= ~VM_MAYWRITE ;shm_inc (shm_file_ns (file), file->f_path.dentry->d_inode->i_ino);
mm/truncate.c +10 -0
@@ -184,6 +184,11 @@ void truncate_inode_pages_range(struct aunlock_page (page);continue ; } +while (page_mapped (page)) { +unmap_mapping_range (mapping, + (loff_t )page_index<<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); + }truncate_complete_page (mapping, page);unlock_page (page); }
@@ -221,6 +226,11 @@ void truncate_inode_pages_range(struct abreak ;lock_page (page);wait_on_page_writeback (page); +while (page_mapped (page)) { +unmap_mapping_range (mapping, + (loff_t )page_index<<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE ,0 ); + }if (page->index > next) next = page->index; next++;
fs/gfs2/ops_file.c +2 -0
@@ -416,6 +416,8 @@ static int gfs2_mmap(struct file *file,else vma->vm_ops = &gfs2_vm_ops_private; + vma->vm_flags |=VM_CAN_INVALIDATE ; +gfs2_glock_dq_uninit (&i_gh);return error;
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 4/7 mm: merge populate and nopage into fault (fixes nonlinear)
In reply to Nick PigginNonlinear mappings are (AFAIKS) simply a virtual memory concept that encodes the virtual address -> file offset differently from linear mappings.
I can't see why the filesystem/pagecache code should need to know anything about it, except for the fact that the ->nopage handler didn't quite pass down enough information (ie. pgoff). But it is more logical to pass pgoff rather than have the ->nopage function calculate it itself anyway. And having the nopage handler install the pte itself is sort of nasty.
This patch introduces a new fault handler that replaces ->nopage and ->populate and (later) ->nopfn. Most of the old mechanism is still in place so there is a lot of duplication and nice cleanups that can be removed if everyone switches over.
The rationale for doing this in the first place is that nonlinear mappings are subject to the pagefault vs invalidate/truncate race too, and it seemed stupid to duplicate the synchronisation logic rather than just consolidate the two.
After this patch, MAP_NONBLOCK no longer sets up ptes for pages present in pagecache. Seems like a fringe functionality anyway.
NOPAGE_REFAULT is removed. This should be implemented with ->fault, and no users have hit mainline yet.
include/linux/mm.h +30 -8
@@ -168,11 +168,12 @@ extern unsigned int kobjsize(const void#define VM_NONLINEAR 0x00800000/* Is non-linear (remap_file_pages) */ #define VM_MAPPED_COPY 0x01000000/* T if mapped copy of data (nommu mmap) */ #define VM_INSERTPAGE 0x02000000/* The vma has had "vm_insert_page()" done on it */ -#define VM_CAN_INVALIDATE 0x04000000/* The mapping may be invalidated, +#define VM_CAN_INVALIDATE 0x04000000/* The mapping may be invalidated, * eg. truncate or invalidate_inode_*. * In this case, do_no_page must * return with the page locked. */ +#define VM_CAN_NONLINEAR 0x08000000/* Has ->fault & does nonlinear pages */ #ifndef VM_STACK_DEFAULT_FLAGS /* arch can override this */ #define VM_STACK_DEFAULT_FLAGS VM_DATA_DEFAULT_FLAGS
@@ -196,6 +197,26 @@ extern unsigned int kobjsize(const void*/ extern pgprot_t protection_map[16 ]; +#define FAULT_FLAG_WRITE 0x01 +#define FAULT_FLAG_NONLINEAR 0x02 + +/* +* fault_data is filled in the the pagefault handler and passed to the +* vma's ->fault function. That function is responsible for filling in +* 'type', which is the type of fault if a page is returned, or the type +* of error if NULL is returned. +* +* pgoff should be used in favour of address, if possible. If pgoff is +* used, one may set VM_CAN_NONLINEAR in the vma->vm_flags to get +* nonlinear mapping support. +*/ +struct fault_data { +unsigned long address; +pgoff_t pgoff; +unsigned int flags; + +int type; +};/* * These are the virtual MM functions - opening of an area, closing and
@@ -205,6 +226,7 @@ extern pgprot_t protection_map[16];struct vm_operations_struct {void (*open)(struct vm_area_struct * area);void (*close)(struct vm_area_struct * area); +struct page * (*fault)(struct vm_area_struct *vma,struct fault_data * fdata);struct page * (*nopage)(struct vm_area_struct * area,unsigned long address,int *type);unsigned long (*nopfn)(struct vm_area_struct * area,unsigned long address);int (*populate)(struct vm_area_struct * area,unsigned long address,unsigned long len,pgprot_t prot,unsigned long pgoff,int nonblock);
@@ -635,7 +657,6 @@ static inline int page_mapped(struct pag*/ #define NOPAGE_SIGBUS (NULL)#define NOPAGE_OOM ((struct page *) (-1)) -#define NOPAGE_REFAULT ((struct page *) (-2))/* Return to userspace, rerun */ /* * Error return values for the *_nopfn functions
@@ -669,14 +690,13 @@ extern void pagefault_out_of_memory(voidextern void show_free_areas (void );#ifdef CONFIG_SHMEM -struct page *shmem_nopage (struct vm_area_struct *vma, -unsigned long address,int *type); +struct page *shmem_fault (struct vm_area_struct *vma,struct fault_data *fdata);int shmem_set_policy (struct vm_area_struct *vma,struct mempolicy *new);struct mempolicy *shmem_get_policy (struct vm_area_struct *vma,unsigned long addr);int shmem_lock (struct file *file,int lock,struct user_struct *user);#else -#define shmem_nopage filemap_nopage +#define shmem_fault filemap_faultstatic inline int shmem_lock (struct file *file,int lock,struct user_struct *user)
@@ -1069,9 +1089,11 @@ extern void truncate_inode_pages_range(s loff_t lstart, loff_t lend);/* generic vm_area_ops exported for stackable file systems */ -extern struct page *filemap_nopage (struct vm_area_struct *,unsigned long ,int *); -extern int filemap_populate (struct vm_area_struct *,unsigned long , -unsigned long ,pgprot_t ,unsigned long ,int ); +extern struct page *filemap_fault (struct vm_area_struct *,struct fault_data *); +extern struct page * __deprecated_for_modules filemap_nopage( +struct vm_area_struct *,unsigned long ,int *); +extern int __deprecated_for_modules filemap_populate(struct vm_area_struct *, +unsigned long ,unsigned long ,pgprot_t ,unsigned long ,int );/* mm/page-writeback.c */ int write_one_page (struct page *page,int wait);
mm/memory.c +84 -45
220 lines not on this page · raw message
mm/filemap.c +57 -36
199 lines not on this page · raw message
mm/fremap.c +73 -32
120 lines not on this page · raw message
fs/gfs2/ops_file.c +1 -1
@@ -416,7 +416,7 @@ static int gfs2_mmap(struct file *file,else vma->vm_ops = &gfs2_vm_ops_private; - vma->vm_flags |=VM_CAN_INVALIDATE ; + vma->vm_flags |=VM_CAN_INVALIDATE |VM_CAN_NONLINEAR ;gfs2_glock_dq_uninit (&i_gh);
fs/gfs2/ops_vm.c +19 -17
@@ -42,17 +42,17 @@ static void pfault_be_greedy(struct gfs2iput (&ip->i_inode); } -static struct page *gfs2_private_nopage (struct vm_area_struct *area, -unsigned long address,int *type) +static struct page *gfs2_private_fault (struct vm_area_struct *vma, +struct fault_data *fdata) { -struct gfs2_inode *ip =GFS2_I (area->vm_file->f_mapping->host); +struct gfs2_inode *ip =GFS2_I (vma->vm_file->f_mapping->host);struct page *result;set_bit (GIF_PAGED , &ip->i_flags); - result =filemap_nopage (area, address, type); + result =filemap_fault (vma, fdata); -if (result && result !=NOPAGE_OOM ) +if (result)pfault_be_greedy (ip);return result;
@@ -126,16 +126,14 @@ out:return error; } -static struct page *gfs2_sharewrite_nopage (struct vm_area_struct *area, -unsigned long address,int *type) +static struct page *gfs2_sharewrite_fault (struct vm_area_struct *vma, +struct fault_data *fdata) { -struct file *file = area->vm_file; +struct file *file = vma->vm_file;struct gfs2_file *gf = file->private_data;struct gfs2_inode *ip =GFS2_I (file->f_mapping->host);struct gfs2_holder i_gh;struct page *result =NULL ; -unsigned long index = ((address - area->vm_start) >>PAGE_CACHE_SHIFT ) + - area->vm_pgoff;int alloc_required;int error;
@@ -146,21 +144,25 @@ static struct page *gfs2_sharewrite_nopaset_bit (GIF_PAGED , &ip->i_flags);set_bit (GIF_SW_PAGED , &ip->i_flags); - error =gfs2_write_alloc_required (ip, (u64 )index <<PAGE_CACHE_SHIFT , -PAGE_CACHE_SIZE , &alloc_required); -if (error) + error =gfs2_write_alloc_required (ip, + (u64 )fdata->pgoff <<PAGE_CACHE_SHIFT , +PAGE_CACHE_SIZE , &alloc_required); +if (error) { + fdata->type =VM_FAULT_OOM ;/* XXX: are these right? */ goto out; + }set_bit (GFF_EXLOCK , &gf->f_flags); - result =filemap_nopage (area, address, type); + result =filemap_fault (vma, fdata);clear_bit (GFF_EXLOCK , &gf->f_flags); -if (!result || result ==NOPAGE_OOM ) +if (!result) goto out;if (alloc_required) { error =alloc_page_backing (ip, result);if (error) {page_cache_release (result); + fdata->type =VM_FAULT_OOM ; result =NULL ; goto out; }
@@ -175,10 +177,10 @@ out: }struct vm_operations_struct gfs2_vm_ops_private = { - .nopage = gfs2_private_nopage, + .fault = gfs2_private_fault, };struct vm_operations_struct gfs2_vm_ops_sharewrite = { - .nopage = gfs2_sharewrite_nopage, + .fault = gfs2_sharewrite_fault, };
fs/ocfs2/mmap.c +8 -9
@@ -42,16 +42,14 @@#include "inode.h" #include "mmap.h" -static struct page *ocfs2_nopage (struct vm_area_struct * area, -unsigned long address, -int *type) +static struct page *ocfs2_fault (struct vm_area_struct *area, +struct fault_data *fdata) { -struct page *page =NOPAGE_SIGBUS ; +struct page *page =NULL ;sigset_t blocked, oldset;int ret; -mlog_entry ("(area=%p, address=%lu, type=%p)\n" , area, address, - type); +mlog_entry ("(area=%p, page offset=%lu)\n" , area, fdata->pgoff);/* The best way to deal with signals in this path is * to block them upfront, rather than allowing the
@@ -62,11 +60,12 @@ static struct page *ocfs2_nopage(struct* from sigprocmask */ ret =sigprocmask (SIG_BLOCK , &blocked, &oldset);if (ret <0 ) { + fdata->type =VM_FAULT_SIGBUS ;mlog_errno (ret); goto out; } - page =filemap_nopage (area, address, type); + page =filemap_fault (area, fdata); ret =sigprocmask (SIG_SETMASK , &oldset,NULL );if (ret <0 )
@@ -77,7 +76,7 @@ out: }static struct vm_operations_struct ocfs2_file_vm_ops = { - .nopage = ocfs2_nopage, + .fault = ocfs2_fault, };int ocfs2_mmap (struct file *file,struct vm_area_struct *vma)
@@ -104,7 +103,7 @@ int ocfs2_mmap(struct file *file, structocfs2_meta_unlock (file->f_dentry->d_inode, lock_level); out: vma->vm_ops = &ocfs2_file_vm_ops; - vma->vm_flags |=VM_CAN_INVALIDATE ; + vma->vm_flags |=VM_CAN_INVALIDATE |VM_CAN_NONLINEAR ;return 0 ; }
fs/xfs/linux-2.6/xfs_file.c +11 -12
@@ -246,18 +246,19 @@ xfs_file_fsync(#ifdef CONFIG_XFS_DMAPI STATIC struct page * -xfs_vm_nopage ( -struct vm_area_struct *area, -unsigned long address, -int *type) +xfs_vm_fault ( +struct vm_area_struct *vma, +struct fault_data *fdata) { -struct inode *inode = area->vm_file->f_path.dentry->d_inode; +struct inode *inode = vma->vm_file->f_path.dentry->d_inode;bhv_vnode_t *vp =vn_from_inode (inode);ASSERT_ALWAYS (vp->v_vfsp->vfs_flag &VFS_DMI ); -if (XFS_SEND_MMAP (XFS_VFSTOM (vp->v_vfsp), area,0 )) +if (XFS_SEND_MMAP (XFS_VFSTOM (vp->v_vfsp), vma,0 )) { + fdata->type =VM_FAULT_SIGBUS ;return NULL ; -return filemap_nopage (area, address, type); + } +return filemap_fault (vma, fdata); }#endif /* CONFIG_XFS_DMAPI */
@@ -343,7 +344,7 @@ xfs_file_mmap( struct vm_area_struct *vma) { vma->vm_ops = &xfs_file_vm_ops; - vma->vm_flags |=VM_CAN_INVALIDATE ; + vma->vm_flags |=VM_CAN_INVALIDATE |VM_CAN_NONLINEAR ;#ifdef CONFIG_XFS_DMAPI if (vn_from_inode (filp->f_path.dentry->d_inode)->v_vfsp->vfs_flag &VFS_DMI )
@@ -502,14 +503,12 @@ const struct file_operations xfs_dir_fil };static struct vm_operations_struct xfs_file_vm_ops = { - .nopage = filemap_nopage, - .populate = filemap_populate, + .fault = filemap_fault, }; #ifdef CONFIG_XFS_DMAPI static struct vm_operations_struct xfs_dmapi_file_vm_ops = { - .nopage = xfs_vm_nopage, - .populate = filemap_populate, + .fault = xfs_vm_fault, #ifdef HAVE_VMOP_MPROTECT .mprotect = xfs_vm_mprotect, #endif
mm/mmap.c +2 -6
@@ -1147,12 +1147,8 @@ out: mm->locked_vm += len >>PAGE_SHIFT ;make_pages_present (addr, addr + len); } -if (flags &MAP_POPULATE ) { -up_write (&mm->mmap_sem); -sys_remap_file_pages (addr, len,0 , - pgoff, flags &MAP_NONBLOCK ); -down_write (&mm->mmap_sem); - } +if ((flags &MAP_POPULATE ) && !(flags &MAP_NONBLOCK )) +make_pages_present (addr, addr + len);return addr; unmap_and_free_vma:
ipc/shm.c +1 -1
@@ -261,7 +261,7 @@ static struct file_operations shm_file_ostatic struct vm_operations_struct shm_vm_ops = { .open = shm_open,/* callback for a new vm-area open */ .close = shm_close,/* callback for when the vm-area is released */ - .nopage = shmem_nopage, + .fault = shmem_fault, #if defined(CONFIG_NUMA) && defined(CONFIG_SHMEM) .set_policy = shmem_set_policy, .get_policy = shmem_get_policy,
mm/filemap_xip.c +24 -22
92 lines not on this page · raw message
mm/nommu.c +1 -2
@@ -1299,8 +1299,7 @@ int in_gate_area_no_task(unsigned long areturn 0 ; } -struct page *filemap_nopage (struct vm_area_struct *area, -unsigned long address,int *type) +struct page *filemap_fault (struct vm_area_struct *vma,struct fault_data *fdata) {BUG ();return NULL ;
mm/shmem.c +19 -61
129 lines not on this page · raw message
mm/truncate.c +1 -1
@@ -74,7 +74,7 @@ EXPORT_SYMBOL(cancel_dirty_page);/* * If truncate cannot remove the fs-private metadata from the page, the page * becomes anonymous. It will be left on the LRU and may even be mapped into -* user pagetables if we're racing with filemap_nopage(). +* user pagetables if we're racing with filemap_fault(). * * We need to bale out if page->mapping is no longer equal to the original * mapping. This happens a) when the VM reclaimed the page while we waited on
fs/gfs2/ops_address.c +1 -1
@@ -212,7 +212,7 @@ static int gfs2_readpage(struct file *fiif (file) { gf = file->private_data;if (test_bit (GFF_EXLOCK , &gf->f_flags)) -/* gfs2_sharewrite_nopage has grabbed the ip->i_gl already */ +/* gfs2_sharewrite_fault has grabbed the ip->i_gl already */ goto skip_lock; }gfs2_holder_init (ip->i_gl,LM_ST_SHARED ,GL_ATIME |LM_FLAG_TRY_1CB , &gh);
fs/ncpfs/mmap.c +12 -11
@@ -25,8 +25,8 @@/* * Fill in the supplied page for mmap */ -static struct page *ncp_file_mmap_nopage (struct vm_area_struct *area, -unsigned long address,int *type) +static struct page *ncp_file_mmap_fault (struct vm_area_struct *area, +struct fault_data *fdata) {struct file *file = area->vm_file;struct dentry *dentry = file->f_path.dentry;
@@ -40,15 +40,17 @@ static struct page* ncp_file_mmap_nopage page =alloc_page (GFP_HIGHUSER );/* ncpfs has nothing against high pages as long as recvmsg and memset works on it */ -if (!page) -return page; +if (!page) { + fdata->type =VM_FAULT_OOM ; +return NULL ; + } pg_addr =kmap (page); - address &=PAGE_MASK ; - pos = address - area->vm_start + (area->vm_pgoff <<PAGE_SHIFT ); + pos = fdata->pgoff <<PAGE_SHIFT ; count =PAGE_SIZE ; -if (address +PAGE_SIZE > area->vm_end) { - count = area->vm_end - address; +if (fdata->address +PAGE_SIZE > area->vm_end) { +WARN_ON (1 );/* shouldn't happen? */ + count = area->vm_end - fdata->address; }/* what we can read in one go */ bufsize =NCP_SERVER (inode)->buffer_size;
@@ -91,15 +93,14 @@ static struct page* ncp_file_mmap_nopage* fetches from the network, here the analogue of disk. * -- wli */ -if (type) - *type =VM_FAULT_MAJOR ; + fdata->type =VM_FAULT_MAJOR ;count_vm_event (PGMAJFAULT );return page; }static struct vm_operations_struct ncp_file_mmap = { - .nopage = ncp_file_mmap_nopage, + .fault = ncp_file_mmap_fault, };
fs/ocfs2/aops.c +1 -1
@@ -215,7 +215,7 @@ static int ocfs2_readpage(struct file *f* might now be discovering a truncate that hit on another node. * block_read_full_page->get_block freaks out if it is asked to read * beyond the end of a file, so we check here. Callers -* (generic_file_read, fault->nopage) are clever enough to check i_size +* (generic_file_read, vm_ops->fault) are clever enough to check i_size * and notice that the page they just read isn't needed. * * XXX sys_readahead() seems to get that wrong?
Documentation/feature-removal-schedule.txt +27 -0
@@ -198,6 +198,33 @@ Who: Nick Piggin <[email protected]> --------------------------- +What: filemap_nopage, filemap_populate +When: February 2007 +Why: These legacy interfaces no longer have any callers in the kernel and + any functionality provided can be provided with filemap_fault. The + removal schedule is short because they are a big maintainence burden + and have some bugs. +Who: Nick Piggin <[email protected]> + +--------------------------- + +What: vm_ops.populate, install_page +When: February 2007 +Why: These legacy interfaces no longer have any callers in the kernel and + any functionality provided can be provided with vm_ops.fault. +Who: Nick Piggin <[email protected]> + +--------------------------- + +What: vm_ops.nopage +When: October 2007, provided in-kernel callers have been converted +Why: This interface is replaced by vm_ops.fault, but it has been around + forever, is used by a lot of drivers, and doesn't cost much to + maintain. +Who: Nick Piggin <[email protected]> + +--------------------------- + What: Interrupt only SA_* flags When: Januar 2007 Why: The interrupt related SA_* flags are replaced by IRQF_* to move them
Documentation/filesystems/Locking +2 -0
@@ -500,12 +500,14 @@ More details about quota locking can be prototypes: void (*open)(struct vm_area_struct*); void (*close)(struct vm_area_struct*); + struct page *(*fault)(struct vm_area_struct*, struct fault_data *); struct page *(*nopage)(struct vm_area_struct*, unsigned long, int *); locking rules: BKL mmap_sem open: no yes close: no yes +fault: no yes nopage: no yes ================================================================================
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 5/7 mm: add vm_insert_pfn
In reply to Nick PigginAdd a vm_insert_pfn helper, so that ->fault handlers can have nopfn functionality by installing their own pte and returning NULL.
include/linux/mm.h +1 -0
@@ -1151,6 +1151,7 @@ unsigned long vmalloc_to_pfn(void *addr)int remap_pfn_range (struct vm_area_struct *,unsigned long addr,unsigned long pfn,unsigned long size,pgprot_t );int vm_insert_page (struct vm_area_struct *,unsigned long addr,struct page *); +int vm_insert_pfn (struct vm_area_struct *,unsigned long addr,unsigned long pfn);struct page *follow_page (struct vm_area_struct *,unsigned long address,unsigned int foll_flags);
mm/memory.c +44 -0
50 lines not on this page · raw message
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 6/7 mm: merge nopfn into fault
In reply to Nick PigginRemove ->nopfn and reimplement the only existing handler using ->fault
drivers/char/mspec.c +19 -10
@@ -182,24 +182,25 @@ mspec_close(struct vm_area_struct *vma)/* -* mspec_nopfn +* mspec_fault * * Creates a mspec page and maps it to user space. */ -static unsigned long -mspec_nopfn (struct vm_area_struct *vma,unsigned long address) +static struct page * +mspec_fault (struct fault_data *fdata) {unsigned long paddr, maddr;unsigned long pfn; -int index; -struct vma_data *vdata = vma->vm_private_data; +int index = fdata->pgoff; +struct vma_data *vdata = fdata->vma->vm_private_data; - index = (address - vma->vm_start) >>PAGE_SHIFT ; maddr = (volatile unsigned long ) vdata->maddr[index];if (maddr ==0 ) { maddr =uncached_alloc_page (numa_node_id ()); -if (maddr ==0 ) -return NOPFN_OOM ; +if (maddr ==0 ) { + fdata->type =VM_FAULT_OOM ; +return NULL ; + }spin_lock (&vdata->lock);if (vdata->maddr[index] ==0 ) {
@@ -219,13 +220,21 @@ mspec_nopfn(struct vm_area_struct *vma, pfn = paddr >>PAGE_SHIFT ; -return pfn; + fdata->type =VM_FAULT_MINOR ; +/* +* vm_insert_pfn can fail with -EBUSY, but in that case it will +* be because another thread has installed the pte first, so it +* is no problem. +*/ +vm_insert_pfn (fdata->vma, fdata->address, pfn); + +return NULL ; }static struct vm_operations_struct mspec_vm_ops = { .open = mspec_open, .close = mspec_close, - .nopfn = mspec_nopfn + .fault = mspec_fault, };/*
include/linux/mm.h +0 -7
@@ -228,7 +228,6 @@ struct vm_operations_struct {void (*close)(struct vm_area_struct * area);struct page * (*fault)(struct vm_area_struct *vma,struct fault_data * fdata);struct page * (*nopage)(struct vm_area_struct * area,unsigned long address,int *type); -unsigned long (*nopfn)(struct vm_area_struct * area,unsigned long address);int (*populate)(struct vm_area_struct * area,unsigned long address,unsigned long len,pgprot_t prot,unsigned long pgoff,int nonblock);/* notification that a previously read-only page is about to become
@@ -659,12 +658,6 @@ static inline int page_mapped(struct pag#define NOPAGE_OOM ((struct page *) (-1))/* -* Error return values for the *_nopfn functions -*/ -#define NOPFN_SIGBUS ((unsigned long) -1) -#define NOPFN_OOM ((unsigned long) -2) - -/* * Different kinds of faults, as returned by handle_mm_fault(). * Used to decide whether a process gets delivered SIGBUS or * just gets major/minor fault counters bumped up.
mm/memory.c +5 -51
74 lines not on this page · raw message
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH 7/7 mm: remove legacy cruft
In reply to Nick PigginRemove legacy filemap_nopage and all of the .populate API cruft.
This patch is optional and can be left out (eg. for a cleaner merge with -mm), and rebased after the previous patches go upstream.
include/linux/mm.h | 9 -- mm/filemap.c | 195 ----------------------------------------------------- mm/fremap.c | 71 ++----------------- mm/memory.c | 37 ++-------- 4 files changed, 21 insertions(+), 291 deletions(-)
include/linux/mm.h +0 -8
@@ -228,8 +228,6 @@ struct vm_operations_struct {void (*close)(struct vm_area_struct * area);struct page * (*fault)(struct vm_area_struct *vma,struct fault_data * fdata);struct page * (*nopage)(struct vm_area_struct * area,unsigned long address,int *type); -int (*populate)(struct vm_area_struct * area,unsigned long address,unsigned long len,pgprot_t prot,unsigned long pgoff,int nonblock); -/* notification that a previously read-only page is about to become * writable, if an error is returned it will cause a SIGBUS */ int (*page_mkwrite)(struct vm_area_struct *vma,struct page *page);
@@ -771,8 +769,6 @@ static inline void unmap_shared_mapping_extern int vmtruncate (struct inode * inode,loff_t offset);extern int vmtruncate_range (struct inode * inode,loff_t offset,loff_t end); -extern int install_page (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,struct page *page,pgprot_t prot); -extern int install_file_pte (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long addr,unsigned long pgoff,pgprot_t prot);#ifdef CONFIG_MMU extern int __handle_mm_fault(struct mm_struct *mm,struct vm_area_struct *vma,
@@ -1083,10 +1079,6 @@ extern void truncate_inode_pages_range(s/* generic vm_area_ops exported for stackable file systems */ extern struct page *filemap_fault (struct vm_area_struct *,struct fault_data *); -extern struct page * __deprecated_for_modules filemap_nopage( -struct vm_area_struct *,unsigned long ,int *); -extern int __deprecated_for_modules filemap_populate(struct vm_area_struct *, -unsigned long ,unsigned long ,pgprot_t ,unsigned long ,int );/* mm/page-writeback.c */ int write_one_page (struct page *page,int wait);
mm/filemap.c +0 -195
201 lines not on this page · raw message
mm/fremap.c +9 -62
93 lines not on this page · raw message
mm/memory.c +10 -26
@@ -2337,18 +2337,10 @@ static int do_linear_fault(struct mm_str - vma->vm_start) >> PAGE_CACHE_SHIFT) + vma->vm_pgoff;unsigned int flags = (write_access ?FAULT_FLAG_WRITE :0 ); -return __do_fault (mm, vma, address, page_table, pmd, pgoff, flags, orig_pte); +return __do_fault (mm, vma, address, page_table, pmd, pgoff, + flags, orig_pte); } -static int do_nonlinear_fault (struct mm_struct *mm,struct vm_area_struct *vma, -unsigned long address,pte_t *page_table,pmd_t *pmd, -int write_access,pgoff_t pgoff,pte_t orig_pte) -{ -unsigned int flags =FAULT_FLAG_NONLINEAR | - (write_access ?FAULT_FLAG_WRITE :0 ); - -return __do_fault (mm, vma, address, page_table, pmd, pgoff, flags, orig_pte); -}/* * Fault of a previously existing named mapping. Repopulate the pte
@@ -2359,17 +2351,19 @@ static int do_nonlinear_fault(struct mm_* but allow concurrent faults), and pte mapped but not yet locked. * We return with mmap_sem still held, but pte unmapped and unlocked. */ -static int do_file_page (struct mm_struct *mm,struct vm_area_struct *vma, +static int do_nonlinear_fault (struct mm_struct *mm,struct vm_area_struct *vma,unsigned long address,pte_t *page_table,pmd_t *pmd,int write_access,pte_t orig_pte) { +unsigned int flags =FAULT_FLAG_NONLINEAR | + (write_access ?FAULT_FLAG_WRITE :0 );pgoff_t pgoff; -int err;if (!pte_unmap_same (mm, pmd, page_table, orig_pte))return VM_FAULT_MINOR ; -if (unlikely (!(vma->vm_flags &VM_NONLINEAR ))) { +if (unlikely (!(vma->vm_flags &VM_NONLINEAR ) || + !(vma->vm_flags &VM_CAN_NONLINEAR ))) {/* * Page table corrupted: show pte and kill process. */
@@ -2379,18 +2373,8 @@ static int do_file_page(struct mm_struct pgoff =pte_to_pgoff (orig_pte); -if (vma->vm_ops && vma->vm_ops->fault) -return do_nonlinear_fault (mm, vma, address, page_table, pmd, - write_access, pgoff, orig_pte); - -/* We can then assume vm->vm_ops && vma->vm_ops->populate */ - err = vma->vm_ops->populate (vma, address &PAGE_MASK ,PAGE_SIZE , - vma->vm_page_prot, pgoff,0 ); -if (err == -ENOMEM ) -return VM_FAULT_OOM ; -if (err) -return VM_FAULT_SIGBUS ; -return VM_FAULT_MAJOR ; +return __do_fault (mm, vma, address, page_table, pmd, pgoff, + flags, orig_pte); }/*
@@ -2426,7 +2410,7 @@ static inline int handle_pte_fault(struc pte, pmd, write_access); }if (pte_file (entry)) -return do_file_page (mm, vma, address, +return do_nonlinear_fault (mm, vma, address, pte, pmd, write_access, entry);return do_swap_page (mm, vma, address, pte, pmd, write_access, entry);
Documentation/feature-removal-schedule.txt +0 -18
@@ -198,24 +198,6 @@ Who: Nick Piggin <[email protected]> --------------------------- -What: filemap_nopage, filemap_populate -When: February 2007 -Why: These legacy interfaces no longer have any callers in the kernel and - any functionality provided can be provided with filemap_fault. The - removal schedule is short because they are a big maintainence burden - and have some bugs. -Who: Nick Piggin <[email protected]> - ---------------------------- - -What: vm_ops.populate, install_page -When: February 2007 -Why: These legacy interfaces no longer have any callers in the kernel and - any functionality provided can be provided with vm_ops.fault. -Who: Nick Piggin <[email protected]> - ---------------------------- - What: vm_ops.nopage When: October 2007, provided in-kernel callers have been converted Why: This interface is replaced by vm_ops.fault, but it has been around
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>