mm: fix PageUptodate data race
Subsystems (4)
PATCH 1/1 mm: fix PageUptodate data race
After running SetPageUptodate, preceeding stores to the page contents to actually bring it uptodate may not be ordered with the store to set the page uptodate.
Therefore, another CPU which checks PageUptodate is true, then reads the page contents can get stale data.
Fix this by having an smp_wmb before SetPageUptodate, and smp_rmb after PageUptodate.
Many places that test PageUptodate, do so with the page locked, and this would be enough to ensure memory ordering in those places if SetPageUptodate were only called while the page is locked. Unfortunately that is not always the case for some filesystems, but it could be an idea for the future.
Also bring the handling of anonymous page uptodateness in line with that of file backed page management, by marking anon pages as uptodate when they _are_ uptodate, rather than when our implementation requires that they be marked as such. Doing allows us to get rid of the smp_wmb's in the page copying functions, which were especially added for anonymous pages for an analogous memory ordering problem. Both file and anonymous pages are handled with the same barriers.
FAQ:
Q. Why not do this in flush_dcache_page?
A. Firstly, flush_dcache_page handles only one side (the smb side) of the ordering protocol; we'd still need smp_rmb somewhere. Secondly, hiding away memory barriers in a completely unrelated function is nasty; at least in the PageUptodate macros, they are located together with (half) the operations involved in the ordering. Thirdly, the smp_wmb is only required when first bringing the page uptodate, wheras flush_dcache_page should be called each time it is written to through the kernel mapping. It is logically the wrong place to put it.
Q. Why does this increase my text size / reduce my performance / etc. A. Because it is adding the necessary instructions to eliminate the data-race.
Q. Can it be improved?
A. Yes, eg. if you were to create a rule that all SetPageUptodate operations run under the page lock, we could avoid the smp_rmb places where PageUptodate is queried under the page lock. Requires audit of all filesystems and at least some would need reworking. That's great you're interested, I'm eagerly awaiting your patches.
---
include/linux/highmem.h +0 -4
@@ -68,8 +68,6 @@ static inline void clear_user_highpage(svoid *addr =kmap_atomic (page,KM_USER0 );clear_user_page (addr, vaddr, page);kunmap_atomic (addr,KM_USER0 ); -/* Make sure this page is cleared on other CPU's too before using it */ -smp_wmb (); }#ifndef __HAVE_ARCH_ALLOC_ZEROED_USER_HIGHPAGE
@@ -160,8 +158,6 @@ static inline void copy_user_highpage(stcopy_user_page (vto, vfrom, vaddr, to);kunmap_atomic (vfrom,KM_USER0 ); kunmap_atomic(vto, KM_USER1); -/* Make sure this page is cleared on other CPU's too before using it */ - smp_wmb(); }#endif
include/linux/page-flags.h +39 -3
55 lines not on this page · raw message
mm/hugetlb.c +2 -0
@@ -808,6 +808,7 @@ static int hugetlb_cow(struct mm_structspin_unlock (&mm->page_table_lock);copy_huge_page (new_page, old_page, address, vma); +__SetPageUptodate (new_page);spin_lock (&mm->page_table_lock); ptep =huge_pte_offset (mm, address &HPAGE_MASK );
@@ -853,6 +854,7 @@ retry: goto out; }clear_huge_page (page, address); +__SetPageUptodate (page);if (vma->vm_flags &VM_SHARED ) {int err;
mm/memory.c +5 -4
@@ -1518,10 +1518,8 @@ static inline void cow_user_page(structmemset (kaddr,0 ,PAGE_SIZE );kunmap_atomic (kaddr,KM_USER0 );flush_dcache_page (dst); -return ; - - } -copy_user_highpage (dst, src, va, vma); + }else +copy_user_highpage (dst, src, va, vma); }/*
@@ -1630,6 +1628,7 @@ gotten:if (!new_page) goto oom;cow_user_page (new_page, old_page, address, vma); +__SetPageUptodate (new_page);/* * Re-check the pte - we dropped the lock
@@ -2162,6 +2161,7 @@ static int do_anonymous_page(struct mm_s page =alloc_zeroed_user_highpage_movable (vma, address);if (!page) goto oom; +__SetPageUptodate (page); entry =mk_pte (page, vma->vm_page_prot); entry =maybe_mkwrite (pte_mkdirty (entry), vma);
@@ -2262,6 +2262,7 @@ static int __do_fault(struct mm_struct * goto out; }copy_user_highpage (page, vmf.page, address, vma); +__SetPageUptodate (page); }else {/* * If the page will be shareable, see if the backing
mm/page_io.c +1 -1
@@ -126,7 +126,7 @@ int swap_readpage(struct file *file, strint ret =0 ;BUG_ON (!PageLocked (page)); -ClearPageUptodate (page); +BUG_ON (PageUptodate (page)); bio =get_swap_bio (GFP_KERNEL ,page_private (page), page, end_swap_bio_read);if (bio ==NULL ) {
mm/swap_state.c +1 -1
@@ -152,6 +152,7 @@ int add_to_swap(struct page * page, gfp_int err;BUG_ON (!PageLocked (page)); +BUG_ON (!PageUptodate (page));for (;;) { entry =get_swap_page ();
@@ -174,7 +175,6 @@ int add_to_swap(struct page * page, gfp_switch (err) {case 0 :/* Success */ -SetPageUptodate (page);SetPageDirty (page);INC_CACHE_INFO (add_total);return 1 ;
--
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>
Andrew Morton
maintainerQuoted 17 lines · On Tue, 22 Jan 2008 05:01:14 +0100 Nick Piggin <[email protected]> wrote:
After running SetPageUptodate, preceeding stores to the page contents to actually bring it uptodate may not be ordered with the store to set the page uptodate.
Therefore, another CPU which checks PageUptodate is true, then reads the page contents can get stale data.
Fix this by having an
smp_wmbbefore SetPageUptodate, andsmp_rmbafter PageUptodate.Many places that test PageUptodate, do so with the page locked, and this would be enough to ensure memory ordering in those places if SetPageUptodate were only called while the page is locked. Unfortunately that is not always the case for some filesystems, but it could be an idea for the future.
Quoted 8 lines
Also bring the handling of anonymous page uptodateness in line with that of file backed page management, by marking anon pages as uptodate when they _are_ uptodate, rather than when our implementation requires that they be marked as such. Doing allows us to get rid of the smp_wmb's in the page copying functions, which were especially added for anonymous pages for an analogous memory ordering problem. Both file and anonymous pages are handled with the same barriers.
So... it's two patches in one.
What kernel is this against? Looks like mainline. Is it complete and correct when applied against the large number of pending MM changes?
--
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>
Sorry, way behind on email here. I'll get through it slowly...
Quoted 26 lines · Andrew Morton
Quoted 17 lines · On Tue, 22 Jan 2008 05:01:14 +0100 Nick Piggin <[email protected]> wrote:
After running SetPageUptodate, preceeding stores to the page contents to actually bring it uptodate may not be ordered with the store to set the page uptodate.
Therefore, another CPU which checks PageUptodate is true, then reads the page contents can get stale data.
Fix this by having an
smp_wmbbefore SetPageUptodate, andsmp_rmbafter PageUptodate.Many places that test PageUptodate, do so with the page locked, and this would be enough to ensure memory ordering in those places if SetPageUptodate were only called while the page is locked. Unfortunately that is not always the case for some filesystems, but it could be an idea for the future.
Quoted 8 lines
Also bring the handling of anonymous page uptodateness in line with that of file backed page management, by marking anon pages as uptodate when they _are_ uptodate, rather than when our implementation requires that they be marked as such. Doing allows us to get rid of the smp_wmb's in the page copying functions, which were especially added for anonymous pages for an analogous memory ordering problem. Both file and anonymous pages are handled with the same barriers.
Quoted 1 line
So... it's two patches in one.
I guess so. Hmm, at least I appreciate it (them) getting testing in -mm for now. I guess I should break it in two, do you agree Hugh? Do you like/dislike the anonymous page change?
Quoted 2 lines
What kernel is this against? Looks like mainline. Is it complete and correct when applied against the large number of pending MM changes?
Uh, I forget. But luckily this one should be quite correct reglardless of pending mm changes... unless something there has fundamentally changed the semantics or locking of PG_uptodate... which wouldn't be too surprising actually ;)
No, it should be OK. I'll double check when I look at resubmitting it as 2 patches.
--
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>
Quoted 1 line · Nick Piggin
Sorry, way behind on email here. I'll get through it slowly...
You're certainly not the only one, and certainly not the worst offender.
Quoted 6 lines · Andrew Morton
Quoted 2 lines
So... it's two patches in one.
I guess so. Hmm, at least I appreciate it (them) getting testing in -mm for now. I guess I should break it in two, do you agree Hugh?
I do agree: I recommended that same split on a previous occasion. Actually, it's three patches: you seem to prefer one style over another in cow_user_page, I don't care either way, go ahead and make the change, but it's nothing to do with the rest of it.
Quoted 1 line
Do you like/dislike the anonymous page change?
Like would be a little strong, but I certainly don't dislike:
you're right that the current old-established way is somewhat contingent, do go ahead and make these Uptodates more consistent.
Quoted 7 lines
Quoted 2 lines
What kernel is this against? Looks like mainline. Is it complete and correct when applied against the large number of pending MM changes?
Uh, I forget. But luckily this one should be quite correct reglardless of pending mm changes... unless something there has fundamentally changed the semantics or locking of
PG_uptodate... which wouldn't be too surprising actually ;)
So far as I could tell, it's correct on top of -rc8-mm1 despite the fuzz - unless we've added more places where a mod is needed, but I don't think so.
I'm testing with that now, mainly because an earlier version hit a BUG or WARNing with shmem: I wanted to check that no longer happens. It doesn't, but then I got curious why not: I believe the problem line was shmem_getpage's
(!PageUptodate(filepage) || TestSetPageLocked(filepage)))
which gave trouble when you had a PageLocked check inside PageUptodate. You don't have that now because you're not at this point trying to push the PageLocked shortcuts; but worth noting if you resurrect those later.
I do like this version _so_ much more than your earlier attempts to avoid the overhead wherever you could argue it. Those might become more acceptable once we've grown accustomed to this initial set.
Thanks for doing the FAQs: I think in the meantime I'd already persuaded myself that you're right that PageUptodate is the proper place for this stuff, even if I regret the complication.
Four little points. In the comments, "preceding" not "preceeding" and I presume "wmb" not "smb". You've carried over several "(page)"s from the macros which would better be "page"s in inline functions.
And do we really need that smp_wmb in __SetPageUptodate? It seems to me that when you're in a position to use __SetPageUptodate, the page cannot yet be visible, and the necessary barrier will be provided later when the page is made visible.
I suspect the answer is that I'm confusing two different kinds of visibility; and that although it's true that we don't actually need that smp_wmb in the places where __SetPageUptodate is being used, it'd be hard to write and remember the rules to justify its removal. But mention it because you may well see this more clearly.
Hugh
--
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>