Skip to contentSkip to conversation

2.6.19-rc1-git5: consolidation of file backed fault handlers

RFC In review Nick Piggin 8 participants 37 messages Oct 10, 2006 – Oct 21, 2006 5 messages not in the archive

Subsystems (12)
lore
Share & tools
lore
#0 lore raw

This patchset is against 2.6.19-rc1-mm1 up to numa-add-zone_to_nid-function-swap_prefetch.patch (ie. no readahead stuff, which causes big rejects and would be much easier to fix in readahead patches than here). Other than this feature, the -mm specific stuff is pretty simple (mainly straightforward filesystem conversions).

Changes since last round:
- trimmed the cc list, no big changes since last time.
- fix the few buglets preventing it from actually booting
- reinstate filemap_nopage and filemap_populate, because they're exported symbols even though no longer used in the tree. Schedule for removal.
- initialise fault_data one field at a time (akpm)
- change prototype of ->fault so it takes a vma (hch)

Has passed an allyesconfig on G5, and booted and stress tested (on ext3 and tmpfs) on 2x P4 Xeon with my userspace tester (which I will send in a reply to this email). (stress tests run with the set_page_dirty_buffers fix that is upstream).

Please apply.

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>

#1 lore raw

PATCH 1/5 mm: fault vs invalidate/truncate check

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
Index: linux-2.6/mm/filemap.c
===================================================================
@@ -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>

#2 lore raw

PATCH 2/5 mm: fault vs invalidate/truncate race fix

In reply to Nick Piggin

Fix 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 allows significant simplifications in do_no_page.

include/linux/mm.h +5 -0
Index: linux-2.6/include/linux/mm.h
===================================================================
@@ -166,6 +166,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 -57

133 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
Index: linux-2.6/mm/shmem.c
===================================================================
@@ -81,6 +81,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,
@@ -1209,8 +1210,10 @@ repeat:
 	}
 done:
 	if (*pagep != filepage) {
-		unlock_page(filepage);
 		*pagep = filepage;
+		if (sgp != SGP_NOPAGE)
+			unlock_page(filepage);
+
 	}
 	return 0;
 
@@ -1229,13 +1232,15 @@ struct page *shmem_nopage(struct vm_area
 	unsigned 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;
 
@@ -1333,6 +1338,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;
 }
 
@@ -2523,5 +2529,6 @@ int shmem_zero_setup(struct vm_area_stru
 		fput(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
Index: linux-2.6/fs/ncpfs/mmap.c
===================================================================
@@ -123,6 +123,7 @@ int ncp_mmap(struct file *file, struct v
 		return -EFBIG;
 
 	vma->vm_ops = &ncp_file_mmap;
+	vma->vm_flags |= VM_CAN_INVALIDATE;
 	file_accessed(file);
 	return 0;
 }
fs/ocfs2/mmap.c +1 -0
Index: linux-2.6/fs/ocfs2/mmap.c
===================================================================
@@ -155,6 +155,7 @@ int ocfs2_mmap(struct file *file, struct
 {
 	file_accessed(file);
 	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
Index: linux-2.6/fs/xfs/linux-2.6/xfs_file.c
===================================================================
@@ -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_dentry->d_inode)->v_vfsp->vfs_flag & VFS_DMI)
ipc/shm.c +1 -0
Index: linux-2.6/ipc/shm.c
===================================================================
@@ -230,6 +230,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_dentry->d_inode->i_ino);
mm/truncate.c +10 -0
Index: linux-2.6/mm/truncate.c
===================================================================
@@ -163,6 +163,11 @@ void truncate_inode_pages_range(struct a
 				unlock_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);
 		}
@@ -200,6 +205,11 @@ void truncate_inode_pages_range(struct a
 				break;
 			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
Index: linux-2.6/fs/gfs2/ops_file.c
===================================================================
@@ -396,6 +396,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;
fs/nfs/fscache.h +3 -1
Index: linux-2.6/fs/nfs/fscache.h
===================================================================
@@ -198,8 +198,10 @@ static inline void nfs_fscache_disable_f
 static inline void nfs_fscache_install_vm_ops(struct inode *inode,
 					      struct vm_area_struct *vma)
 {
-	if (NFS_I(inode)->fscache)
+	if (NFS_I(inode)->fscache) {
 		vma->vm_ops = &nfs_fs_vm_operations;
+		vma->vm_flags |= VM_CAN_INVALIDATE;
+	}
 }
 
 /*
fs/afs/file.c +1 -0
Index: linux-2.6/fs/afs/file.c
===================================================================
@@ -82,6 +82,7 @@ static int afs_file_mmap(struct file *fi
 
 	file_accessed(file);
 	vma->vm_ops = &afs_fs_vm_operations;
+	vma->vm_flags |= VM_CAN_INVALIDATE;
 	return 0;
 }
 

--
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>

#3 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 47 lines · On Tue, 10 Oct 2006 16:21:49 +0200 (CEST) Nick Piggin <[email protected]> wrote:

Fix 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).

Quoted 2 lines

This allows significant simplifications in do_no_page.

The (unchangelogged) changes to filemap_nopage() appear to have switched the try-the-read-twice logic into try-it-forever logic. I think it'll hang if there's a bad sector?

--
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>

#4 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 51 lines · Andrew Morton
Quoted 47 lines · On Tue, 10 Oct 2006 16:21:49 +0200 (CEST) Nick Piggin <[email protected]> wrote:

Fix 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).

Quoted 8 lines
Quoted 3 lines

This allows significant simplifications in do_no_page.

The (unchangelogged) changes to filemap_nopage() appear to have switched the try-the-read-twice logic into try-it-forever logic. I think it'll hang if there's a bad sector?

It should fall out if there is an error. I think. I'll go through it again.

Some of the changes simply come about due to find_lock_page meaning that we never see an !uptodate page unless it is an error, so some of that stuff can come out.

But I see that it does read twice. Do you want that behaviour retained? It seems like at this level it would be logical to read it once and let lower layers take care of any retries?

--

Send instant messages to your online friends http://au.messenger.yahoo.com

--
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>

#5 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 3 lines · On Wed, 11 Oct 2006 15:39:22 +1000 Nick Piggin <[email protected]> wrote:

But I see that it does read twice. Do you want that behaviour retained? It seems like at this level it would be logical to read it once and let lower layers take care of any retries?

argh. Linus has good-sounding reasons for retrying the pagefault-path's read a single time, but I forget what they are. Something to do with networked filesystems? (adds cc)

--
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>

#6 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 10 lines · Andrew Morton
Quoted 3 lines · On Wed, 11 Oct 2006 15:39:22 +1000 Nick Piggin <[email protected]> wrote:

But I see that it does read twice. Do you want that behaviour retained? It seems like at this level it would be logical to read it once and let lower layers take care of any retries?

argh. Linus has good-sounding reasons for retrying the pagefault-path's read a single time, but I forget what they are. Something to do with networked filesystems? (adds cc)

While you're there, can anyone tell me why we want an external ptracer to be able to access pages that are outside i_size? I haven't removed the logic of course, but I'm curious about the history and usage of such a thing.

--
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>

#7 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

In reply to Andrew Morton
Quoted 11 lines · Andrew Morton
Quoted 3 lines · On Wed, 11 Oct 2006 15:39:22 +1000 Nick Piggin <[email protected]> wrote:

But I see that it does read twice. Do you want that behaviour retained? It seems like at this level it would be logical to read it once and let lower layers take care of any retries?

argh. Linus has good-sounding reasons for retrying the pagefault-path's read a single time, but I forget what they are. Something to do with networked filesystems? (adds cc)

Indeed. We _have_ to re-try a failed IO that we didn't start ourselves.

The original IO could have been started by a person who didn't have permissions to actually carry it out successfully, so if you enter with the page locked (because somebody else started the IO), and you wait for the page and it's not up-to-date afterwards, you absolutely _have_ to try the IO, and can only return a real IO error after your _own_ IO has failed.

There is another issue too: even if the page was marked as having an error when we entered (and no longer locked - maybe the IO failed last time around), we should _still_ re-try. It might be a temporary error that has since gone away, and if we don't re-try, we can end up in the totally untenable situation where the kernel makes a soft error into a hard one.

Neither case simply isn't acceptable. End result: only things like read-ahead should actually honor the "page exists but is not up-to-date" as a "don't even try".

Linus

--
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>

#8 lore raw

Re: SPAM: Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 15 lines · Linus Torvalds
Quoted 11 lines · Andrew Morton
Quoted 3 lines · On Wed, 11 Oct 2006 15:39:22 +1000 Nick Piggin <[email protected]> wrote:

But I see that it does read twice. Do you want that behaviour retained? It seems like at this level it would be logical to read it once and let lower layers take care of any retries?

argh. Linus has good-sounding reasons for retrying the pagefault-path's read a single time, but I forget what they are. Something to do with networked filesystems? (adds cc)

Quoted 8 lines

Indeed. We _have_ to re-try a failed IO that we didn't start ourselves.

The original IO could have been started by a person who didn't have permissions to actually carry it out successfully, so if you enter with the page locked (because somebody else started the IO), and you wait for the page and it's not up-to-date afterwards, you absolutely _have_ to try the IO, and can only return a real IO error after your _own_ IO has failed.

Sure, but we currently try to read _twice_, don't we?

Quoted 5 lines

There is another issue too: even if the page was marked as having an error when we entered (and no longer locked - maybe the IO failed last time around), we should _still_ re-try. It might be a temporary error that has since gone away, and if we don't re-try, we can end up in the totally untenable situation where the kernel makes a soft error into a hard one.

Yes, and in that case I think the page should be !Uptodate, so no problem there.

--
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>

#9 lore raw

Re: SPAM: Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 9 lines · Nick Piggin
Quoted 7 lines

The original IO could have been started by a person who didn't have permissions to actually carry it out successfully, so if you enter with the page locked (because somebody else started the IO), and you wait for the page and it's not up-to-date afterwards, you absolutely _have_ to try the IO, and can only return a real IO error after your _own_ IO has failed.

Sure, but we currently try to read _twice_, don't we?

Well, we have the read-ahead, and then the real read. By the time we do the real read, we have forgotten about the read-ahead details, so..

We also end up often having a _third_ one, simply because the _user_ tries it twice: it gets a partial IO read first, and then tries to continue and won't give up until it gets a real error.

So yes, we can end up reading it even more than twice, if only due to standard UNIX interfaces: you always have to have one extra "read()" system call in order to get the final error (or - much more commonly - EOF, of course).

If we tracked the read-aheads that _we_ started, we could probably get rid of one of them.

Linus

--
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>

#10 lore raw

Re: SPAM: Re: SPAM: Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 11 lines · Linus Torvalds
Quoted 8 lines · Nick Piggin
Quoted 7 lines

The original IO could have been started by a person who didn't have permissions to actually carry it out successfully, so if you enter with the page locked (because somebody else started the IO), and you wait for the page and it's not up-to-date afterwards, you absolutely _have_ to try the IO, and can only return a real IO error after your _own_ IO has failed.

Quoted 4 lines
Quoted 1 line

Sure, but we currently try to read _twice_, don't we?

Well, we have the read-ahead, and then the real read. By the time we do the real read, we have forgotten about the read-ahead details, so..

I mean filemap_nopage does *two* synchronous reads when finding a !uptodate page. This is despite the comment saying that it retries once on 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>

#11 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 3 lines · Nick Piggin

I mean filemap_nopage does *two* synchronous reads when finding a !uptodate page. This is despite the comment saying that it retries once on error.

Ahh.

Yes, now that you point to the actual code, that does look ugly.

I think it's related to the

	ClearPageError(page);

thing, and probably related to that function being rather old and having gone through several re-organizations. I suspect we used to fall through to the error handling code regardless of whether we did the read ourselves etc.

Are you saying that something like this would be preferable?

Linus

---

mm/filemap.c +3 -10
index 3464b68..e5ecf42 100644
@@ -1496,6 +1496,8 @@ page_not_uptodate:
 		goto success;
 	}
 
+	/* Clear any potential old errors, and try to read.. */
+	ClearPageError(page);
 	error = mapping->a_ops->readpage(file, page);
 	if (!error) {
 		wait_on_page_locked(page);
@@ -1526,21 +1528,12 @@ page_not_uptodate:
 		unlock_page(page);
 		goto success;
 	}
-	ClearPageError(page);
-	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;
-	}
 
 	/*
 	 * Things didn't work out. Return zero to tell the
 	 * mm layer so, possibly freeing the page cache page first.
 	 */
+	unlock_page(page);
 	shrink_readahead_size_eio(file, ra);
 	page_cache_release(page);
 	return NOPAGE_SIGBUS;

--
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>

#12 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 11 lines · Linus Torvalds
Quoted 3 lines · Nick Piggin

I mean filemap_nopage does *two* synchronous reads when finding a !uptodate page. This is despite the comment saying that it retries once on error.

Ahh.

Yes, now that you point to the actual code, that does look ugly.

Quoted 8 lines

I think it's related to the

	ClearPageError(page);

thing, and probably related to that function being rather old and having gone through several re-organizations. I suspect we used to fall through to the error handling code regardless of whether we did the read ourselves etc.

Yeah, it may have even been a mismerge at some point in time.

Quoted 1 line

Are you saying that something like this would be preferable?

I think so, it is neater and clearer. I actually didn't even bother relocking and checking the page again on readpage error so got rid of quite a bit of code.

Quoted 30 lines

Linus

---

mm/filemap.c +2 -9
index 3464b68..e5ecf42 100644
@@ -1496,6 +1496,8 @@ page_not_uptodate:
 		goto success;
 	}
 
+	/* Clear any potential old errors, and try to read.. */
+	ClearPageError(page);
 	error = mapping->a_ops->readpage(file, page);
 	if (!error) {
 		wait_on_page_locked(page);
@@ -1526,21 +1528,12 @@ page_not_uptodate:
 		unlock_page(page);
 		goto success;
 	}
-	ClearPageError(page);
-	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;

mm/filemap.c page_not_uptodate: · line 1539

Quoted 10 lines
mm/filemap.c +1 -1
-	}
 
 	/*
 	 * Things didn't work out. Return zero to tell the
 	 * mm layer so, possibly freeing the page cache page first.
 	 */
+	unlock_page(page);
 	shrink_readahead_size_eio(file, ra);
 	page_cache_release(page);
 	return NOPAGE_SIGBUS;

--
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>

#13 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 6 lines · Nick Piggin
Quoted 2 lines

Are you saying that something like this would be preferable?

I think so, it is neater and clearer. I actually didn't even bother relocking and checking the page again on readpage error so got rid of quite a bit of code.

Well, the readpage error should be rare (and for the _normal_ case we just do the "wait_on_page_locked()" thing). And I think we should lock the page in order to do the truncation check, no?

But I don't have any really strong feelings. I'm certainly ok with the patch I sent out. How about putting it through -mm? Here's my sign-off:

if you want to send it off to Andrew (or if Andrew wants to just take it himself ;)

Btw, how did you even notice this? Just by reading the source, or because you actually saw multiple errors reported?

Linus

--
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>

#14 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 6 lines · Linus Torvalds
Quoted 3 lines · Nick Piggin
Quoted 2 lines

Are you saying that something like this would be preferable?

Quoted 7 lines
Quoted 3 lines

I think so, it is neater and clearer. I actually didn't even bother relocking and checking the page again on readpage error so got rid of quite a bit of code.

Well, the readpage error should be rare (and for the _normal_ case we just do the "wait_on_page_locked()" thing). And I think we should lock the page in order to do the truncation check, no?

Definitely.

Quoted 7 lines

But I don't have any really strong feelings. I'm certainly ok with the patch I sent out. How about putting it through -mm? Here's my sign-off:

if you want to send it off to Andrew (or if Andrew wants to just take it himself ;)

OK... maybe it can wait till the other changes, and we can think about it then. I'll carry around the split out patct, though.

Quoted 2 lines

Btw, how did you even notice this? Just by reading the source, or because you actually saw multiple errors reported?

Reading the source, thinking about the cleanups we can do if filemap_nopage takes the page lock...

--
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>

#15 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

In reply to Nick Piggin
Quoted 15 lines · On Tue, 10 Oct 2006 16:21:49 +0200 (CEST) Nick Piggin <[email protected]> wrote:
mm/filemap.c +2 -1
@@ -1392,9 +1392,10 @@ struct page *filemap_nopage(struct vm_ar
 	unsigned long size, pgoff;
 	int did_readaround = 0, majmin = VM_FAULT_MINOR;
 
+	BUG_ON(!(area->vm_flags & VM_CAN_INVALIDATE));
+
 	pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
 
-retry_all:
 	size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	if (pgoff >= size)
 		goto outside_data_content;

mm/filemap.c retry_all: · line 1420

Quoted 5 lines
mm/filemap.c +1 -1
@@ -1416,7 +1417,7 @@ retry_all:
 	 * Do we have something in the page cache already?
 	 */
 retry_find:
-	page = find_get_page(mapping, pgoff);
+	page = find_lock_page(mapping, pgoff);

Here's a little problem. Locking the page in the pagefault handler takes our deadlock while writing from a mmapped copy of the page into the same page from "extremely hard to hit" to "super-easy to hit". Try running write-deadlock-demo.c from
http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz

It conveniently deadlocks while holding mmap_sem, so `ps' get stuck too.

So this whole idea of locking the page in the fault handler is off the table until we fix that deadlock for real. Coincidentally I started coding a fix for that a couple of weeks ago, but spend too much time with my nose in other people's crap to get around to writing my own crap.

The basic idea is

- revert the recent changes to the core write() code (the ones which killed writev() performance, especially on NFS overwrites).

- clean some stuff up

- modify the core of write() so that instead of doing copy_from_user(), we do inc_preempt_count();copy_from_user_inatomic(). So we never enter the pagefault handler while holding the lock on the pagecache page.

If the fault happens, we run commit_write() on however much stuff we managed to copy and then go back and try to fault the target page back in again. Repeat for ten times then give up.

It gets tricky because it means that we'll need to go back to zeroing out the uncopied part of the pagecache page before commit_write+unlock_page(). This will resurrect the recently-fixed problem where userspace can fleetingly see a bunch of zeroes in pagecache where it expected to see either the old data or the new data.

But I don't think that problem was terribly serious, and we can improve the situation quite a lot by not doing that zeroing if the page is already up-to-date.

Anyway, if you're feeling up to it I'll document the patches I have and hand them over - they're not making much progress here.

--
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>

#16 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 32 lines · Andrew Morton
Quoted 19 lines · On Tue, 10 Oct 2006 16:21:49 +0200 (CEST) Nick Piggin <[email protected]> wrote:
mm/filemap.c +2 -1
@@ -1392,9 +1392,10 @@ struct page *filemap_nopage(struct vm_ar
 	unsigned long size, pgoff;
 	int did_readaround = 0, majmin = VM_FAULT_MINOR;
 
+	BUG_ON(!(area->vm_flags & VM_CAN_INVALIDATE));
+
 	pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
 
-retry_all:
 	size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	if (pgoff >= size)
 		goto outside_data_content;
@@ -1416,7 +1417,7 @@ retry_all:
 	 * Do we have something in the page cache already?
 	 */

retry_find:

mm/filemap.c

Quoted 2 lines
mm/filemap.c +1 -1
-	page = find_get_page(mapping, pgoff);
+	page = find_lock_page(mapping, pgoff);

Here's a little problem. Locking the page in the pagefault handler takes our deadlock while writing from a mmapped copy of the page into the same page from "extremely hard to hit" to "super-easy to hit". Try running write-deadlock-demo.c from
http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz

Quoted 5 lines

It conveniently deadlocks while holding mmap_sem, so `ps' get stuck too.

So this whole idea of locking the page in the fault handler is off the table until we fix that deadlock for real.

OK. Can it sit in -mm for now, though? Or is this deadlock less theoretical than it sounds? At any rate, thanks for catching this.

Quoted 11 lines

Coincidentally I started coding
a fix for that a couple of weeks ago, but spend too much time with my nose in other people's crap to get around to writing my own crap.

The basic idea is

- revert the recent changes to the core write() code (the ones which killed writev() performance, especially on NFS overwrites).

- clean some stuff up

Quoted 8 lines

- modify the core of write() so that instead of doing copy_from_user(), we do inc_preempt_count();copy_from_user_inatomic(). So we never enter the pagefault handler while holding the lock on the pagecache page.

If the fault happens, we run commit_write() on however much stuff we managed to copy and then go back and try to fault the target page back in again. Repeat for ten times then give up.

Without looking at any code, perhaps we could instead run get_user_pages and copy the memory that way.

We'd still want to do try the initial copy_from_user, because the TLB is quite likely to exist or at least the pte will exist so the low level TLB refill can reach it - so we don't want to walk the pagetables manually if we can help it.

At that point, if we end up doing the get_user_pages thing, do we even need to do the intermediate commit_write()? Or just do the whole copy (the partial copied data is going to be in cache on physically indexed caches anyway, so it will be very low cost to copy again). And it should be a reasonably unlikely path... but I'll instrument it.

Quoted 6 lines

It gets tricky because it means that we'll need to go back to zeroing out the uncopied part of the pagecache page before commit_write+unlock_page(). This will resurrect the recently-fixed problem where userspace can fleetingly see a bunch of zeroes in pagecache where it expected to see either the old data or the new data.

Quoted 7 lines

But I don't think that problem was terribly serious, and we can improve the situation quite a lot by not doing that zeroing if the page is already up-to-date.

Anyway, if you're feeling up to it I'll document the patches I have and hand them over - they're not making much progress here.

Yeah I'll have a go.

--

Send instant messages to your online friends http://au.messenger.yahoo.com

--
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>

#17 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

Quoted 34 lines · On Wed, 11 Oct 2006 15:50:11 +1000 Nick Piggin <[email protected]> wrote:
Quoted 32 lines · Andrew Morton
Quoted 19 lines · On Tue, 10 Oct 2006 16:21:49 +0200 (CEST) Nick Piggin <[email protected]> wrote:
mm/filemap.c +2 -1
@@ -1392,9 +1392,10 @@ struct page *filemap_nopage(struct vm_ar
 	unsigned long size, pgoff;
 	int did_readaround = 0, majmin = VM_FAULT_MINOR;
 
+	BUG_ON(!(area->vm_flags & VM_CAN_INVALIDATE));
+
 	pgoff = ((address-area->vm_start) >> PAGE_CACHE_SHIFT) + area->vm_pgoff;
 
-retry_all:
 	size = (i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
 	if (pgoff >= size)
 		goto outside_data_content;
@@ -1416,7 +1417,7 @@ retry_all:
 	 * Do we have something in the page cache already?
 	 */

retry_find:

mm/filemap.c

Quoted 2 lines
mm/filemap.c +1 -1
-	page = find_get_page(mapping, pgoff);
+	page = find_lock_page(mapping, pgoff);

Here's a little problem. Locking the page in the pagefault handler takes our deadlock while writing from a mmapped copy of the page into the same page from "extremely hard to hit" to "super-easy to hit". Try running write-deadlock-demo.c from
http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz

Quoted 7 lines
Quoted 5 lines

It conveniently deadlocks while holding mmap_sem, so `ps' get stuck too.

So this whole idea of locking the page in the fault handler is off the table until we fix that deadlock for real.

OK. Can it sit in -mm for now, though?

argh. It took me two goes to unpickle all the bits and pieces (please patch things like cachefiles separately, unless you want your stuff to be merged after that stuff) and now I've gone and deleted it all.

Maybe later? We do have that infinite-loop-on-EIO to look at as well.

Quoted 2 lines

Or is this deadlock less theoretical
than it sounds?

I _think_ people have hit it in the wild, due to memory pressure.

But no, it's a silly thing which will only hit when people are running silly tests under silly amounts of load.

Or if they're trying to kill your computer...

Quoted 17 lines

At any rate, thanks for catching this.

Quoted 15 lines

Coincidentally I started coding
a fix for that a couple of weeks ago, but spend too much time with my nose in other people's crap to get around to writing my own crap.

The basic idea is

- revert the recent changes to the core write() code (the ones which killed writev() performance, especially on NFS overwrites).

- clean some stuff up

- modify the core of write() so that instead of doing copy_from_user(), we do inc_preempt_count();copy_from_user_inatomic(). So we never enter the pagefault handler while holding the lock on the pagecache page.

Quoted 7 lines
Quoted 4 lines

If the fault happens, we run commit_write() on however much stuff we managed to copy and then go back and try to fault the target page back in again. Repeat for ten times then give up.

Without looking at any code, perhaps we could instead run get_user_pages and copy the memory that way.

That would certainly work, but we've always shied away from doing that because of the performance implications.

Quoted 4 lines

We'd still want to do try the initial copy_from_user, because the TLB is quite likely to exist or at least the pte will exist so the low level TLB refill can reach it - so we don't want to walk the pagetables manually if we can help it.

Yeah, that's an alternative to the fault-it-in-ten-times-then-give-up approach.

Quoted 2 lines

At that point, if we end up doing the get_user_pages thing, do we even need to do the intermediate commit_write()?

Yes, we will. get_user_pages() will run the pagefault handler, which will lock the page, so we're back to square one.

Quoted 5 lines

Or just do the whole copy (the
partial
copied data is going to be in cache on physically indexed caches anyway, so it will be very low cost to copy again). And it should be a reasonably unlikely path... but I'll instrument it.

I'm not sure what you're suggesting here.

Quoted 10 lines
Quoted 10 lines

It gets tricky because it means that we'll need to go back to zeroing out the uncopied part of the pagecache page before commit_write+unlock_page(). This will resurrect the recently-fixed problem where userspace can fleetingly see a bunch of zeroes in pagecache where it expected to see either the old data or the new data.

But I don't think that problem was terribly serious, and we can improve the situation quite a lot by not doing that zeroing if the page is already up-to-date.

Quoted 5 lines
Quoted 3 lines

Anyway, if you're feeling up to it I'll document the patches I have and hand them over - they're not making much progress here.

Yeah I'll have a go.

Thanks.

--
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>

#18 lore raw

PATCH 1/6 revert "generic_file_buffered_write(): handle zero length iovec segments"

In reply to Nick Piggin

Revert 81b0c8713385ce1b1b9058e916edcf9561ad76d6.

This was a bugfix against 6527c2bdf1f833cc18e8f42bd97973d583e4aa83, which we also revert.

---

 mm/filemap.c |    9 +--------
 mm/filemap.h |    4 ++--
 2 files changed, 3 insertions(+), 10 deletions(-)
mm/filemap.c renamed from mm/filemap.c~revert-generic_file_buffered_write-handle-zero-length-iovec-segments +1 -8
diff -puN mm/filemap.c~revert-generic_file_buffered_write-handle-zero-length-iovec-segments mm/filemap.c
@@ -2121,12 +2121,6 @@ generic_file_buffered_write(struct kiocb
 			break;
 		}
 
-		if (unlikely(bytes == 0)) {
-			status = 0;
-			copied = 0;
-			goto zero_length_segment;
-		}
-
 		status = a_ops->prepare_write(file, page, offset, offset+bytes);
 		if (unlikely(status)) {
 			loff_t isize = i_size_read(inode);
@@ -2156,8 +2150,7 @@ generic_file_buffered_write(struct kiocb
 			page_cache_release(page);
 			continue;
 		}
-zero_length_segment:
-		if (likely(copied >= 0)) {
+		if (likely(copied > 0)) {
 			if (!status)
 				status = copied;
 
mm/filemap.h renamed from mm/filemap.h~revert-generic_file_buffered_write-handle-zero-length-iovec-segments +2 -2
diff -puN mm/filemap.h~revert-generic_file_buffered_write-handle-zero-length-iovec-segments mm/filemap.h
@@ -87,7 +87,7 @@ filemap_set_next_iovec(const struct iove
 	const struct iovec *iov = *iovp;
 	size_t base = *basep;
 
-	do {
+	while (bytes) {
 		int copy = min(bytes, iov->iov_len - base);
 
 		bytes -= copy;
@@ -96,7 +96,7 @@ filemap_set_next_iovec(const struct iove
 			iov++;
 			base = 0;
 		}
-	} while (bytes);
+	}
 	*iovp = iov;
 	*basep = base;
 }

_

--
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>

A message that is not in the archive (sent off-list or not yet received): [email protected]

#20 lore raw

PATCH 2/6 revert "generic_file_buffered_write(): deadlock on vectored write"

Revert 6527c2bdf1f833cc18e8f42bd97973d583e4aa83

This patch fixed the following bug:

When prefaulting in the pages in generic_file_buffered_write(), we only faulted in the pages for the firts segment of the iovec. If the second of successive segment described a mmapping of the page into which we're write()ing, and that page is not up-to-date, the fault handler tries to lock the already-locked page (to bring it up to date) and deadlocks.

An exploit for this bug is in writev-deadlock-demo.c, in
http://www.zip.com.au/~akpm/linux/patches/stuff/ext3-tools.tar.gz.

(These demos assume blocksize < PAGE_CACHE_SIZE).

The problem with this fix is that it takes the kernel back to doing a single prepare_write()/commit_write() per iovec segment. So in the worst case we'll run prepare_write+commit_write 1024 times where we previously would have run it once.

<insert numbers obtained via ext3-tools's writev-speed.c here>

And apparently this change killed NFS overwrite performance, because, I suppose, it talks to the server for each prepare_write+commit_write.

So just back that patch out - we'll be fixing the deadlock by other means.

---

 mm/filemap.c |   18 +++++++-----------
 1 file changed, 7 insertions(+), 11 deletions(-)
mm/filemap.c renamed from mm/filemap.c~revert-generic_file_buffered_write-deadlock-on-vectored-write +7 -11
diff -puN mm/filemap.c~revert-generic_file_buffered_write-deadlock-on-vectored-write mm/filemap.c
@@ -2091,21 +2091,14 @@ generic_file_buffered_write(struct kiocb
 	do {
 		unsigned long index;
 		unsigned long offset;
+		unsigned long maxlen;
 		size_t copied;
 
 		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
 		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
-
-		/* Limit the size of the copy to the caller's write size */
-		bytes = min(bytes, count);
-
-		/*
-		 * Limit the size of the copy to that of the current segment,
-		 * because fault_in_pages_readable() doesn't know how to walk
-		 * segments.
-		 */
-		bytes = min(bytes, cur_iov->iov_len - iov_base);
+		if (bytes > count)
+			bytes = count;
 
 		/*
 		 * Bring in the user page that we will copy from _first_.
@@ -2113,7 +2106,10 @@ generic_file_buffered_write(struct kiocb
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
 		 */
-		fault_in_pages_readable(buf, bytes);
+		maxlen = cur_iov->iov_len - iov_base;
+		if (maxlen > bytes)
+			maxlen = bytes;
+		fault_in_pages_readable(buf, maxlen);
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
 		if (!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>

A message that is not in the archive (sent off-list or not yet received): [email protected]

#22 lore raw

PATCH 3/6 generic_file_buffered_write() cleanup

---

 mm/filemap.c |   35 ++++++++++++++++++-----------------
 1 file changed, 18 insertions(+), 17 deletions(-)
mm/filemap.c renamed from mm/filemap.c~generic_file_buffered_write-cleanup +18 -17
diff -puN mm/filemap.c~generic_file_buffered_write-cleanup mm/filemap.c
@@ -2064,16 +2064,15 @@ generic_file_buffered_write(struct kiocb
 		size_t count, ssize_t written)
 {
 	struct file *file = iocb->ki_filp;
-	struct address_space * mapping = file->f_mapping;
+	struct address_space *mapping = file->f_mapping;
 	const struct address_space_operations *a_ops = mapping->a_ops;
 	struct inode 	*inode = mapping->host;
 	long		status = 0;
 	struct page	*page;
 	struct page	*cached_page = NULL;
-	size_t		bytes;
 	struct pagevec	lru_pvec;
 	const struct iovec *cur_iov = iov; /* current iovec */
-	size_t		iov_base = 0;	   /* offset in the current iovec */
+	size_t		iov_offset = 0;	   /* offset in the current iovec */
 	char __user	*buf;
 
 	pagevec_init(&lru_pvec, 0);
@@ -2084,31 +2083,33 @@ generic_file_buffered_write(struct kiocb
 	if (likely(nr_segs == 1))
 		buf = iov->iov_base + written;
 	else {
-		filemap_set_next_iovec(&cur_iov, &iov_base, written);
-		buf = cur_iov->iov_base + iov_base;
+		filemap_set_next_iovec(&cur_iov, &iov_offset, written);
+		buf = cur_iov->iov_base + iov_offset;
 	}
 
 	do {
-		unsigned long index;
-		unsigned long offset;
-		unsigned long maxlen;
-		size_t copied;
+		pgoff_t index;		/* Pagecache index for current page */
+		unsigned long offset;	/* Offset into pagecache page */
+		unsigned long maxlen;	/* Bytes remaining in current iovec */
+		size_t bytes;		/* Bytes to write to page */
+		size_t copied;		/* Bytes copied from user */
 
-		offset = (pos & (PAGE_CACHE_SIZE -1)); /* Within page */
+		offset = (pos & (PAGE_CACHE_SIZE - 1));
 		index = pos >> PAGE_CACHE_SHIFT;
 		bytes = PAGE_CACHE_SIZE - offset;
 		if (bytes > count)
 			bytes = count;
 
+		maxlen = cur_iov->iov_len - iov_offset;
+		if (maxlen > bytes)
+			maxlen = bytes;
+
 		/*
 		 * Bring in the user page that we will copy from _first_.
 		 * Otherwise there's a nasty deadlock on copying from the
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
 		 */
-		maxlen = cur_iov->iov_len - iov_base;
-		if (maxlen > bytes)
-			maxlen = bytes;
 		fault_in_pages_readable(buf, maxlen);
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
@@ -2139,7 +2140,7 @@ generic_file_buffered_write(struct kiocb
 							buf, bytes);
 		else
 			copied = filemap_copy_from_user_iovec(page, offset,
-						cur_iov, iov_base, bytes);
+						cur_iov, iov_offset, bytes);
 		flush_dcache_page(page);
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
 		if (status == AOP_TRUNCATED_PAGE) {
@@ -2157,12 +2158,12 @@ generic_file_buffered_write(struct kiocb
 				buf += status;
 				if (unlikely(nr_segs > 1)) {
 					filemap_set_next_iovec(&cur_iov,
-							&iov_base, status);
+							&iov_offset, status);
 					if (count)
 						buf = cur_iov->iov_base +
-							iov_base;
+							iov_offset;
 				} else {
-					iov_base += status;
+					iov_offset += status;
 				}
 			}
 		}

_

--
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>

A message that is not in the archive (sent off-list or not yet received): [email protected]

#24 lore raw

PATCH 4/6 generic_file_buffered_write(): fix page prefaulting

generic_file_buffered_write() is passing the wrong length arg to fault_in_pages_readable() (I think - please check).

---

 mm/filemap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
mm/filemap.c renamed from mm/filemap.c~generic_file_buffered_write-fix-page-prefaulting +1 -1
diff -puN mm/filemap.c~generic_file_buffered_write-fix-page-prefaulting mm/filemap.c
@@ -2110,7 +2110,7 @@ generic_file_buffered_write(struct kiocb
 		 * same page as we're writing to, without it being marked
 		 * up-to-date.
 		 */
-		fault_in_pages_readable(buf, maxlen);
+		fault_in_pages_readable(buf, bytes);
 
 		page = __grab_cache_page(mapping,index,&cached_page,&lru_pvec);
 		if (!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>

A message that is not in the archive (sent off-list or not yet received): [email protected]

#26 lore raw

PATCH 5/6 generic_file_buffered_write(): max_len cleanup

More dirty code.

---

 mm/filemap.c |    5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)
mm/filemap.c renamed from mm/filemap.c~generic_file_buffered_write-max_len-cleanup +1 -4
diff -puN mm/filemap.c~generic_file_buffered_write-max_len-cleanup mm/filemap.c
@@ -2090,7 +2090,6 @@ generic_file_buffered_write(struct kiocb
 	do {
 		pgoff_t index;		/* Pagecache index for current page */
 		unsigned long offset;	/* Offset into pagecache page */
-		unsigned long maxlen;	/* Bytes remaining in current iovec */
 		size_t bytes;		/* Bytes to write to page */
 		size_t copied;		/* Bytes copied from user */
 
@@ -2100,9 +2099,7 @@ generic_file_buffered_write(struct kiocb
 		if (bytes > count)
 			bytes = count;
 
-		maxlen = cur_iov->iov_len - iov_offset;
-		if (maxlen > bytes)
-			maxlen = bytes;
+		bytes = min(cur_iov->iov_len - iov_offset, bytes);
 
 		/*
 		 * Bring in the user page that we will copy from _first_.

_

--
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>

A message that is not in the archive (sent off-list or not yet received): [email protected]

#28 lore raw

PATCH 6/6 fix pagecache write deadlocks

This is half-written and won't work.

The idea is to modify the core write() code so that it won't take a pagefault while holding a lock on the pagecache page.

- Instead of copy_from_user(), use inc_preempt_count() and copy_from_user_inatomic().

- If the copy_from_user_inatomic() hits a pagefault, it'll return a short copy.

- So zero out the remainder of the pagecache page (the uncopied bit).

- but only if the page is not uptodate.

- commit_write()

- unlock_page()

- adjust various pointers and counters

- go back and try to fault the page in again, redo the lock_page, prepare_write, copy_from_user_inatomic(), etc.

- After a certain number of retries, someone is being silly: give up.

Now, the design objective here isn't just to fix the deadlock. It's to be able to copy multiple iovec segments into the pagecache page within a single prepare-write/commit_write pair. But to do that, we'll need to prefault them.

That could get complex. Walk across the segments, touching each user page until we reach the point where we see that this iovec segment doesn't fall into the target page.

Alternatively, only prefault the *present* iovec segment. The code as designed will handle pagefaults against the user's pages quite happily. But is it efficient? Needs thought.

(I think we will end up with quite a bit of dead code as a result of this exercise - some of the fancy user-copying inlines. Needs checking when the dust has settled).

---

 mm/filemap.c |    7 ++--
 mm/filemap.h |   69 ++++++++++++++++++++++++++++++++++---------------
 2 files changed, 53 insertions(+), 23 deletions(-)
mm/filemap.c renamed from mm/filemap.c~fix-pagecache-write-deadlocks +4 -3
diff -puN mm/filemap.c~fix-pagecache-write-deadlocks mm/filemap.c
@@ -2133,11 +2133,12 @@ generic_file_buffered_write(struct kiocb
 			break;
 		}
 		if (likely(nr_segs == 1))
-			copied = filemap_copy_from_user(page, offset,
+			copied = filemap_copy_from_user_atomic(page, offset,
 							buf, bytes);
 		else
-			copied = filemap_copy_from_user_iovec(page, offset,
-						cur_iov, iov_offset, bytes);
+			copied = filemap_copy_from_user_iovec_atomic(page,
+						offset, cur_iov, iov_offset,
+						bytes);
 		flush_dcache_page(page);
 		status = a_ops->commit_write(file, page, offset, offset+bytes);
 		if (status == AOP_TRUNCATED_PAGE) {
mm/filemap.h renamed from mm/filemap.h~fix-pagecache-write-deadlocks +49 -20

96 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>

#29 lore raw

Re: [patch 2/5] mm: fault vs invalidate/truncate race fix

In reply to Nick Piggin
Quoted 2 lines

Without looking at any code, perhaps we could instead run get_user_pages and copy the memory that way.

I have a deep hatred for get_user_pages().... maybe not totally rational though :) It will also only work with things that are actually backed up by struct page. Is that ok in your case ?

Ben.

--
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>

#30 lore raw

PATCH 3/5 mm: fault handler to replace nopage and populate

In reply to Nick Piggin

Nonlinear 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 +24 -6
Index: linux-2.6/include/linux/mm.h
===================================================================
@@ -166,11 +166,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
@@ -194,6 +195,23 @@ 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.
+ */
+struct fault_data {
+	struct vm_area_struct *vma;
+	unsigned long address;
+	pgoff_t pgoff;
+	unsigned int flags;
+
+	int type;
+};
 
 /*
  * These are the virtual MM functions - opening of an area, closing and
@@ -203,6 +221,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);
@@ -614,7 +633,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
@@ -643,14 +661,13 @@ static inline int page_mapped(struct pag
 extern 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_fault
 
 static inline int shmem_lock(struct file *file, int lock,
 			     struct user_struct *user)
@@ -1043,7 +1060,8 @@ 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 struct page * __deprecated filemap_fault(struct vm_area_struct *, struct fault_data *);
+extern struct page * __deprecated 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);
 
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
Index: linux-2.6/fs/gfs2/ops_file.c
===================================================================
@@ -396,7 +396,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
Index: linux-2.6/fs/gfs2/ops_vm.c
===================================================================
@@ -42,17 +42,17 @@ static void pfault_be_greedy(struct gfs2
 		iput(&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_nopa
 	set_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
Index: linux-2.6/fs/ocfs2/mmap.c
===================================================================
@@ -59,24 +59,23 @@ static inline int ocfs2_vm_op_unblock_si
 	return sigprocmask(SIG_SETMASK, oldset, NULL);
 }
 
-static struct page *ocfs2_nopage(struct vm_area_struct * area,
-				 unsigned long address,
-				 int *type)
+static struct page *ocfs2_fault(struct vm_area_struct *vma,
+					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, address=%lu)\n", vma, fdata->address);
 
 	ret = ocfs2_vm_op_block_sigs(&blocked, &oldset);
 	if (ret < 0) {
 		mlog_errno(ret);
+		fdata->type = VM_FAULT_SIGBUS;
 		goto out;
 	}
 
-	page = filemap_nopage(area, address, type);
+	page = filemap_fault(vma, fdata);
 
 	ret = ocfs2_vm_op_unblock_sigs(&oldset);
 	if (ret < 0)
@@ -147,7 +146,7 @@ out:
 }
 
 static struct vm_operations_struct ocfs2_file_vm_ops = {
-	.nopage		= ocfs2_nopage,
+	.fault		= ocfs2_fault,
 	.page_mkwrite	= ocfs2_page_mkwrite,
 };
 
@@ -155,7 +154,7 @@ int ocfs2_mmap(struct file *file, struct
 {
 	file_accessed(file);
 	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
Index: linux-2.6/fs/xfs/linux-2.6/xfs_file.c
===================================================================
@@ -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_dentry->d_inode;
+	struct inode	*inode = vma->vm_file->f_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), area, 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_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
Index: linux-2.6/mm/mmap.c
===================================================================
@@ -1148,12 +1148,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
Index: linux-2.6/ipc/shm.c
===================================================================
@@ -260,7 +260,7 @@ static struct file_operations shm_file_o
 static 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
Index: linux-2.6/mm/nommu.c
===================================================================
@@ -1299,8 +1299,7 @@ int in_gate_area_no_task(unsigned long a
 	return 0;
 }
 
-struct page *filemap_nopage(struct vm_area_struct *area,
-			unsigned long address, int *type)
+struct page *filemap_fault(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
Index: linux-2.6/mm/truncate.c
===================================================================
@@ -53,7 +53,7 @@ static inline void truncate_partial_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/afs/file.c +2 -3
Index: linux-2.6/fs/afs/file.c
===================================================================
@@ -63,8 +63,7 @@ const struct address_space_operations af
 };
 
 static struct vm_operations_struct afs_fs_vm_operations = {
-	.nopage		= filemap_nopage,
-	.populate	= filemap_populate,
+	.fault		= filemap_fault,
 #ifdef CONFIG_AFS_FSCACHE
 	.page_mkwrite	= afs_file_page_mkwrite,
 #endif
@@ -82,7 +81,7 @@ static int afs_file_mmap(struct file *fi
 
 	file_accessed(file);
 	vma->vm_ops = &afs_fs_vm_operations;
-	vma->vm_flags |= VM_CAN_INVALIDATE;
+	vma->vm_flags |= VM_CAN_INVALIDATE | VM_CAN_NONLINEAR;
 	return 0;
 }
 
fs/gfs2/ops_address.c +1 -1
Index: linux-2.6/fs/gfs2/ops_address.c
===================================================================
@@ -227,7 +227,7 @@ static int gfs2_readpage(struct file *fi
 		if (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|GL_AOP, &gh);
fs/ncpfs/mmap.c +12 -11
Index: linux-2.6/fs/ncpfs/mmap.c
===================================================================
@@ -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_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/nfs/fscache.c +1 -2
Index: linux-2.6/fs/nfs/fscache.c
===================================================================
@@ -293,8 +293,7 @@ static int nfs_file_page_mkwrite(struct 
 }
 
 struct vm_operations_struct nfs_fs_vm_operations = {
-	.nopage		= filemap_nopage,
-	.populate	= filemap_populate,
+	.fault		= filemap_fault,
 	.page_mkwrite	= nfs_file_page_mkwrite,
 };
 
fs/nfs/fscache.h +1 -1
Index: linux-2.6/fs/nfs/fscache.h
===================================================================
@@ -200,7 +200,7 @@ static inline void nfs_fscache_install_v
 {
 	if (NFS_I(inode)->fscache) {
 		vma->vm_ops = &nfs_fs_vm_operations;
-		vma->vm_flags |= VM_CAN_INVALIDATE;
+		vma->vm_flags |= VM_CAN_INVALIDATE | VM_CAN_NONLINEAR;
 	}
 }
 
fs/ocfs2/aops.c +1 -1
Index: linux-2.6/fs/ocfs2/aops.c
===================================================================
@@ -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
Index: linux-2.6/Documentation/feature-removal-schedule.txt
===================================================================
@@ -203,6 +203,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 2008, 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

--
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>

#31 lore raw

PATCH 4/5 mm: add vm_insert_pfn helpler

In reply to Nick Piggin

Add 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
Index: linux-2.6/include/linux/mm.h
===================================================================
@@ -1121,6 +1121,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>

#32 lore raw

Re: [patch 4/5] mm: add vm_insert_pfn helpler

Quoted 28 lines · Nick Piggin

Add a vm_insert_pfn helper, so that ->fault handlers can have nopfn functionality by installing their own pte and returning NULL.

Index: linux-2.6/include/linux/mm.h
===================================================================

include/linux/mm.h +1 -0
@@ -1121,6 +1121,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);

Index: linux-2.6/mm/memory.c
===================================================================

mm/memory.c +3 -0
@@ -1267,6 +1267,50 @@ int vm_insert_page(struct vm_area_struct
 }
 EXPORT_SYMBOL(vm_insert_page);
 
+/**
+ * vm_insert_pfn - insert single pfn into user vma
+ * @vma: user vma to map to

mm/memory.c int vm_insert_page(struct vm_area_struct · line 1282

Quoted 10 lines
mm/memory.c +10 -0
+ * @addr: target user address of this page
+ * @pfn: source kernel pfn
+ *
+ * Similar to vm_inert_page, this allows drivers to insert individual pages
+ * they've allocated into a user vma. Same comments apply.
+ *
+ * This function should only be called from a vm_ops->fault handler, and
+ * in that case the handler should return NULL.
+ */
+int vm_insert_pfn(struct vm_area_struct *vma, unsigned long addr, unsigned long pfn)

Nick, I just realized: would it be possible to have a pgprot_t argument to this one, instead of it using vma->vm_pgprot?

The motivation for this (DRM again) is that some architectures (powerpc) cannot map the AGP aperture through IO space, but needs to remap the page from memory with a nocache attribute set. Others need special pgprot settings for write-combined mappings.

Now, there's a possibility to change vma->vm_pgprot during the first ->fault(), but again, we only have the mmap_sem in read mode.

/Thomas

#33 lore raw

Re: [patch 4/5] mm: add vm_insert_pfn helpler

Quoted 10 lines · Thomas Hellstrom

Nick, I just realized: would it be possible to have a pgprot_t argument to this one, instead of it using vma->vm_pgprot?

The motivation for this (DRM again) is that some architectures (powerpc) cannot map the AGP aperture through IO space, but needs to remap the page from memory with a nocache attribute set. Others need special pgprot settings for write-combined mappings.

Now, there's a possibility to change vma->vm_pgprot during the first ->fault(), but again, we only have the mmap_sem in read mode.

I don't see a problem with that. It would be nice if vm_pgprot could be kept in synch with the pte protections, but I guess a crazy driver should be allowed to do anything it wants ;)

#34 lore raw

Re: [patch 4/5] mm: add vm_insert_pfn helpler

Quoted 7 lines · Nick Piggin
Quoted 4 lines · Thomas Hellstrom

Nick, I just realized: would it be possible to have a pgprot_t argument to this one, instead of it using vma->vm_pgprot?

The motivation for this (DRM again) is that some architectures (powerpc) cannot map the AGP aperture through IO space, but needs to remap the page from memory with a nocache attribute set. Others need special pgprot settings for write-combined mappings.

Quoted 12 lines
Quoted 4 lines

Now, there's a possibility to change vma->vm_pgprot during the first ->fault(), but again, we only have the mmap_sem in read mode.

I don't see a problem with that. It would be nice if vm_pgprot could be kept in synch with the pte protections, but I guess a crazy driver should be allowed to do anything it wants ;)

:).
Actually, the caching bits are sort of left out from the mm code anyway. For example, mprotect will reset them, which is sort of a security risc, since an unpriviliged user can call mprotect on uncached page mappings, causing inconsistent mappings and stability problems.

Thomas

#35 lore raw

PATCH 5/5 mm: merge nopfn with fault handler

In reply to Nick Piggin

Remove ->nopfn and reimplement the only existing handler using ->fault

drivers/char/mspec.c +19 -10
Index: linux-2.6/drivers/char/mspec.c
===================================================================
@@ -178,24 +178,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) {
@@ -215,13 +216,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
Index: linux-2.6/include/linux/mm.h
===================================================================
@@ -223,7 +223,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
@@ -635,12 +634,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>

#36 lore raw
In reply to Nick Piggin

Arh, bad subject: should be 2.6.19-rc1-mm1

Quoted 4 lines · Nick Piggin

Has passed an allyesconfig on G5, and booted and stress tested (on ext3 and tmpfs) on 2x P4 Xeon with my userspace tester (which I will send in a reply to this email). (stress tests run with the set_page_dirty_buffers fix that is upstream).

Anyway, here is the pagefault vs invalidate/truncate race finder. It can trigger the bug introduced in patch 1/5 for both linear and nonlinear mappings. After the patchset, I cannot reproduce.
--

#define _XOPEN_SOURCE 600
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>

#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <errno.h>
#include <string.h>
#include <stdio.h>

#define max(x, y) ((x) > (y) ? (x) : (y))

/* following 4 parameters: (1, 1, 7, 1) gives nonlinear bug */ /* (1, 0, 7, 1) for linear bug */
static int invalidate = 0;
static int nonlinear = 0;
static int faulters = 7;
static int samepage = 1;

#define O_DIRECT 00040000

#define FNAME "dnp-inv.dat"
static int PAGE_SIZE;

static void error(const char *str)

{
	perror(str);
	exit(EXIT_FAILURE);
}

static void oom(void)

{
	fprintf(stderr, "Out of memory\n");
	exit(EXIT_FAILURE);
}

static sigjmp_buf SIGBUS_env;
static void SIGBUS_handler(int sig)

{
	siglongjmp(SIGBUS_env, 1);
}

static void dnp_child(int nr, int fd)

{
	time_t start;
	int i;
	struct sigaction sa = { .sa_handler = &SIGBUS_handler };
	if (sigemptyset(&sa.sa_mask) == -1)
		error("sigemptyset");
	if (sigaction(SIGBUS, &sa, NULL) == -1)
		error("sigaction");
 
	if (ftruncate(fd, PAGE_SIZE*faulters) == -1)
		error("ftruncate");

	i = 0;
	for (;;) {
		volatile long *lock;
		char *mem;

		if (i > 100) {
			i = 0;
			printf(".");
			fflush(stdout);
		}

		start = time(NULL);

		mem = mmap(NULL, PAGE_SIZE, PROT_READ|PROT_WRITE,
						MAP_SHARED, fd, (samepage && !nonlinear) ? 0 : PAGE_SIZE*nr);
		if (mem == MAP_FAILED)
			error("mmap");

		if (nonlinear) {
			if (remap_file_pages(mem, PAGE_SIZE, 0, samepage ? 0 : faulters-nr, 0) == -1)
				goto next;
		}
		lock = mem;
		lock += nr;

		if (!sigsetjmp(SIGBUS_env, 1)) {
			static int stuck = 0;
			int l;
			*lock = 1;

			for (l = 0; *lock; l++) {
				if (!stuck) {
					time_t delta;
					delta = time(NULL) - start;
					if (delta > 10) {
						stuck = 1;
						fprintf(stderr, "page stuck\n");
						exit(EXIT_FAILURE);
					}
				}
				if (l > 1000)	
					usleep(1);
			}
			if (stuck)
				fprintf(stderr, "page unstuck\n");

			i++;
		}

next:

		if (munmap(mem, PAGE_SIZE) == -1)
			error("munmap");
	}
}

static void trunc_child(int fd)

{
	int k;
	char buf = 0;

	for (k = 0;; k++) {
		int i;
		int err;

		if (k > 1000) {
			k = 0;
			printf("+");
			usleep(1*1000*1000);
			fflush(stdout);
		}

		if (ftruncate(fd, 0) == -1)
			error("ftruncate");
		if (ftruncate(fd, PAGE_SIZE*faulters) == -1)
			error("ftruncate");

		for (i = 0; i < (samepage?1:faulters); i++) {
			time_t start = time(NULL);
			do {
				time_t delta;
				err = pwrite(fd, &buf, 1, PAGE_SIZE*i);

				delta = time(NULL) - start;
				if (delta > 10) {
					fprintf(stderr, "write stuck\n");
					break;
				}
			} while (err == -1 /* && errno == EINTR */);
			if (err == -1)
				error("write");
			if (err != 1)
				fprintf(stderr, "Partial write? %d\n", err), exit(EXIT_FAILURE);
		}

	}
}

static void inv_child(int fd)

{
	int k;
	char *buf;
	int flags;

	if (posix_memalign(&buf, PAGE_SIZE, PAGE_SIZE) != 0)
		oom();

	if (ftruncate(fd, PAGE_SIZE*faulters) == -1)
		error("ftruncate");

	flags = fcntl(fd, F_GETFL);
	if (flags == -1)
		error("fcntl");
	if (fcntl(fd, F_SETFL, flags|O_DIRECT) == -1)
		error("fcntl");

	memset(buf, 0, PAGE_SIZE);

	for (k = 0;; k++) {
		int i;
		int err;

		if (k > 100) {
			k = 0;
			printf("+");
			usleep(1*1000*1000);
			fflush(stdout);
		}

		for (i = 0; i < (samepage?1:faulters); i++) {
			time_t start = time(NULL);
			do {
				time_t delta;

				err = pwrite(fd, buf, PAGE_SIZE, PAGE_SIZE*i);

				delta = time(NULL) - start;

				if (delta > 10) {
					fprintf(stderr, "write stuck\n");
					break;
				}
			} while (err == -1 /* && errno == EINTR */);
			if (err == -1)
				error("write");
			if (err != PAGE_SIZE)
				fprintf(stderr, "Partial write? %d\n", err), exit(EXIT_FAILURE);
		}
	}
}

int main(void)

{
	int i;
	int fd, err;

	PAGE_SIZE = getpagesize();

	fd = open(FNAME, O_RDWR|O_CREAT|O_TRUNC, S_IRUSR|S_IWUSR);
	if (fd == -1)
		error("open");

	for (i = 0; i < faulters; i++) {
		err = fork();
		if (err == -1)
			error("fork");
		if (!err) {
			//nice(20);
			dnp_child(i, fd);
			exit(EXIT_SUCCESS);
		}
	}
#if 1
	if (invalidate)
		inv_child(fd);
	else
		trunc_child(fd);
#endif
	sleep(10);

	if (close(fd) == -1)
		error("close");

	exit(EXIT_SUCCESS);
}

--
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>

#37 lore raw
In reply to Nick Piggin
Quoted 11 lines · Nick Piggin

This patchset is against 2.6.19-rc1-mm1 up to numa-add-zone_to_nid-function-swap_prefetch.patch (ie. no readahead stuff, which causes big rejects and would be much easier to fix in readahead patches than here). Other than this feature, the -mm specific stuff is pretty simple (mainly straightforward filesystem conversions).

Changes since last round:
- trimmed the cc list, no big changes since last time.
- fix the few buglets preventing it from actually booting
- reinstate filemap_nopage and filemap_populate, because they're exported symbols even though no longer used in the tree. Schedule for removal.

Just kill them and the whole ->populate methods. We have a better API that replaces them 100% with your patch, and they've never been a widespread API.

--
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>

#38 lore raw
Quoted 13 lines · Christoph Hellwig
Quoted 11 lines · Nick Piggin

This patchset is against 2.6.19-rc1-mm1 up to numa-add-zone_to_nid-function-swap_prefetch.patch (ie. no readahead stuff, which causes big rejects and would be much easier to fix in readahead patches than here). Other than this feature, the -mm specific stuff is pretty simple (mainly straightforward filesystem conversions).

Changes since last round:
- trimmed the cc list, no big changes since last time.
- fix the few buglets preventing it from actually booting
- reinstate filemap_nopage and filemap_populate, because they're exported symbols even though no longer used in the tree. Schedule for removal.

Quoted 3 lines

Just kill them and the whole ->populate methods. We have a better API that replaces them 100% with your patch, and they've never been a widespread API.

OK, this is what the patch looks like. I'm happy for .nopage to stay around for a while longer, because it costs us exactly 12 lines of code in mm/memory.c

Andrew?

--
Remove legacy filemap_nopage and all of the .populate API cruft.

 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 +1 -8
Index: linux-2.6/include/linux/mm.h
===================================================================
@@ -223,8 +223,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);
@@ -742,8 +740,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,
@@ -1053,10 +1049,7 @@ 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 * __deprecated filemap_fault(struct vm_area_struct *, struct fault_data *);
-extern struct page * __deprecated 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 *);
 
 /* 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
Index: linux-2.6/mm/memory.c
===================================================================
@@ -2327,18 +2327,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
@@ -2349,17 +2341,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.
 		 */
@@ -2369,18 +2363,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);
 }
 
 /*
@@ -2416,7 +2400,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 +1 -19
Index: linux-2.6/Documentation/feature-removal-schedule.txt
===================================================================
@@ -203,26 +203,8 @@ 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 2008, provided in-kernel callers have been converted
+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.

--
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>

#39 lore raw

Documentation/feature-removal-schedule.txt Who: Nick Piggin <[email protected]> · line 210

Quoted 6 lines

\ What: vm_ops.nopage

Documentation/feature-removal-schedule.txt +1 -1
-When:	October 2008, provided in-kernel callers have been converted
+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.

but a year is a really long time; 6 months would be a lot more reasonable..
(it's not as if most external modules will switch until it's really gone.. more notice isn't really going to help that at all; at least make the kernel printk once on the first use of this so that they notice!)

--
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>

#40 lore raw

Re: SPAM: Re: [rfc] 2.6.19-rc1-git5: consolidation of file backed fault handlers

Quoted 4 lines · Arjan van de Ven
Quoted 4 lines

\ What: vm_ops.nopage

Documentation/feature-removal-schedule.txt +1 -1
-When:	October 2008, provided in-kernel callers have been converted
+When:	October 2007, provided in-kernel callers have been converted
 Why:	This interface is replaced by vm_ops.fault, but it has been around
Quoted 8 lines

Documentation/feature-removal-schedule.txt Who: Nick Piggin <[email protected]> · line 210

Quoted 2 lines
Documentation/feature-removal-schedule.txt +0 -0
 	forever, is used by a lot of drivers, and doesn't cost much to
 	maintain.

but a year is a really long time; 6 months would be a lot more reasonable..
(it's not as if most external modules will switch until it's really gone.. more notice isn't really going to help that at all; at least make the kernel printk once on the first use of this so that they notice!)

I agree with that. But the printk can't go in until all the in-tree users are converted. I will get around to doing that once the interface is firmer.

As for timeframe, I don't have any strong feelings, but 6 months might only be 1 kernel release, and we may not have got around to putting the printk in yet ;)

--
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>

#41 lore raw
In reply to Christoph Hellwig
Quoted 13 lines · Christoph Hellwig
Quoted 11 lines · Nick Piggin

This patchset is against 2.6.19-rc1-mm1 up to numa-add-zone_to_nid-function-swap_prefetch.patch (ie. no readahead stuff, which causes big rejects and would be much easier to fix in readahead patches than here). Other than this feature, the -mm specific stuff is pretty simple (mainly straightforward filesystem conversions).

Changes since last round:
- trimmed the cc list, no big changes since last time.
- fix the few buglets preventing it from actually booting
- reinstate filemap_nopage and filemap_populate, because they're exported symbols even though no longer used in the tree. Schedule for removal.

Quoted 3 lines

Just kill them and the whole ->populate methods. We have a better API that replaces them 100% with your patch, and they've never been a widespread API.

concur; just nuke the parts that have become unused right away. They're not "removed" as such, but "replaced by better"

--
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>