Skip to contentSkip to conversation

2.6.19 file content corruption on ext3

PATCH NAK Andrei Popa 43 participants 311 messages Dec 17, 2006 – Jan 7, 2007 3 messages not in the archive Page 1 of 2

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

Hello,
I had filesystem data corruption with rtorrent with 2.6.19. I tried recent git with Peter Zijlstra patch
http://lkml.org/lkml/2006/12/16/144 and it seems that the problem is fixed.

Please CC as I am not subscribed to lkml.

Andrei

#1 lore raw
Quoted 6 lines · On Sun, 17 Dec 2006 02:13:18 +0200 Andrei Popa <[email protected]> wrote:

Hello,
I had filesystem data corruption with rtorrent with 2.6.19. I tried recent git with Peter Zijlstra patch
http://lkml.org/lkml/2006/12/16/144 and it seems that the problem is fixed.

oh crap, I'd forgotten that test_clear_page_dirty() now fiddles with the ptes.

I'd be really surprised if this was all due to a race though. Is everyone who has observed this problem running SMP and/or premptible kernels?

Peter, why isn't that proposed patch's cleaning of the pte racy against do_wp_page()?

#2 lore raw
Quoted 2 lines · Andrew Morton

I'd be really surprised if this was all due to a race though. Is everyone who has observed this problem running SMP and/or premptible kernels?

Linux torres 2.6.19.1-zgsrv #1 SMP PREEMPT Wed Dec 13 01:31:27 UTC 2006 i686 GNU/Linux

So, it's a "yes" to both counts, and I'll build a kernel without SMP and without preemption asap.

Greetings
Marc

Signature · 4 lines
-----------------------------------------------------------------------------
Marc Haber         | "I don't trust Computers. They | Mailadresse im Header
Mannheim, Germany  |  lose things."    Winona Ryder | Fon: *49 621 72739834
Nordisch by Nature |  How to make an American Quilt | Fax: *49 621 72739835
#3 lore raw
In reply to Andrew Morton

ierdnac ~ # uname -a
Linux ierdnac 2.6.20-rc1 #1 SMP PREEMPT Sun Dec 17 01:52:28 EET 2006
i686 Genuine Intel(R) CPU T2050 @ 1.60GHz GenuineIntel
GNU/Linux

Quoted 18 lines · Andrew Morton
Quoted 6 lines · On Sun, 17 Dec 2006 02:13:18 +0200 Andrei Popa <[email protected]> wrote:

Hello,
I had filesystem data corruption with rtorrent with 2.6.19. I tried recent git with Peter Zijlstra patch
http://lkml.org/lkml/2006/12/16/144 and it seems that the problem is fixed.

oh crap, I'd forgotten that test_clear_page_dirty() now fiddles with the ptes.

I'd be really surprised if this was all due to a race though. Is everyone who has observed this problem running SMP and/or premptible kernels?

Peter, why isn't that proposed patch's cleaning of the pte racy against do_wp_page()?

#4 lore raw
In reply to Andrew Morton

I was mistaken, I'm still having file corruption with rtorrent.

Quoted 18 lines · Andrew Morton
Quoted 6 lines · On Sun, 17 Dec 2006 02:13:18 +0200 Andrei Popa <[email protected]> wrote:

Hello,
I had filesystem data corruption with rtorrent with 2.6.19. I tried recent git with Peter Zijlstra patch
http://lkml.org/lkml/2006/12/16/144 and it seems that the problem is fixed.

oh crap, I'd forgotten that test_clear_page_dirty() now fiddles with the ptes.

I'd be really surprised if this was all due to a race though. Is everyone who has observed this problem running SMP and/or premptible kernels?

Peter, why isn't that proposed patch's cleaning of the pte racy against do_wp_page()?

#5 lore raw
Quoted 2 lines · On Sun, 17 Dec 2006 15:39:32 +0200 Andrei Popa <[email protected]> wrote:

I was mistaken, I'm still having file corruption with rtorrent.

Well I'm not very optimistic, but if people could try this, please...

From: Andrew Morton <[email protected]>

try_to_free_buffers() clears the page's dirty state if it successfully removed the page's buffers.

Background for this:

- a process does a one-byte-write to a file on a 64k pagesize, 4k blocksize ext3 filesystem. The page is now PageDirty, !PgeUptodate and has one dirty buffer and 15 not uptodate buffers.

- kjournald writes the dirty buffer. The page is now PageDirty, !PageUptodate and has a mix of clean and not uptodate buffers.

- try_to_free_buffers() removes the page's buffers. It MUST now clear PageDirty. If we were to leave the page dirty then we'd have a dirty, not uptodate page with no buffer_heads.

We're screwed: we cannot write the page because we don't know which sections of it contain garbage. We cannot read the page because we don't know which sections of it contain modified data. We cannot free the page because it is dirty.

Peter's "mm: tracking shared dirty pages" (d08b3851da41d0ee60851f2c75b118e1f7a5fc89) modified clear_page_dirty() so that it also clears the page's pte mapping's dirty flags, arranging for a subsequent userspace modification of the page to cause a fault.

That change to clear_page_dirty() was correct for when it is called on the writeback path. Here, we effectively do:

ClearPageDirty()
pte_mkclean()
submit-the-writeout

if a page-dirtying via write() or via pte's happens after the ClearPageDirty() or the pte_mkclean() then the page is redirtied while writeout is in flight and the page will again need writing; no probs.

But that change to clear_page_dirty() was incorrect for when it is called on the try_to_free_buffers() path. Here, we want to preserve any pte-dirtiness because we're not going to write the page to backing store. We need to keep a record of any userspace modification to the page.

One way of addressing this would be to bale from try_to_free_buffers() if the page is mapped into pagetables. However that is racy, because the pagefault path doesn't lock the page when establishing a pte against it (I which it did
- it would solve a lot of nasties).

So this patch instead arranges for clear_page_dirty() to not clean the pte's when it is called on the try_to_free_buffers() path.

clear_page_dirty() had several callers and it's not immediately obvious to me what the appropriate behaviour is in each case. Could maintainers please take a look?

Quoted 1 line

From my quick reading, all callers of try_to_free_buffers() have already

unmapped the page from pagetables, and given that the reported ext3 corruption happens on uniprocessor, non-preempt kernels, I doubt if this patch will fix things.

But even if it is true that try_to_free_buffers() callers unmap the page first, this fix is still needed, because a minor fault could reestablish pte's in the meanwhile.

Note that with this change, we can now restore try_to_free_buffers()'s ->private_lock to cover the test_clear_page_dirty(). If we indeed need to do that, it'll be in a separate patch.

(Need to think about this some more. How can a page be pte-dirty, but not have dirty buffers? We're supposed to clean the pte's when we write the page, and we dirty the page and buffers when userspace dirties the pte...)

Cc Miklos Szeredi <[email protected]>
Cc Dave Kleikamp <[email protected]>
Cc David Chinner <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Hugh Dickins <[email protected]>

---

 fs/buffer.c                 |    2 +-
 fs/cifs/file.c              |    2 +-
 fs/fuse/file.c              |    2 +-
 fs/hugetlbfs/inode.c        |    2 +-
 fs/jfs/jfs_metapage.c       |    2 +-
 fs/reiserfs/stree.c         |    2 +-
 fs/xfs/linux-2.6/xfs_aops.c |    2 +-
 include/linux/page-flags.h  |    6 +++---
 mm/page-writeback.c         |    5 +++--
 mm/truncate.c               |    4 ++--
 10 files changed, 15 insertions(+), 14 deletions(-)
fs/buffer.c renamed from fs/buffer.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/buffer.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/buffer.c
@@ -2858,7 +2858,7 @@ int try_to_free_buffers(struct page *pag
 		 * the page's buffers clean.  We discover that here and clean
 		 * the page also.
 		 */
-		if (test_clear_page_dirty(page))
+		if (test_clear_page_dirty(page, 0))
 			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	}
 out:
fs/fuse/file.c renamed from fs/fuse/file.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/fuse/file.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/fuse/file.c
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c renamed from fs/hugetlbfs/inode.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/hugetlbfs/inode.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/hugetlbfs/inode.c
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 1);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c renamed from fs/jfs/jfs_metapage.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/jfs/jfs_metapage.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/jfs/jfs_metapage.c
@@ -773,7 +773,7 @@ void release_metapage(struct metapage * 
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c renamed from fs/reiserfs/stree.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/reiserfs/stree.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/reiserfs/stree.c
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c renamed from fs/xfs/linux-2.6/xfs_aops.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/xfs/linux-2.6/xfs_aops.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/xfs/linux-2.6/xfs_aops.c
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h renamed from include/linux/page-flags.h~try_to_free_buffers-dont-clear-pte-dirty-bits +3 -3
diff -puN include/linux/page-flags.h~try_to_free_buffers-dont-clear-pte-dirty-bits include/linux/page-flags.h
@@ -253,13 +253,13 @@ static inline void SetPageUptodate(struc
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int must_clean_ptes)
 {
-	test_clear_page_dirty(page);
+	test_clear_page_dirty(page, must_clean_ptes);
 }
 
 static inline void set_page_writeback(struct page *page)
mm/page-writeback.c renamed from mm/page-writeback.c~try_to_free_buffers-dont-clear-pte-dirty-bits +3 -2
diff -puN mm/page-writeback.c~try_to_free_buffers-dont-clear-pte-dirty-bits mm/page-writeback.c
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory accounting. 
  * Returns true if the page was previously dirty.
  */
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
 {
 	struct address_space *mapping = page_mapping(page);
 	unsigned long flags;
@@ -866,7 +866,8 @@ int test_clear_page_dirty(struct page *p
 		 * page is locked, which pins the address_space
 		 */
 		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
+			if (must_clean_ptes)
+				page_mkclean(page);
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
 		return 1;
mm/truncate.c renamed from mm/truncate.c~try_to_free_buffers-dont-clear-pte-dirty-bits +2 -2
diff -puN mm/truncate.c~try_to_free_buffers-dont-clear-pte-dirty-bits mm/truncate.c
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 1))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
fs/cifs/file.c renamed from fs/cifs/file.c~try_to_free_buffers-dont-clear-pte-dirty-bits +1 -1
diff -puN fs/cifs/file.c~try_to_free_buffers-dont-clear-pte-dirty-bits fs/cifs/file.c
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 1)) {
 				unlock_page(page);
 				break;
 			}

_

#6 lore raw
Quoted 3 lines · Andrew Morton

So this patch instead arranges for clear_page_dirty() to not clean the pte's when it is called on the try_to_free_buffers() path.

No. This is wrong.

It's wrong exactly because it now _breaks_ the whole thing that the 2.6.19 PG_dirty changes were all about: keeping track of dirty pages. Now you have a page that is dirty, but it's no longer marked PG_dirty, and thus it doesn't participate in the dirty accounting.

Quoted 4 lines

From my quick reading, all callers of try_to_free_buffers() have already unmapped the page from pagetables, and given that the reported ext3 corruption happens on uniprocessor, non-preempt kernels, I doubt if this patch will fix things.

So not only are you breaking this, you also claim that it cannot happen in the first place. So either the patch is buggy, or it's pointless. In neither case does it seem to be a good idea to do.

Linus

#7 lore raw
In reply to Andrew Morton
Quoted 5 lines · Andrew Morton

From my quick reading, all callers of try_to_free_buffers() have already unmapped the page from pagetables, and given that the reported ext3 corruption happens on uniprocessor, non-preempt kernels, I doubt if this patch will fix things.

Hmm. One possible explanation: maybe the page actually _did_ get unmapped from the page tables, but got added back?

I don't think we lock the page when faulting it in (we want it to be uptodate, but not necessarily locked). So assuming the pageout sequence always _does_ follow the rule that it only does try_to_free_buffers() on pages that aren't mapped, what actually protects them from not becoming mapped (and dirtied) during that sequence?

So we should probably do a "wait_for_page()" in do_no_page()?

Or maybe only do it for write accesses (since we don't really care about getting mapped readably)? If so, we need to do it in the write case of do_no_page() _and_ in the do_wp_page() case. Hmm?

Linus

#8 lore raw
Quoted 6 lines · Linus Torvalds

So we should probably do a "wait_for_page()" in do_no_page()?

Or maybe only do it for write accesses (since we don't really care about getting mapped readably)? If so, we need to do it in the write case of do_no_page() _and_ in the do_wp_page() case. Hmm?

I think we discussed doing exactly this at some earlier time, actually, just to try to throttle people who do lots of page dirtying.

Maybe we even do it somewhere, but I tried to see it, and in the normal "nopage()" routine we very much try to _avoid_ locking the page (ie if it's marked PageUptodate() we'll return it whether locked or not). Which is fine - especially for readers, there really isn't any reason to ever delay them getting access to a page just because it's locked for write-out or something (once it's mapped, they'll have access to it regardless of any locked state in the kernel anyway).

So I don't actually see any serialization at all that would keep a random page from being paged back in.

Linus

#9 lore raw

[ Replying to myself - a sure sign that I don't get out enough ]

Quoted 3 lines · Linus Torvalds

So I don't actually see any serialization at all that would keep a random page from being paged back in.

We do actually serialize, but we do it _after_ the page has already been mapped back. Ie we do it for the dirty page case at rthe end of do_wp_page() and do_no_page() when we do the "set_page_dirty_balance()", but that's potentially too late - we've already mapped the page read-write into the address space.

That said, this means that only threaded apps should ever trigger any problems, which would seem to make it unlikely that this is the issue.

But Andrew: I don't think it's necessarily true that "try_to_free_buffers()" callers have all unmapped the page.

That seems to be true for vmscan.c (ie the shrink_page_list -> try_to_release_page -> try_to_release_buffers callchain), but what about the other callchains (through filesystems, or through "pagevec_strip()" or similar?) That pagevec_strip() is called from shrink_active_list(), I don't see that unmapping the pages..

Linus

#10 lore raw
Quoted 8 lines · Linus Torvalds

[ Replying to myself - a sure sign that I don't get out enough ]

Quoted 1 line · Linus Torvalds

So I don't actually see any serialization at all that would keep a random page from being paged back in.

We do actually serialize, but we do it _after_ the page has already been mapped back. Ie we do it for the dirty page case at rthe end of do_wp_page() and do_no_page() when we do the "set_page_dirty_balance()", but that's potentially too late - we've already mapped the page read-write into the address space.

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

Quoted 5 lines

That said, this means that only threaded apps should ever trigger any problems, which would seem to make it unlikely that this is the issue.

But Andrew: I don't think it's necessarily true that "try_to_free_buffers()" callers have all unmapped the page.

That seems to be true for vmscan.c (ie the shrink_page_list -> try_to_release_page -> try_to_release_buffers callchain), but what about the other callchains (through filesystems, or through "pagevec_strip()" or similar?) That pagevec_strip() is called from shrink_active_list(), I don't see that unmapping the pages..

Right. But would it really matter whether they are currently mapped or not, given that we agree it may become mapped at any point?

I think the problem Andrew identified is real.

The issue is the disconnect between the pte dirtiness and a filesystem bringing buffers clean. But I disagree with his fix, because we don't actually want to just throw out that pte dirtiness information: we're just trying to get the PG_dirty bit into synch with what the buffers are telling us, not actually clean or dirty anything, as such.

Can we clear the page dirty bit, then run set_page_dirty afterwards, if any dirty ptes are found?

The other thing we might be able to do is to skip doing the clear_page_dirty if the page is uptodate. This feels more hackish but it might be faster?

Signature · 2 lines
SUSE Labs, Novell Inc.
Send instant messages to your online friends http://au.messenger.yahoo.com 
#11 lore raw
Quoted 1 line · On Mon, 18 Dec 2006 15:51:52 +1100 Nick Piggin <[email protected]> wrote:

I think the problem Andrew identified is real.

I don't. In fact I don't think I described any problem (well, I tried to, but then I contradicted myself).

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

Quoted 2 lines

The issue is the disconnect between the pte dirtiness and a filesystem bringing buffers clean.

Really? The dirtying direction goes pte_dirty->PG_dirty->BH_Dirty and the cleaning direction goes !BH_Dirty->!PG_dirty->!pte_dirty. That's pretty simple, setting aside races.

In the try_to_free_buffers case there's a large time inverval between !BH_Dirty and !PG_dirty, but that shouldn't affect anything.

I don't think we even have a theory as to what's gone wrong yet.

#12 lore raw
Quoted 9 lines · Andrew Morton
Quoted 1 line · On Mon, 18 Dec 2006 15:51:52 +1100 Nick Piggin <[email protected]> wrote:

I think the problem Andrew identified is real.

I don't. In fact I don't think I described any problem (well, I tried to, but then I contradicted myself).

By saying that there shouldn't be any dirty ptes if there are no dirty buffers? But in that case the _page_ shouldn't be dirty either, so that clear_page_dirty would be redundant. But presumably it isn't.

Quoted 4 lines

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

Yes I could believe it the corruption is caused by something else completely.

Quoted 10 lines
Quoted 2 lines

The issue is the disconnect between the pte dirtiness and a filesystem bringing buffers clean.

Really? The dirtying direction goes pte_dirty->PG_dirty->BH_Dirty and the cleaning direction goes !BH_Dirty->!PG_dirty->!pte_dirty. That's pretty simple, setting aside races.

In the try_to_free_buffers case there's a large time inverval between !BH_Dirty and !PG_dirty, but that shouldn't affect anything.

After try_to_free_buffers detaches the buffers from the page, a pagefault can come in, and mark the pte writeable, then set_page_dirty (which finds no buffers, so only sets PG_dirty).

The page can now get dirtied through this mapping.

try_to_free_buffers then goes on to clean the page and ptes.

I really thought you were the one who identified this race, and I didn't see where you showed it is safe.

It may be very unlikely with small SMPs, but less so with preempt. All we have to do is preempt at spin_unlock in try_to_free_buffers AFAIKS. Were you testing with preempt?

Signature · 2 lines
SUSE Labs, Novell Inc.
Send instant messages to your online friends http://au.messenger.yahoo.com 
#13 lore raw
Quoted 8 lines · On Mon, 18 Dec 2006 18:22:42 +1100 Nick Piggin <[email protected]> wrote:
Quoted 7 lines · Andrew Morton
Quoted 1 line · On Mon, 18 Dec 2006 15:51:52 +1100 Nick Piggin <[email protected]> wrote:

I think the problem Andrew identified is real.

Quoted 6 lines
Quoted 2 lines

I don't. In fact I don't think I described any problem (well, I tried to, but then I contradicted myself).

By saying that there shouldn't be any dirty ptes if there are no dirty buffers? But in that case the _page_ shouldn't be dirty either, so that clear_page_dirty would be redundant. But presumably it isn't.

I don't follow that.

The linkage between pte-dirtiness and buffer_heads is a bit hard to follow without also considering page-dirtiness.

Quoted 7 lines
Quoted 4 lines

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

Yes I could believe it the corruption is caused by something else completely.

Think so. We do have a problem here, but only on threaded apps, I believe. rtorrent doesn't appear to be threaded, and the bug is hit on non-preempt UP.

Quoted 11 lines
Quoted 10 lines
Quoted 2 lines

The issue is the disconnect between the pte dirtiness and a filesystem bringing buffers clean.

Really? The dirtying direction goes pte_dirty->PG_dirty->BH_Dirty and the cleaning direction goes !BH_Dirty->!PG_dirty->!pte_dirty. That's pretty simple, setting aside races.

In the try_to_free_buffers case there's a large time inverval between !BH_Dirty and !PG_dirty, but that shouldn't affect anything.

Quoted 7 lines

After try_to_free_buffers detaches the buffers from the page, a pagefault can come in, and mark the pte writeable, then set_page_dirty (which finds no buffers, so only sets PG_dirty).

The page can now get dirtied through this mapping.

try_to_free_buffers then goes on to clean the page and ptes.

try_to_free_buffers() isn't called against a page which doesn't have buffers. It'll oops.

Quoted 1 line

Were you testing with preempt?

nope, just SMP.

#14 lore raw
Quoted 28 lines · Andrew Morton
Quoted 8 lines · On Mon, 18 Dec 2006 18:22:42 +1100 Nick Piggin <[email protected]> wrote:
Quoted 7 lines · Andrew Morton
Quoted 1 line · On Mon, 18 Dec 2006 15:51:52 +1100 Nick Piggin <[email protected]> wrote:

I think the problem Andrew identified is real.

Quoted 6 lines
Quoted 2 lines

I don't. In fact I don't think I described any problem (well, I tried to, but then I contradicted myself).

By saying that there shouldn't be any dirty ptes if there are no dirty buffers? But in that case the _page_ shouldn't be dirty either, so that clear_page_dirty would be redundant. But presumably it isn't.

I don't follow that.

The linkage between pte-dirtiness and buffer_heads is a bit hard to follow without also considering page-dirtiness.

Quoted 5 lines
Quoted 4 lines

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

Quoted 6 lines
Quoted 2 lines

Yes I could believe it the corruption is caused by something else completely.

Think so. We do have a problem here, but only on threaded apps, I believe. rtorrent doesn't appear to be threaded, and the bug is hit on non-preempt UP.

ierdnac ~ # uname -a
Linux ierdnac 2.6.20-rc1 #2 SMP PREEMPT Mon Dec 18 11:01:52 EET 2006
i686 Genuine Intel(R) CPU T2050 @ 1.60GHz GenuineIntel
GNU/Linux

and the other person who had corruption with rtorrent has also SMP and PREEMPT.

Quoted 27 lines
Quoted 12 lines
Quoted 11 lines
Quoted 3 lines

The issue is the disconnect between the pte dirtiness and a filesystem bringing buffers clean.

Really? The dirtying direction goes pte_dirty->PG_dirty->BH_Dirty and the cleaning direction goes !BH_Dirty->!PG_dirty->!pte_dirty. That's pretty simple, setting aside races.

In the try_to_free_buffers case there's a large time inverval between !BH_Dirty and !PG_dirty, but that shouldn't affect anything.

Quoted 7 lines

After try_to_free_buffers detaches the buffers from the page, a pagefault can come in, and mark the pte writeable, then set_page_dirty (which finds no buffers, so only sets PG_dirty).

The page can now get dirtied through this mapping.

try_to_free_buffers then goes on to clean the page and ptes.

try_to_free_buffers() isn't called against a page which doesn't have buffers. It'll oops.

Quoted 1 line

Were you testing with preempt?

nope, just SMP.

#15 lore raw
In reply to Andrew Morton
Quoted 10 lines · Andrew Morton
Quoted 2 lines · On Mon, 18 Dec 2006 18:22:42 +1100 Nick Piggin <[email protected]> wrote:

Yes I could believe it the corruption is caused by something else completely.

Think so. We do have a problem here, but only on threaded apps, I believe. rtorrent doesn't appear to be threaded, and the bug is hit on non-preempt UP.

I think (see below) that it does not apply only to threaded apps. But it would need one of SMP or PREEMPT to trigger.

Quoted 11 lines
Quoted 7 lines

After try_to_free_buffers detaches the buffers from the page, a pagefault can come in, and mark the pte writeable, then set_page_dirty (which finds no buffers, so only sets PG_dirty).

The page can now get dirtied through this mapping.

try_to_free_buffers then goes on to clean the page and ptes.

try_to_free_buffers() isn't called against a page which doesn't have buffers. It'll oops.

Sure. But I think the race exists... I'll try spelling it out in the conventional way:

try_to_free_buffers()
drop_buffers() (succeeds)

** preempt here or run right-hand thread on 2nd CPU in SMP **

do_no_page()
set_page_dirty()

[now modify the page via this mapping (from this process or a concurrent thread)]

clear_page_dirty() (clears PG_dirty + pte dirty, oops)

Signature · 2 lines
SUSE Labs, Novell Inc.
Send instant messages to your online friends http://au.messenger.yahoo.com 
#16 lore raw
In reply to Andrew Morton
Quoted 4 lines · Andrew Morton

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

I do not have a clue about memory management at all, but is it possible that you're testing on a box with too much memory? My box has only 256 MB, and I used to use mutt with a _huge_ inbox with mutt taking somewhat 150 MB. Add spamassassin and a reasonably busy mail server, and the box used to be like 150 MB in swap.

I have tidied my inbox in the mean time and mutt's memory requirement has been reduced to somewhat 30 MB, which might be the cause that I don't see the issue that often any more.

Greetings
Marc, just trying to give input

Signature · 4 lines
-----------------------------------------------------------------------------
Marc Haber         | "I don't trust Computers. They | Mailadresse im Header
Mannheim, Germany  |  lose things."    Winona Ryder | Fon: *49 621 72739834
Nordisch by Nature |  How to make an American Quilt | Fax: *49 621 72739835
#17 lore raw
Quoted 5 lines · Marc Haber

I do not have a clue about memory management at all, but is it possible that you're testing on a box with too much memory? My box has only 256 MB, and I used to use mutt with a _huge_ inbox with mutt taking somewhat 150 MB. Add spamassassin and a reasonably busy mail server, and the box used to be like 150 MB in swap.

FWIW, the ARM box I see this on has only 32 MB memory (and a 133 or 266 MHz CPU). I don't see it on another ARM box (different ARM sub-arch) with 128 MB memory and a 600 MHz CPU.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#18 lore raw
In reply to Marc Haber
Quoted 12 lines · Marc Haber
Quoted 4 lines · Andrew Morton

Six hours here of fsx-linux plus high memory pressure on SMP on 1k blocksize ext3, mainline. Zero failures. It's unlikely that this testing would pass, yet people running normal workloads are able to easily trigger failures. I suspect we're looking in the wrong place.

I do not have a clue about memory management at all, but is it possible that you're testing on a box with too much memory? My box has only 256 MB, and I used to use mutt with a _huge_ inbox with mutt taking somewhat 150 MB. Add spamassassin and a reasonably busy mail server, and the box used to be like 150 MB in swap.

Quoted 3 lines

I have tidied my inbox in the mean time and mutt's memory requirement has been reduced to somewhat 30 MB, which might be the cause that I don't see the issue that often any more.

After being up for ten days, I have now encountered the file corruption of pkgcache.bin for the first time again. The 256 MB i386 box is like 26M in swap, is under very moderate load.

I am running plain vanilla 2.6.19.1. Is there a patch that I should apply against 2.6.19.1 that would help in debugging?

Greetings
Marc

Signature · 4 lines
-----------------------------------------------------------------------------
Marc Haber         | "I don't trust Computers. They | Mailadresse im Header
Mannheim, Germany  |  lose things."    Winona Ryder | Fon: *49 621 72739834
Nordisch by Nature |  How to make an American Quilt | Fax: *49 621 72739835
#19 lore raw
Quoted 7 lines · Marc Haber

After being up for ten days, I have now encountered the file corruption of pkgcache.bin for the first time again. The 256 MB i386 box is like 26M in swap, is under very moderate load.

I am running plain vanilla 2.6.19.1. Is there a patch that I should apply against 2.6.19.1 that would help in debugging?

Not right now.

And I have a test-program that shows the corruption _much_ easier (at least according to my own testing, and that of several reporters that back me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

So it just got a lot _easier_ to trigger in 2.6.19, but it's not a new bug.

What we need now is actually looking at the source code, and people who understand the VM, I'm afraid. I'm gathering traces now that I have a good test-case. I'll post my trace tools once I've tested that they work, in case others want to help.

(And hey, you don't have to be a VM expert to help: this could be a learning experience. However, I'll warn you: this is _the_ most grotty part of the whole kernel. It's not even ugly, it's just damn hard and complex).

Linus

#20 lore raw
Quoted 4 lines · Linus Torvalds

And I have a test-program that shows the corruption _much_ easier (at least according to my own testing, and that of several reporters that back me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

#21 lore raw
Quoted 5 lines · Petri Kaukasoina
Quoted 2 lines

me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

I bet it's the same bug, and it's been around for ages.

Linus

#22 lore raw
Quoted 6 lines · Linus Torvalds
Quoted 3 lines · Petri Kaukasoina
Quoted 2 lines

me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

Quoted 8 lines
Quoted 2 lines

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

The only notable VM patch in Fedora kernels of that vintage that I recall was Ingo's 4g/4g thing.

Dave

Signature · 1 line
http://www.codemonkey.org.uk
#23 lore raw
Quoted 10 lines · Dave Jones
Quoted 9 lines · Linus Torvalds
Quoted 5 lines · Petri Kaukasoina
Quoted 2 lines

me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Quoted 8 lines
Quoted 5 lines

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

The only notable VM patch in Fedora kernels of that vintage that I recall was Ingo's 4g/4g thing.

which does tlb flushes *all the time* so that even rules out (well almost) a stale tlb somewhere...

#24 lore raw
In reply to Dave Jones
Quoted 16 lines · Linus Torvalds
Quoted 14 lines · Petri Kaukasoina
Quoted 5 lines
Quoted 2 lines

me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

Quoted 4 lines

The only notable VM patch in Fedora kernels of that vintage that I recall was Ingo's 4g/4g thing.

Dave

no the fedora 2.6.18 kernel is affected.
it carries the same -mm patches that Debian backported for LSB 3.1 compliance.

Signature · 3 lines
maks

ps sorry for stripping cc, only downloaded that message raw.
#25 lore raw
Quoted 16 lines · maximilian attems
Quoted 16 lines · Linus Torvalds
Quoted 14 lines · Petri Kaukasoina
Quoted 5 lines
Quoted 2 lines

me up), and that seems to show the corruption going way way back (ie going back to Linux-2.6.5 at least, according to one tester).

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

Quoted 4 lines
Quoted 2 lines

The only notable VM patch in Fedora kernels of that vintage that I recall was Ingo's 4g/4g thing.

no the fedora 2.6.18 kernel is affected.

I wasn't denying that, but Linus was talking about a 2.6.5 Fedora kernel.

Quoted 2 lines

it carries the same -mm patches that Debian backported for LSB 3.1 compliance.

The only -mm stuff I recall being in the Fedora 2.6.18 is the inode-diet stuff which ended up in 2.6.19, though the xmas break has left my head somewhat empty so I may be forgetting something. What patch in particular are you talking about?

Dave

Signature · 1 line
http://www.codemonkey.org.uk
#26 lore raw
Quoted 2 lines · Dave Jones
Quoted 1 line · maximilian attems
Quoted 1 line · Linus Torvalds

<snipp>

Quoted 16 lines
Quoted 9 lines
Quoted 9 lines
Quoted 8 lines
Quoted 2 lines

That was a Fedora kernel. Has anyone seen the corruption in vanilla 2.6.18 (or older)?

Well, that was a really _old_ fedora kernel. I guarantee you it didn't have the page throttling patches in it, those were written this summer. So it would either have to be Fedora carrying around another patch that just happens to result in the same corruption for _years_, or it's the same bug.

Quoted 4 lines
Quoted 2 lines

The only notable VM patch in Fedora kernels of that vintage that I recall was Ingo's 4g/4g thing.

no the fedora 2.6.18 kernel is affected.

I wasn't denying that, but Linus was talking about a 2.6.5 Fedora kernel.

Quoted 7 lines
Quoted 2 lines

it carries the same -mm patches that Debian backported for LSB 3.1 compliance.

The only -mm stuff I recall being in the Fedora 2.6.18 is the inode-diet stuff which ended up in 2.6.19, though the xmas break has left my head somewhat empty so I may be forgetting something. What patch in particular are you talking about?

it's no longer visible in the FC6 cvs, due to rebase
but it's name was linux-2.6-mm-tracking-dirty-pages.patch
it is an earlier almagame of the merged patch serie:
- mm: tracking shared dirty pages
- mm: balance dirty pages
- mm: optimize the new mprotect() code a bit
- mm: small cleanup of install_page()
- mm: fixup do_wp_page()
- mm: msync() cleanup (closes: #394392)

--
maks

#27 lore raw
Quoted 5 lines · maximilian attems
Quoted 4 lines

The only -mm stuff I recall being in the Fedora 2.6.18 is the inode-diet stuff which ended up in 2.6.19, though the xmas break has left my head somewhat empty so I may be forgetting something. What patch in particular are you talking about?

Quoted 9 lines

it's no longer visible in the FC6 cvs, due to rebase
but it's name was linux-2.6-mm-tracking-dirty-pages.patch
it is an earlier almagame of the merged patch serie:
- mm: tracking shared dirty pages
- mm: balance dirty pages
- mm: optimize the new mprotect() code a bit
- mm: small cleanup of install_page()
- mm: fixup do_wp_page()
- mm: msync() cleanup (closes: #394392)

Ohh, that. Yes. I had forgotten all about that.
I've been hitting the nog a little too hard :)

Dave

Signature · 1 line
http://www.codemonkey.org.uk
#28 lore raw
In reply to Linus Torvalds
Quoted 5 lines · Linus Torvalds

What we need now is actually looking at the source code, and people who understand the VM, I'm afraid. I'm gathering traces now that I have a good test-case. I'll post my trace tools once I've tested that they work, in case others want to help.

Ok, I've got the traces, but quite frankly, I doubt anybody is crazy enough to want to trawl through them. It's a bit painful, since we're talking thousands of pages to trigger this problem.

Also, I've used the PG_arch_1 flag, which is fine on x86[-64] and probably ARM, but is used for other things on ia64, powerpc and sparc64. But here's the patch in case anybody cares.

It wants a _big_ kernel buffer to capture all the crud into (which is why I made the thing accept a bigger log buffer), and quite frankly, I'm not at all sure that all the locking is ok (ie I could imagine that the dcache-locking thing there in "is_interesting()" could deadlock, what do I know..)

But I've captured some real data with this, which I'll describe separately.

Linus

----

include/linux/page-flags.h +2 -0
index 350878a..967dd80 100644
@@ -91,6 +91,8 @@
 #define PG_nosave_free		18	/* Used for system suspend/resume */
 #define PG_buddy		19	/* Page is free, on buddy lists */
 
+#define SetPageInteresting(page) set_bit(PG_arch_1, &(page)->flags)
+#define PageInteresting(page)	test_bit(PG_arch_1, &(page)->flags)
 
 #if (BITS_PER_LONG > 32)
 /*
lib/Kconfig.debug +1 -1
index 5c26818..7735b83 100644
@@ -79,7 +79,7 @@ config DEBUG_KERNEL
 
 config LOG_BUF_SHIFT
 	int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
-	range 12 21
+	range 12 24
 	default 17 if S390 || LOCKDEP
 	default 16 if X86_NUMAQ || IA64
 	default 15 if SMP
mm/filemap.c +21 -0
index 8332c77..d6a0f56 100644
@@ -116,6 +116,7 @@ void __remove_from_page_cache(struct page *page)
 {
 	struct address_space *mapping = page->mapping;
 
+if (PageInteresting(page)) printk("Removing index %08x from page cache\n", page->index);
 	radix_tree_delete(&mapping->page_tree, page->index);
 	page->mapping = NULL;
 	mapping->nrpages--;
@@ -421,6 +422,23 @@ int filemap_write_and_wait_range(struct address_space *mapping,
 	return err;
 }
 
+static noinline int is_interesting(struct address_space *mapping)
+{
+	struct inode *inode = mapping->host;
+	struct dentry *dentry;
+	int retval = 0;
+
+	spin_lock(&dcache_lock);
+	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+		if (strcmp(dentry->d_name.name, "mapfile"))
+			continue;
+		retval = 1;
+		break;
+	}
+	spin_unlock(&dcache_lock);
+	return retval;
+}
+
 /**
  * add_to_page_cache - add newly allocated pagecache pages
  * @page:	page to add
@@ -439,6 +457,9 @@ int add_to_page_cache(struct page *page, struct address_space *mapping,
 {
 	int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
 
+	if (is_interesting(mapping))
+		SetPageInteresting(page);
+
 	if (error == 0) {
 		write_lock_irq(&mapping->tree_lock);
 		error = radix_tree_insert(&mapping->page_tree, offset, page);
mm/memory.c +4 -0
index 563792f..14c9815 100644
@@ -667,6 +667,8 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
 			tlb_remove_tlb_entry(tlb, pte, addr);
 			if (unlikely(!page))
 				continue;
+if (PageInteresting(page))
+	printk("Unmapped index %08x at %08x\n", page->index, addr);
 			if (unlikely(details) && details->nonlinear_vma
 			    && linear_page_index(details->nonlinear_vma,
 						addr) != page->index)
@@ -1605,6 +1607,7 @@ gotten:
 		 */
 		ptep_clear_flush(vma, address, page_table);
 		set_pte_at(mm, address, page_table, entry);
+if (PageInteresting(new_page)) printk("do_wp_page: mapping index %08x at %08lx\n", new_page->index, address);
 		update_mmu_cache(vma, address, entry);
 		lru_cache_add_active(new_page);
 		page_add_new_anon_rmap(new_page, vma, address);
@@ -2249,6 +2252,7 @@ retry:
 		entry = mk_pte(new_page, vma->vm_page_prot);
 		if (write_access)
 			entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+if (PageInteresting(new_page)) printk("do_no_page: mapping index %08x at %08lx (%s)\n", new_page->index, address, write_access ? "write" : "read");
 		set_pte_at(mm, address, page_table, entry);
 		if (anon) {
 			inc_mm_counter(mm, anon_rss);
mm/page-writeback.c +2 -0
index b3a198c..0466601 100644
@@ -813,6 +813,7 @@ int fastcall set_page_dirty(struct page *page)
 		if (!spd)
 			spd = __set_page_dirty_buffers;
 #endif
+if (PageInteresting(page)) printk("Setting page %08x dirty\n", page->index);
 		return (*spd)(page);
 	}
 	if (!PageDirty(page)) {
@@ -867,6 +868,7 @@ int clear_page_dirty_for_io(struct page *page)
 
 	if (TestClearPageDirty(page)) {
 		if (mapping_cap_account_dirty(mapping)) {
+if (PageInteresting(page)) printk("cpd_for_io: index %08x\n", page->index);
 			page_mkclean(page);
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
mm/rmap.c +3 -0
index 57306fa..e98e84c 100644
@@ -448,6 +448,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 	if (pte_dirty(*pte) || pte_write(*pte)) {
 		pte_t entry;
 
+if (PageInteresting(page)) printk("cleaning index %08x at %08x\n", page->index, address);
 		flush_cache_page(vma, address, pte_pfn(*pte));
 		entry = ptep_clear_flush(vma, address, pte);
 		entry = pte_wrprotect(entry);
@@ -637,6 +638,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
 		goto out_unmap;
 	}
 
+if (PageInteresting(page)) printk("unmapping index %08x from %08lx\n", page->index, address);
 	/* Nuke the page table entry. */
 	flush_cache_page(vma, address, page_to_pfn(page));
 	pteval = ptep_clear_flush(vma, address, pte);
@@ -767,6 +769,7 @@ static void try_to_unmap_cluster(unsigned long cursor,
 		if (ptep_clear_flush_young(vma, address, pte))
 			continue;
 
+if (PageInteresting(page)) printk("Cluster-unmapping %08x from %08lx\n", page->index, address);
 		/* Nuke the page table entry. */
 		flush_cache_page(vma, address, pte_pfn(*pte));
 		pteval = ptep_clear_flush(vma, address, pte);
#29 lore raw
Quoted 7 lines · Linus Torvalds
Quoted 5 lines · Linus Torvalds

What we need now is actually looking at the source code, and people who understand the VM, I'm afraid. I'm gathering traces now that I have a good test-case. I'll post my trace tools once I've tested that they work, in case others want to help.

Quoted 7 lines

Ok, I've got the traces, but quite frankly, I doubt anybody is crazy enough to want to trawl through them. It's a bit painful, since we're talking thousands of pages to trigger this problem.

Also, I've used the PG_arch_1 flag, which is fine on x86[-64] and probably ARM, but is used for other things on ia64, powerpc and sparc64. But here's the patch in case anybody cares.

PG_arch_1 is used on ARM to flag pages that need a dcache flush prior to hitting userspace, in the same way that sparc64 uses it. So ARM systems should not have this patch applied.

Signature · 3 lines
Russell King
 Linux kernel    2.6 ARM Linux   - http://www.arm.linux.org.uk/
 maintainer of:
#30 lore raw
In reply to Linus Torvalds

Ok,
with the ugly trace capture patch, I've actually captured this corruption
in action, I think.

I did a full trace of all pages involved in one run, and picked one corruption at random:

	Chunk 14465 corrupted (0-75)  (01423fb4-01423fff)
	Expected 129, got 0
	Written as (5126)9509(15017)

That's the first 76 bytes of a chunk missing, and it's the last 76 bytes on a page. It's page index 01423 in the mapped file, and bytes fb4-fff within that file.

There were four chunks written to that page:

	Writing chunk 14463/15800 (15%) (0142344c) (1)
	Writing chunk 14462/15800 (30%) (01422e98) (2) (overflows into 00001423)
	Writing chunk 14464/15800 (32%) (01423a00) (3)
	Writing chunk 14465/15800 (60%) (01423fb4) (4)  <--- LOST!

and the other three chunks checked out all right.

And here's the annotated trace as it concerns that page:

- here we write the first chunk to the page:

	** (1)  do_no_page: mapping index 00001423 at b7d1f44c (write)
	**      Setting page 00001423 dirty

- something flushes it out to disk:

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we write the second chunk (which was split over the previous page and the interesting one):

	** (2)  Setting page 00001422 dirty
	** (2)  Setting page 00001423 dirty

- and here we do a cleaning event

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we write the third chunk:

	** (3)  Setting page 00001423 dirty

- here we write the fourth chunk:
** (4) NO DIRTY EVENT

- and a third flush to disk:

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we unmap and flush:

	**      Unmapped index 00001423 at b7d1f000
	**      Removing index 00001423 from page cache

- here we remap to check:

	**      do_no_page: mapping index 00001423 at b7d1f000 (read)
	**      Unmapped index 00001423 at b7d1f000

- and finally, here I remove the file after the run:
** Removing index 00001423 from page cache

Now, the important thing to see here is:

- the missing write did not have a "Setting page 00001423 dirty" event associated with it.

- but I can _see_ where the actual dirty event would be happening in the logs, because I can see the dirty events of the other chunk writes around it, so I know exactly where that fourth write happens. And indeed, it _shouldn't_ get a dirty event, because the page is still dirty from the write of chunk #3 to that page, which _did_ get a dirty event.

I can see that, because the testing app writes the log of the pages it writes, and this is the log around the fourth and final write:

	...
        Writing chunk 5338/15800 (60%) (0076eb48)       PFN: 76e/76f
        Writing chunk 960/15800 (60%) (00156300)        PFN: 156
        Writing chunk 14465/15800 (60%) (01423fb4)  <----
        Writing chunk 8594/15800 (60%) (00bf74a8)       PFN: bf7
        Writing chunk 556/15800 (60%) (000c62f0)        PFN: c6
	Writing chunk 15190/15800 (60%) (01526678)	PFN: 1526
	...

and I can match this up with the full log from the kernel, which looks like this:

        Setting page 0000076e dirty
        Setting page 0000076f dirty
        Setting page 00000156 dirty
        Setting page 000000c6 dirty
	Setting page 00001526 dirty

so I know exactly where the missing writes (to our page at pfn 1423, and the fpn-bf7 page) happened.

- and the thing is, I can see a "cpd_for_io()" happening AFTER that fourth write. Quite a long while after, in fact. So all of this looks very fine indeed. We are not losing any dirty bits.

- EVEN MORE INTERESTING: write 3 makes it onto disk, and it really uses the SAME dirty bit as write 4 did (which didn't make it out to disk!). The event that clears the dirty bit that write 3 did happens AFTER write 4 has happened!

So if we're not losing any dirty bits, what's going on?

I think we have some nasty interaction with the buffer heads. In particular, I don't think it's the dirty page bits that are broken (I _see_ that the PageDirty bit was set after write 4 was done to memory in the kernel traces). So I think that a real writeback just doesn't happen, because somebody has marked the buffer heads clean _after_ it started IO on them.

I think "__mpage_writepage()" is buggy in this regard, for example. It even has a comment about its crapola behaviour:

/*
* Must try to add the page before marking the buffer clean or
* the confused fail path above (OOM) will be very confused when
* it finds all bh marked clean (i.e. it will not write anything) */

however, I don't think that particular thing explains it, because I don't think we use that function for the cases I'm looking at.

Anyway, I'll add tracing for page-writeback setting/cleaning too, in case I might see anything new there..

Linus

maintainer
#31 lore raw

From: Linus Torvalds <[email protected]>
Date: Thu, 28 Dec 2006 14:37:37 -0800 (PST)

Quoted 1 line

So if we're not losing any dirty bits, what's going on?

What happens when we writeback, to the PTEs?

page_mkclean_file() iterates the VMAs and when it finds a shared one it goes:

		entry = ptep_clear_flush(vma, address, pte);
		entry = pte_wrprotect(entry);
		entry = pte_mkclean(entry);

and that's fine, but that PTE is still marked writable, and I think that's key.

What does the fault path do in this situation?

	if (write_access) {
		if (!pte_write(entry))
			return do_wp_page(mm, vma, address,
					pte, pmd, ptl, entry);
		entry = pte_mkdirty(entry);
	}

It does nothing to update the page dirty state, because it's writable, it just sets the PTE dirty bit and that's it. Should it be setting the page dirty here for SHARED cases?

So until vmscan actually unmaps the PTE completely, we have this window in which the application can write to the PTE and the page dirty state doesn't get updated.

Perhaps something later cleans up after this, f.e. by rechecking the PTE dirty bit at the end of I/O or when vmscan unmaps the page. I guess that should handle things, but the above logic definitely stood out to me.

#32 lore raw
Quoted 2 lines · David Miller

What happens when we writeback, to the PTEs?

Not a damn thing.

We clear the PTE's _before_ we even start the write. The writeback does nothing to them. If the user dirties the page while writeback is in progress, we'll take the page fault and re-dirty it _again_.

Quoted 9 lines

page_mkclean_file() iterates the VMAs and when it finds a shared one it goes:

		entry = ptep_clear_flush(vma, address, pte);
		entry = pte_wrprotect(entry);
		entry = pte_mkclean(entry);

and that's fine, but that PTE is still marked writable, and I think that's key.

No it's not. It's right there. "pte_wrprotect(entry)". You even copied it yourself.

Quoted 6 lines

What does the fault path do in this situation?

	if (write_access) {
		if (!pte_write(entry))
			return do_wp_page(mm, vma, address,
					pte, pmd, ptl, entry);

So we call "do_wp_page()", and that does everythign right.

Linus

#33 lore raw
In reply to David Miller

Btw,
much cleaned-up page tracing patch here, in case anybody cares (and
"test.c" attached, although I don't think it changed since last time).

The test.c output is a bit hard to read at times, since it will give offsets in bytes as hex (ie "00a77664" means page frame 00000a77, and byte 664h within that page), while the kernel output is obvioiusly the page indexes (but the page fault _addresses_ can contain information about the exact byte in a page, so you can match them up when some kernel event is related to a page fault).

So both forms are necessary/logical, but it means that to match things up, you often need to ignore the last three hex digits of the address that "test.c" outputs.

This one also adds traces for the tags and the writeback activity, but since I'm going out for birthday dinner, I won't have time to try to actually analyse the trace I have.. Which is why I'm sending it out, in the hope that somebody else is working on this corruption issue and is interested..

Linus

----

fs/buffer.c +2 -0
index 263f88e..f5e132a 100644
@@ -722,6 +722,7 @@ int __set_page_dirty_buffers(struct page *page)
 			set_buffer_dirty(bh);
 			bh = bh->b_this_page;
 		} while (bh != head);
+		PAGE_TRACE(page, "dirtied buffers");
 	}
 	spin_unlock(&mapping->private_lock);
 
@@ -734,6 +735,7 @@ int __set_page_dirty_buffers(struct page *page)
 			__inc_zone_page_state(page, NR_FILE_DIRTY);
 			task_io_account_write(PAGE_CACHE_SIZE);
 		}
+		PAGE_TRACE(page, "setting TAG_DIRTY");
 		radix_tree_tag_set(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
 	}
include/linux/page-flags.h +20 -6
index 350878a..0cf3dce 100644
@@ -91,6 +91,14 @@
 #define PG_nosave_free		18	/* Used for system suspend/resume */
 #define PG_buddy		19	/* Page is free, on buddy lists */
 
+#define SetPageInteresting(page) set_bit(PG_arch_1, &(page)->flags)
+#define PageInteresting(page)	test_bit(PG_arch_1, &(page)->flags)
+
+#define PAGE_TRACE(page, msg, arg...) do {	 				\
+	if (PageInteresting(page))	 					\
+		printk(KERN_DEBUG "PG %08lx: %s:%d " msg "\n", 			\
+			(page)->index, __FILE__, __LINE__ ,##arg );		\
+} while (0)
 
 #if (BITS_PER_LONG > 32)
 /*
@@ -183,32 +191,38 @@ static inline void SetPageUptodate(struct page *page)
 #define PageWriteback(page)	test_bit(PG_writeback, &(page)->flags)
 #define SetPageWriteback(page)						\
 	do {								\
-		if (!test_and_set_bit(PG_writeback,			\
-				&(page)->flags))			\
+		if (!test_and_set_bit(PG_writeback, &(page)->flags)) {	\
+			PAGE_TRACE(page, "set writeback");		\
 			inc_zone_page_state(page, NR_WRITEBACK);	\
+		}							\
 	} while (0)
 #define TestSetPageWriteback(page)					\
 	({								\
 		int ret;						\
 		ret = test_and_set_bit(PG_writeback,			\
 					&(page)->flags);		\
-		if (!ret)						\
+		if (!ret) {						\
+			PAGE_TRACE(page, "set writeback");		\
 			inc_zone_page_state(page, NR_WRITEBACK);	\
+		}							\
 		ret;							\
 	})
 #define ClearPageWriteback(page)					\
 	do {								\
-		if (test_and_clear_bit(PG_writeback,			\
-				&(page)->flags))			\
+		if (test_and_clear_bit(PG_writeback, &(page)->flags)) {	\
+			PAGE_TRACE(page, "end writeback");		\
 			dec_zone_page_state(page, NR_WRITEBACK);	\
+		}							\
 	} while (0)
 #define TestClearPageWriteback(page)					\
 	({								\
 		int ret;						\
 		ret = test_and_clear_bit(PG_writeback,			\
 				&(page)->flags);			\
-		if (ret)						\
+		if (ret) {						\
+			PAGE_TRACE(page, "end writeback");		\
 			dec_zone_page_state(page, NR_WRITEBACK);	\
+		}							\
 		ret;							\
 	})
 
lib/Kconfig.debug +1 -1
index 5c26818..7735b83 100644
@@ -79,7 +79,7 @@ config DEBUG_KERNEL
 
 config LOG_BUF_SHIFT
 	int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
-	range 12 21
+	range 12 24
 	default 17 if S390 || LOCKDEP
 	default 16 if X86_NUMAQ || IA64
 	default 15 if SMP
mm/filemap.c +21 -0
index 8332c77..4df7d35 100644
@@ -116,6 +116,7 @@ void __remove_from_page_cache(struct page *page)
 {
 	struct address_space *mapping = page->mapping;
 
+	PAGE_TRACE(page, "Removing page cache");
 	radix_tree_delete(&mapping->page_tree, page->index);
 	page->mapping = NULL;
 	mapping->nrpages--;
@@ -421,6 +422,23 @@ int filemap_write_and_wait_range(struct address_space *mapping,
 	return err;
 }
 
+static noinline int is_interesting(struct address_space *mapping)
+{
+	struct inode *inode = mapping->host;
+	struct dentry *dentry;
+	int retval = 0;
+
+	spin_lock(&dcache_lock);
+	list_for_each_entry(dentry, &inode->i_dentry, d_alias) {
+		if (strcmp(dentry->d_name.name, "mapfile"))
+			continue;
+		retval = 1;
+		break;
+	}
+	spin_unlock(&dcache_lock);
+	return retval;
+}
+
 /**
  * add_to_page_cache - add newly allocated pagecache pages
  * @page:	page to add
@@ -439,6 +457,9 @@ int add_to_page_cache(struct page *page, struct address_space *mapping,
 {
 	int error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
 
+	if (is_interesting(mapping))
+		SetPageInteresting(page);
+
 	if (error == 0) {
 		write_lock_irq(&mapping->tree_lock);
 		error = radix_tree_insert(&mapping->page_tree, offset, page);
mm/memory.c +3 -0
index 563792f..20af32f 100644
@@ -667,6 +667,7 @@ static unsigned long zap_pte_range(struct mmu_gather *tlb,
 			tlb_remove_tlb_entry(tlb, pte, addr);
 			if (unlikely(!page))
 				continue;
+			PAGE_TRACE(page, "unmapped at %08lx", addr);
 			if (unlikely(details) && details->nonlinear_vma
 			    && linear_page_index(details->nonlinear_vma,
 						addr) != page->index)
@@ -1605,6 +1606,7 @@ gotten:
 		 */
 		ptep_clear_flush(vma, address, page_table);
 		set_pte_at(mm, address, page_table, entry);
+		PAGE_TRACE(new_page, "write fault at %08lx", address);
 		update_mmu_cache(vma, address, entry);
 		lru_cache_add_active(new_page);
 		page_add_new_anon_rmap(new_page, vma, address);
@@ -2249,6 +2251,7 @@ retry:
 		entry = mk_pte(new_page, vma->vm_page_prot);
 		if (write_access)
 			entry = maybe_mkwrite(pte_mkdirty(entry), vma);
+		PAGE_TRACE(new_page, "mapping at %08lx (%s)", address, write_access ? "write" : "read");
 		set_pte_at(mm, address, page_table, entry);
 		if (anon) {
 			inc_mm_counter(mm, anon_rss);
mm/page-writeback.c +12 -3
index b3a198c..15f3aaf 100644
@@ -773,6 +773,7 @@ int __set_page_dirty_nobuffers(struct page *page)
 				__inc_zone_page_state(page, NR_FILE_DIRTY);
 				task_io_account_write(PAGE_CACHE_SIZE);
 			}
+			PAGE_TRACE(page, "setting TAG_DIRTY");
 			radix_tree_tag_set(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
 		}
@@ -813,6 +814,7 @@ int fastcall set_page_dirty(struct page *page)
 		if (!spd)
 			spd = __set_page_dirty_buffers;
 #endif
+		PAGE_TRACE(page, "setting dirty");
 		return (*spd)(page);
 	}
 	if (!PageDirty(page)) {
@@ -867,6 +869,7 @@ int clear_page_dirty_for_io(struct page *page)
 
 	if (TestClearPageDirty(page)) {
 		if (mapping_cap_account_dirty(mapping)) {
+			PAGE_TRACE(page, "clean_for_io");
 			page_mkclean(page);
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
@@ -886,10 +889,12 @@ int test_clear_page_writeback(struct page *page)
 
 		write_lock_irqsave(&mapping->tree_lock, flags);
 		ret = TestClearPageWriteback(page);
-		if (ret)
+		if (ret) {
+			PAGE_TRACE(page, "clearing TAG_WRITEBACK");
 			radix_tree_tag_clear(&mapping->page_tree,
 						page_index(page),
 						PAGECACHE_TAG_WRITEBACK);
+		}
 		write_unlock_irqrestore(&mapping->tree_lock, flags);
 	} else {
 		ret = TestClearPageWriteback(page);
@@ -907,14 +912,18 @@ int test_set_page_writeback(struct page *page)
 
 		write_lock_irqsave(&mapping->tree_lock, flags);
 		ret = TestSetPageWriteback(page);
-		if (!ret)
+		if (!ret) {
+			PAGE_TRACE(page, "setting TAG_WRITEBACK");
 			radix_tree_tag_set(&mapping->page_tree,
 						page_index(page),
 						PAGECACHE_TAG_WRITEBACK);
-		if (!PageDirty(page))
+		}
+		if (!PageDirty(page)) {
+			PAGE_TRACE(page, "clearing TAG_DIRTY");
 			radix_tree_tag_clear(&mapping->page_tree,
 						page_index(page),
 						PAGECACHE_TAG_DIRTY);
+		}
 		write_unlock_irqrestore(&mapping->tree_lock, flags);
 	} else {
 		ret = TestSetPageWriteback(page);
mm/rmap.c +3 -0
index 57306fa..e6b4676 100644
@@ -448,6 +448,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 	if (pte_dirty(*pte) || pte_write(*pte)) {
 		pte_t entry;
 
+		PAGE_TRACE(page, "cleaning PTE %08lx", address);
 		flush_cache_page(vma, address, pte_pfn(*pte));
 		entry = ptep_clear_flush(vma, address, pte);
 		entry = pte_wrprotect(entry);
@@ -637,6 +638,7 @@ static int try_to_unmap_one(struct page *page, struct vm_area_struct *vma,
 		goto out_unmap;
 	}
 
+	PAGE_TRACE(page, "unmapping from %08lx", address);
 	/* Nuke the page table entry. */
 	flush_cache_page(vma, address, page_to_pfn(page));
 	pteval = ptep_clear_flush(vma, address, pte);
@@ -767,6 +769,7 @@ static void try_to_unmap_cluster(unsigned long cursor,
 		if (ptep_clear_flush_young(vma, address, pte))
 			continue;
 
+		PAGE_TRACE(page, "unmapping from %08lx", address);
 		/* Nuke the page table entry. */
 		flush_cache_page(vma, address, pte_pfn(*pte));
 		pteval = ptep_clear_flush(vma, address, pte);

#include <sys/mman.h>
#include <sys/fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#define TARGETSIZE (22 << 20)
#define CHUNKSIZE (1460)
#define NRCHUNKS (TARGETSIZE / CHUNKSIZE)
#define SIZE (NRCHUNKS * CHUNKSIZE)

static void fillmem(void *start, int nr)

{
	memset(start, nr, CHUNKSIZE);
}

#define page_offset(buf, off) (unsigned)((unsigned long)(buf)+(off)-(unsigned long)(mapping))

static int chunkorder[NRCHUNKS];
static char *mapping;

static int order(int nr)

{
	int i;
	if (nr < 0 || nr >= NRCHUNKS)
		return -1;
	for (i = 0; i < NRCHUNKS; i++)
		if (chunkorder[i] == nr)
			return i;
	return -2;
}

static void checkmem(void *buf, int nr)

{
	unsigned int start = ~0u, end = 0;
	unsigned char c = nr, *p = buf, differs = 0;
	int i;
	for (i = 0; i < CHUNKSIZE; i++) {
		unsigned char got = *p++;
		if (got != c) {
			if (i < start)
				start = i;
			if (i > end)
				end = i;
			differs = got;
		}
	}
	if (start < end) {
		printf("Chunk %d corrupted (%u-%u)  (%x-%x)            \n", nr, start, end,
			page_offset(buf, start), page_offset(buf, end));
		printf("Expected %u, got %u\n", c, differs);
		printf("Written as (%d)%d(%d)\n", order(nr-1), order(nr), order(nr+1));
	}
}

static char *remap(int fd, char *mapping)

{
	if (mapping) {
		munmap(mapping, SIZE);
		posix_fadvise(fd, 0, SIZE, POSIX_FADV_DONTNEED);
	}
	return mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
}

int main(int argc, char **argv)

{
	int fd, i;

	/*
	 * Make some random ordering of writing the chunks to the
	 * memory map..
	 *
	 * Start with fully ordered..
	 */
	for (i = 0; i < NRCHUNKS; i++)
		chunkorder[i] = i;

	/* ..and then mix it up randomly */
	srandom(time(NULL));
	for (i = 0; i < NRCHUNKS; i++) {
		int index = (unsigned int) random() % NRCHUNKS;
		int nr = chunkorder[index];
		chunkorder[index] = chunkorder[i];
		chunkorder[i] = nr;
	}

	fd = open("mapfile", O_RDWR | O_TRUNC | O_CREAT, 0666);
	if (fd < 0)
		return -1;
	if (ftruncate(fd, SIZE) < 0)
		return -1;
	mapping = remap(fd, NULL);
	if (-1 == (int)(long)mapping)
		return -1;

	for (i = 0; i < NRCHUNKS; i++) {
		int chunk = chunkorder[i];
		printf("Writing chunk %d/%d (%d%%) (%08x)     \r",
			chunk, NRCHUNKS,
			100*i/NRCHUNKS,
			page_offset(mapping, chunk * CHUNKSIZE));
		fillmem(mapping + chunk * CHUNKSIZE, chunk);
	}
	printf("\n");

	/* Unmap, drop, and remap.. */
	mapping = remap(fd, mapping);

	/* .. and check */
	for (i = 0; i < NRCHUNKS; i++) {
		int chunk = i;
		printf("Checking chunk %d/%d (%d%%) (%08x)    \r",
			i, NRCHUNKS,
			100*i/NRCHUNKS,
			page_offset(mapping, i * CHUNKSIZE));
		checkmem(mapping + chunk * CHUNKSIZE, chunk);
	}
	printf("\n");

	/* Clean up for next time */
	sleep(5);
	sync();
	sleep(5);
	munmap(mapping, SIZE);
	close(fd);
	unlink("mapfile");
	
	return 0;
}
#34 lore raw
Quoted 3 lines · On Thu, 28 Dec 2006 17:38:38 -0800 (PST) Linus Torvalds <[email protected]>…

in
the hope that somebody else is working on this corruption issue and is interested..

What corruption issue? ;)

I'm finding that the corruption happens trivially with your test app, but apparently doesn't happen at all with ext2 or ext3, data=writeback. Maybe it will happen with increased rarity, but the difference is quite stark.

Removing the

err = walk_page_buffers(handle, page_bufs, 0, PAGE_CACHE_SIZE,
NULL, journal_dirty_data_fn);

from ext3_ordered_writepage() fixes things up.

The things which journal_submit_data_buffers() does after dropping all the locks are ... disturbing - I don't think we have sufficient tests in there to ensure that the buffer is still where we think it is after we retake locks (they're slippery little buggers). But that wouldn't explain it anyway.

It's inefficient that journal_dirty_data() will put these locked, clean buffers onto BJ_SyncData instead of BJ_Locked, but journal_submit_data_buffers() seems to dtrt with them.

So no theory yet. Maybe ext3 is just altering timing. But the difference is really large..

Disabling all the WB_SYNC_NONE stuff and making everything go synchronous everywhere has no effect. Disabling bdi_write_congested() has no effect.

#35 lore raw
In reply to Linus Torvalds
Quoted 104 lines · Linus Torvalds

Ok,
with the ugly trace capture patch, I've actually captured this corruption
in action, I think.

I did a full trace of all pages involved in one run, and picked one corruption at random:

	Chunk 14465 corrupted (0-75)  (01423fb4-01423fff)
	Expected 129, got 0
	Written as (5126)9509(15017)

That's the first 76 bytes of a chunk missing, and it's the last 76 bytes on a page. It's page index 01423 in the mapped file, and bytes fb4-fff within that file.

There were four chunks written to that page:

	Writing chunk 14463/15800 (15%) (0142344c) (1)
	Writing chunk 14462/15800 (30%) (01422e98) (2) (overflows into 00001423)
	Writing chunk 14464/15800 (32%) (01423a00) (3)
	Writing chunk 14465/15800 (60%) (01423fb4) (4)  <--- LOST!

and the other three chunks checked out all right.

And here's the annotated trace as it concerns that page:

- here we write the first chunk to the page:

	** (1)  do_no_page: mapping index 00001423 at b7d1f44c (write)
	**      Setting page 00001423 dirty

- something flushes it out to disk:

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we write the second chunk (which was split over the previous page and the interesting one):

	** (2)  Setting page 00001422 dirty
	** (2)  Setting page 00001423 dirty

- and here we do a cleaning event

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we write the third chunk:

	** (3)  Setting page 00001423 dirty

- here we write the fourth chunk:
** (4) NO DIRTY EVENT

- and a third flush to disk:

	**      cpd_for_io: index 00001423
	**      cleaning index 00001423 at b7d1f000

- here we unmap and flush:

	**      Unmapped index 00001423 at b7d1f000
	**      Removing index 00001423 from page cache

- here we remap to check:

	**      do_no_page: mapping index 00001423 at b7d1f000 (read)
	**      Unmapped index 00001423 at b7d1f000

- and finally, here I remove the file after the run:
** Removing index 00001423 from page cache

Now, the important thing to see here is:

- the missing write did not have a "Setting page 00001423 dirty" event associated with it.

- but I can _see_ where the actual dirty event would be happening in the logs, because I can see the dirty events of the other chunk writes around it, so I know exactly where that fourth write happens. And indeed, it _shouldn't_ get a dirty event, because the page is still dirty from the write of chunk #3 to that page, which _did_ get a dirty event.

   I can see that, because the testing app writes the log of the pages it 
   writes, and this is the log around the fourth and final write:

	...
        Writing chunk 5338/15800 (60%) (0076eb48)       PFN: 76e/76f
        Writing chunk 960/15800 (60%) (00156300)        PFN: 156
        Writing chunk 14465/15800 (60%) (01423fb4)  <----
        Writing chunk 8594/15800 (60%) (00bf74a8)       PFN: bf7
        Writing chunk 556/15800 (60%) (000c62f0)        PFN: c6
	Writing chunk 15190/15800 (60%) (01526678)	PFN: 1526
	...

   and I can match this up with the full log from the kernel, which looks 
   like this:

        Setting page 0000076e dirty
        Setting page 0000076f dirty
        Setting page 00000156 dirty
        Setting page 000000c6 dirty
	Setting page 00001526 dirty

   so I know exactly where the missing writes (to our page at pfn 1423, 
   and the fpn-bf7 page) happened.

- and the thing is, I can see a "cpd_for_io()" happening AFTER that fourth write. Quite a long while after, in fact. So all of this looks very fine indeed. We are not losing any dirty bits.

Quoted 8 lines

- EVEN MORE INTERESTING: write 3 makes it onto disk, and it really uses the SAME dirty bit as write 4 did (which didn't make it out to disk!). The event that clears the dirty bit that write 3 did happens AFTER write 4 has happened!

So if we're not losing any dirty bits, what's going on?

I think we have some nasty interaction with the buffer heads. In

But are chunks 3 and 4 in separate buffer heads? Sorry could not see it immediately from the output you showed...

It is just that there may be a different cause rather than buffer dirty state...

A shot in the dark I know but it could perhaps be that a "COW for MAP_PRIVATE" like event happens when the page is dirty already thus the second write never actually makes it to the shared page thus it never gets written out.

I am almost certainly totally barking up the wrong tree but I thought it may be worth mentioning just in case there was a slip in the COW logic or page writable state maintenance somewhere...

Best regards,

Anton

Quoted 22 lines

particular, I don't think it's the dirty page bits that are broken (I _see_ that the PageDirty bit was set after write 4 was done to memory in the kernel traces). So I think that a real writeback just doesn't happen, because somebody has marked the buffer heads clean _after_ it started IO on them.

I think "__mpage_writepage()" is buggy in this regard, for example. It even has a comment about its crapola behaviour:

/*
* Must try to add the page before marking the buffer clean or
* the confused fail path above (OOM) will be very confused when
* it finds all bh marked clean (i.e. it will not write anything) */

however, I don't think that particular thing explains it, because I don't think we use that function for the cases I'm looking at.

Anyway, I'll add tracing for page-writeback setting/cleaning too, in case I might see anything new there..

Linus

Signature · 3 lines
Anton Altaparmakov <aia21 at cam.ac.uk> (replace at with @)
Unix Support, Computing Service, University of Cambridge, CB2 3QH, UK
Linux NTFS maintainer, http://www.linux-ntfs.org/
#36 lore raw
Quoted 3 lines · Anton Altaparmakov

But are chunks 3 and 4 in separate buffer heads? Sorry could not see it immediately from the output you showed...

No, this is a 4kB filesystem. A single bh per page.

Quoted 2 lines

It is just that there may be a different cause rather than buffer dirty state...

Sure.

Quoted 4 lines

A shot in the dark I know but it could perhaps be that a "COW for MAP_PRIVATE" like event happens when the page is dirty already thus the second write never actually makes it to the shared page thus it never gets written out.

There are no private mappings anywhere, and no forks. Just a single mmap (well, we unmap and remap in order to force the page cache to be invalidated properly with the posix_fadvise() thing, but that's literally the only user).

Linus

#37 lore raw
In reply to Linus Torvalds
Quoted 2 lines · Linus Torvalds

going back to Linux-2.6.5 at least, according to one tester).

I apologize for the confusion, but it just occurred to me that I was actually experiencing a totally different problem: I set a root filesystem of 3Mib for qemu, so the test program just didn't have enough space for its file.

Signature · 1 line
Guillaume
#38 lore raw
In reply to Nick Piggin
Quoted 4 lines · Nick Piggin

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

But the point being that "try_to_free_buffers()" marks it clean AFTERWARDS.

So yes, the page gets marked dirty in the pte's - the hardware generally does that for us, so we don't have to worry about that part going on.

But "try_to_free_buffers()" seems to clear those dirty bits without serializing it really any way. It just says "ok, I will now clear them". Without knowing whether the dirty bits got set before the IO that cleared the buffer head dirty bits or not.

What is _that_ serialization? As far as I can see, the only way to guarantee that to happen (since the dirty bits in the page tables will get set without us ever even being notified) is that the page tables themselves must simply never contain that page in a writable form at all.

And that seems to be lacking.

Anyway, I have what I consider a much simpler solution: just don't DO all that crap in try_to_free_buffers() at all. I sent it out to some people already, not not very widely.

I reproduce my suggestion here for you (and maybe others too who weren't cc'd in that other discussion group) to comment on..

Linus

---

So I think your patch is really broken, how about this one instead?

It's really my previous patch, BUT it also adds a

	if (PageDirty(page) ..
		return 0;

case, on the assumption that since PageDirty() measn that one of the buffers should be dirty, there's no point in even _trying_ drop_buffers, since that should just fail anyway.

Now, that assumption is obviously wrong _if_ the buffers have been cleaned by something else. So in that case, we now don't remove the buffer heads, but who really cares? The page will remain on the dirty list, and something should be trying to write it out, but since now all the buffers are clean, once that happens, there is no actual IO to happen.

Hmm? So this means that we simply don't remove the buffers early from such pages, but there shouldn't be any real downside.

Now, the only question would be if the page is marked dirty _while_ this is running. We do hold the page lock, but page dirtying doesn't get the lock, does it? But at least we won't mark the page _clean_ when it shouldn't be.. And we still are atomic wrt the actual buffer lists (mapping->private_lock), so I think this should all be ok, and drop_buffers() will do the right thing.

So no race possible either.

At least as far as I can see. And the patch certainly is simple.

Now the question whether this actually _fixes_ any problems does remain, but I think this should be a pretty good solution if the bug really is here. Andrew?

Linus

----

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
#39 lore raw
Quoted 11 lines · On Sun, 17 Dec 2006 21:50:43 -0800 (PST) Linus Torvalds <[email protected]>…
Quoted 4 lines · Nick Piggin

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

But the point being that "try_to_free_buffers()" marks it clean AFTERWARDS.

Quoted 7 lines

So yes, the page gets marked dirty in the pte's - the hardware generally does that for us, so we don't have to worry about that part going on.

But "try_to_free_buffers()" seems to clear those dirty bits without serializing it really any way. It just says "ok, I will now clear them". Without knowing whether the dirty bits got set before the IO that cleared the buffer head dirty bits or not.

Yes, I can't see anything correct about the current behaviour.

But I'm going blue in the face here trying to feed try_to_free_buffers() a page_mapped(page), without success. pagevec_strip() presumably isn't triggering.

Quoted 40 lines

What is _that_ serialization? As far as I can see, the only way to guarantee that to happen (since the dirty bits in the page tables will get set without us ever even being notified) is that the page tables themselves must simply never contain that page in a writable form at all.

And that seems to be lacking.

Anyway, I have what I consider a much simpler solution: just don't DO all that crap in try_to_free_buffers() at all. I sent it out to some people already, not not very widely.

I reproduce my suggestion here for you (and maybe others too who weren't cc'd in that other discussion group) to comment on..

...

fs/buffer.c +1 -10
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *

fs/buffer.c int try_to_free_buffers(struct page *page) · line 2850

Quoted 10 lines
fs/buffer.c +0 -7
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;

This will (at least) cause truncate to do peculiar things. do_invalidatepage() runs discard_buffer() against the dirty page and will then expect try_to_free_buffers() to remove those buffers and then clean the page. truncate_complete_page() will clean the page, but it still has those invalidated buffers. We'll end up with a large number of clean, unused pages on the LRU, with attached buffers. These should eventually get reaped, but it'll change the page aging dynamics.

#40 lore raw
Quoted 11 lines · On Sun, 17 Dec 2006 23:16:17 -0800 Andrew Morton <[email protected]> wrote:

fs/buffer.c int try_to_free_buffers(struct page *page) · line 2850

Quoted 3 lines

out:

fs/buffer.c +0 -0
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;

This will (at least) cause truncate to do peculiar things. do_invalidatepage() runs discard_buffer() against the dirty page and will then expect try_to_free_buffers() to remove those buffers and then clean the page. truncate_complete_page() will clean the page, but it still has those invalidated buffers. We'll end up with a large number of clean, unused pages on the LRU, with attached buffers. These should eventually get reaped, but it'll change the page aging dynamics.

That being said, it's be great to get this tested by someone who can trigger this bug, please.

#41 lore raw
In reply to Andrew Morton
Quoted 15 lines · Andrew Morton
Quoted 11 lines · On Sun, 17 Dec 2006 21:50:43 -0800 (PST) Linus Torvalds <[email protected]>…
Quoted 3 lines · Nick Piggin

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

But the point being that "try_to_free_buffers()" marks it clean AFTERWARDS.

So yes, the page gets marked dirty in the pte's - the hardware generally does that for us, so we don't have to worry about that part going on.

Quoted 8 lines
Quoted 1 line

But "try_to_free_buffers()" seems to clear those dirty bits without serializing it really any way. It just says "ok, I will now clear them". Without knowing whether the dirty bits got set before the IO that cleared the buffer head dirty bits or not.

Yes, I can't see anything correct about the current behaviour.

But I'm going blue in the face here trying to feed try_to_free_buffers() a page_mapped(page), without success. pagevec_strip() presumably isn't triggering.

I can trigger it here, with a kernel patch to call pagevec_strip unconditionally. I am seeing it clearing pte dirty bits, which is surely a dataloss bug.

BUG: warning at mm/page-writeback.c:862/clear_page_dirty_warn()
 [<c013f65a>] clear_page_dirty_warn+0xdb/0xdd
 [<c0174309>] try_to_free_buffers+0x6b/0x7e
 [<c01937ec>] ext3_releasepage+0x0/0x74
 [<c013bb48>] try_to_release_page+0x2c/0x40
 [<c0140925>] pagevec_strip+0x52/0x54
 [<c0141580>] shrink_active_list+0x2a0/0x3c8
 [<c0142100>] shrink_zone+0xcd/0xea
 [<c014266d>] kswapd+0x311/0x41e
 [<c012c6aa>] autoremove_wake_function+0x0/0x37
 [<c014235c>] kswapd+0x0/0x41e
 [<c012c527>] kthread+0xde/0xe2
 [<c012c449>] kthread+0x0/0xe2
 [<c010395b>] kernel_thread_helper+0x7/0x1c
 =======================

(clear_page_dirty_warn() is test_clear_page_dirty which WARN_ON()s the result of page_mkclean)

Quoted 6 lines

This will (at least) cause truncate to do peculiar things. do_invalidatepage() runs discard_buffer() against the dirty page and will then expect try_to_free_buffers() to remove those buffers and then clean
the page. truncate_complete_page() will clean the page, but it still has
those invalidated buffers. We'll end up with a large number of clean,
unused pages on the LRU, with attached buffers. These should eventually
get reaped, but it'll change the page aging dynamics.

This isn't so nice. I wonder if you could just ClearPageDirty before calling try_to_free_buffers in this case, or is that too much of a hack? Ideally I guess you want a variant that is happy to discard dirtiness (alternatively, my proposal to redirty the page if we find a dirty pte should also handle this).

Signature · 2 lines
SUSE Labs, Novell Inc.
Send instant messages to your online friends http://au.messenger.yahoo.com 
#42 lore raw
In reply to Linus Torvalds
Quoted 9 lines · Linus Torvalds
Quoted 3 lines · Nick Piggin

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

But the point being that "try_to_free_buffers()" marks it clean AFTERWARDS.

For some reason I thought you were suggesting it is a problem on its own :P

Yes I agree there is a pagefault vs ttfb race.

Signature · 2 lines
SUSE Labs, Novell Inc.
Send instant messages to your online friends http://au.messenger.yahoo.com 
#43 lore raw
In reply to Linus Torvalds

I tried latest git with the patch from this email and it still get file content corruption. If I can help you further debug the problem tell me what to do.

Quoted 111 lines · Linus Torvalds
Quoted 4 lines · Nick Piggin

I can't see how that's exactly a problem -- so long as the page does not get reclaimed (it won't, because we have a ref on it) then all that matters is that the page eventually gets marked dirty.

But the point being that "try_to_free_buffers()" marks it clean AFTERWARDS.

So yes, the page gets marked dirty in the pte's - the hardware generally does that for us, so we don't have to worry about that part going on.

But "try_to_free_buffers()" seems to clear those dirty bits without serializing it really any way. It just says "ok, I will now clear them". Without knowing whether the dirty bits got set before the IO that cleared the buffer head dirty bits or not.

What is _that_ serialization? As far as I can see, the only way to guarantee that to happen (since the dirty bits in the page tables will get set without us ever even being notified) is that the page tables themselves must simply never contain that page in a writable form at all.

And that seems to be lacking.

Anyway, I have what I consider a much simpler solution: just don't DO all that crap in try_to_free_buffers() at all. I sent it out to some people already, not not very widely.

I reproduce my suggestion here for you (and maybe others too who weren't cc'd in that other discussion group) to comment on..

Linus

---

So I think your patch is really broken, how about this one instead?

It's really my previous patch, BUT it also adds a

	if (PageDirty(page) ..
		return 0;

case, on the assumption that since PageDirty() measn that one of the buffers should be dirty, there's no point in even _trying_ drop_buffers, since that should just fail anyway.

Now, that assumption is obviously wrong _if_ the buffers have been cleaned by something else. So in that case, we now don't remove the buffer heads, but who really cares? The page will remain on the dirty list, and something should be trying to write it out, but since now all the buffers are clean, once that happens, there is no actual IO to happen.

Hmm? So this means that we simply don't remove the buffers early from such pages, but there shouldn't be any real downside.

Now, the only question would be if the page is marked dirty _while_ this is running. We do hold the page lock, but page dirtying doesn't get the lock, does it? But at least we won't mark the page _clean_ when it shouldn't be.. And we still are atomic wrt the actual buffer lists (mapping->private_lock), so I think this should all be ok, and drop_buffers() will do the right thing.

So no race possible either.

At least as far as I can see. And the patch certainly is simple.

Now the question whether this actually _fixes_ any problems does remain, but I think this should be a pretty good solution if the bug really is here. Andrew?

Linus

----

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
#44 lore raw
Quoted 4 lines · On Mon, 18 Dec 2006 11:19:04 +0200 Andrei Popa <[email protected]> wrote:

I tried latest git with the patch from this email and it still get file content corruption. If I can help you further debug the problem tell me what to do.

Can you please tell us all the steps which we need to take to reproduce this?

#45 lore raw
Quoted 9 lines · Andrew Morton
Quoted 4 lines · On Mon, 18 Dec 2006 11:19:04 +0200 Andrei Popa <[email protected]> wrote:

I tried latest git with the patch from this email and it still get file content corruption. If I can help you further debug the problem tell me what to do.

Can you please tell us all the steps which we need to take to reproduce this?

I'm using rtorrent-0.7.0 and libtorrent-0.11.0, just download a torrent with multiple files(I downloaded 84 rar files) and when it will finish it will do a hash check and at the end of the check will say "Hash check on download completion found bad chunks, consider using "safe_sync"." and stop and most of the downloaded files are broken. With Peter Zijlstra patch this error doesn't show but there is file corruption(although less files are corrupted); afther the hash check, rtorrent will download the bad chunks and do another hash check and all files are ok.

#46 lore raw
Quoted 11 lines · Andrei Popa
Quoted 9 lines · Andrew Morton
Quoted 4 lines · On Mon, 18 Dec 2006 11:19:04 +0200 Andrei Popa <[email protected]> wrote:

I tried latest git with the patch from this email and it still get file content corruption. If I can help you further debug the problem tell me what to do.

Can you please tell us all the steps which we need to take to reproduce this?

Quoted 9 lines

I'm using rtorrent-0.7.0 and libtorrent-0.11.0, just download a torrent with multiple files(I downloaded 84 rar files) and when it will finish it will do a hash check and at the end of the check will say "Hash check on download completion found bad chunks, consider using "safe_sync"." and stop and most of the downloaded files are broken. With Peter Zijlstra patch this error doesn't show but there is file corruption(although less files are corrupted); afther the hash check, rtorrent will download the bad chunks and do another hash check and all files are ok.

OK, I'll try this on a ext3 box. BTW, what data mode are you using ext3 in?

Also, for testings sake, could you give this a go:
It's a total hack but I guess worth testing.

---
 mm/rmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
mm/rmap.c +1 -1
Index: linux-2.6-git/mm/rmap.c
===================================================================
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/* entry = pte_mkclean(entry); */
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);
#47 lore raw
Quoted 3 lines

OK, I'll try this on a ext3 box. BTW, what data mode are you using ext3 in?

ordered

Quoted 14 lines

Also, for testings sake, could you give this a go:
It's a total hack but I guess worth testing.

---
 mm/rmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6-git/mm/rmap.c
===================================================================

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 8 lines
mm/rmap.c +1 -1
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/* entry = pte_mkclean(entry); */
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

with latest git and this patch there is no corruption !

#48 lore raw
Quoted 19 lines · Andrei Popa
Quoted 2 lines

OK, I'll try this on a ext3 box. BTW, what data mode are you using ext3 in?

ordered

Quoted 14 lines

Also, for testings sake, could you give this a go:
It's a total hack but I guess worth testing.

---
 mm/rmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6-git/mm/rmap.c
===================================================================

mm/rmap.c +0 -0
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page
 		goto unlock;
 
Quoted 9 lines

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 6 lines
mm/rmap.c +1 -1
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/* entry = pte_mkclean(entry); */
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

with latest git and this patch there is no corruption !

I've not run a torrent app here recently. Should this patch be applied to a plain 2.6-20-rc1 before I do run azureas or similar apps?

Quoted 7 lines

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 7 lines
Cheers, Gene
"There are four boxes to be used in defense of liberty:
 soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
Yahoo.com and AOL/TW attorneys please note, additions to the above
message by Gene Heskett are:
Copyright 2006 by Maurice Eugene Heskett, all rights reserved.
#49 lore raw
Quoted 29 lines · Gene Heskett
Quoted 19 lines · Andrei Popa
Quoted 2 lines

OK, I'll try this on a ext3 box. BTW, what data mode are you using ext3 in?

ordered

Quoted 14 lines

Also, for testings sake, could you give this a go:
It's a total hack but I guess worth testing.

---
 mm/rmap.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6-git/mm/rmap.c
===================================================================

mm/rmap.c +0 -0
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page
 		goto unlock;
 
Quoted 9 lines

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 6 lines
mm/rmap.c +1 -1
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/* entry = pte_mkclean(entry); */
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

with latest git and this patch there is no corruption !

Quoted 2 lines

I've not run a torrent app here recently. Should this patch be applied to a plain 2.6-20-rc1 before I do run azureas or similar apps?

depends on what the blue frog does, if it uses MAP_SHARED like rtorrent does then yeah, probably. This patch really should not be the final one, I'm currently still trying to wrap my head around the issue. That said, it should be safe to use :-)

#50 lore raw

On Monday 18 December 2006 10:32, Peter Zijlstra wrote:
[...]

Quoted 9 lines
Quoted 3 lines

I've not run a torrent app here recently. Should this patch be applied to a plain 2.6-20-rc1 before I do run azureas or similar apps?

depends on what the blue frog does, if it uses MAP_SHARED like rtorrent does then yeah, probably. This patch really should not be the final one, I'm currently still trying to wrap my head around the issue. That said, it should be safe to use :-)

Thanks, I'll do it.

Signature · 7 lines
Cheers, Gene
"There are four boxes to be used in defense of liberty:
 soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
Yahoo.com and AOL/TW attorneys please note, additions to the above
message by Gene Heskett are:
Copyright 2006 by Maurice Eugene Heskett, all rights reserved.
#51 lore raw
In reply to Andrew Morton
Quoted 24 lines · Andrew Morton
Quoted 2 lines · On Sun, 17 Dec 2006 15:39:32 +0200 Andrei Popa <[email protected]> wrote:

I was mistaken, I'm still having file corruption with rtorrent.

Well I'm not very optimistic, but if people could try this, please...

From: Andrew Morton <[email protected]>

try_to_free_buffers() clears the page's dirty state if it successfully removed the page's buffers.

Background for this:

- a process does a one-byte-write to a file on a 64k pagesize, 4k blocksize ext3 filesystem. The page is now PageDirty, !PgeUptodate and has one dirty buffer and 15 not uptodate buffers.

- kjournald writes the dirty buffer. The page is now PageDirty, !PageUptodate and has a mix of clean and not uptodate buffers.

Quoted 9 lines

- try_to_free_buffers() removes the page's buffers. It MUST now clear PageDirty. If we were to leave the page dirty then we'd have a dirty, not uptodate page with no buffer_heads.

We're screwed: we cannot write the page because we don't know which sections of it contain garbage. We cannot read the page because we don't know which sections of it contain modified data. We cannot free the page because it is dirty.

How about we stick something like this on top of that patch. It should preserve the dirty state as required.

I tried to tinker with avoiding the clear/set thing but could not convince myself it was close to safe.

This should be safe; page_mkclean walks the rmap and flips the pte's under the pte lock and records the dirty state while iterating. Concurrent faults will either do set_page_dirty() before we get around to doing it or vice versa, but dirty state is not lost.

---
 mm/page-writeback.c |    5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)
mm/page-writeback.c +3 -2
Index: linux-2.6-git/mm/page-writeback.c
===================================================================
@@ -872,8 +872,9 @@ int test_clear_page_dirty(struct page *p
 		 * page is locked, which pins the address_space
 		 */
 		if (mapping_cap_account_dirty(mapping)) {
-			if (must_clean_ptes)
-				page_mkclean(page);
+			int cleaned = page_mkclean(page);
+			if (!must_clean_ptes && cleaned)
+				set_page_dirty(page);
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
 		return 1;
#52 lore raw

Andrei,
could you try Peter's patch (on top of Andrew's patch - it depends on
it, and wouldn't work on an unmodified -git kernel, but add the WARN_ON() I mention in this email? You seem to be able to reproduce this easily.. Thanks)

Quoted 5 lines · Peter Zijlstra

This should be safe; page_mkclean walks the rmap and flips the pte's under the pte lock and records the dirty state while iterating. Concurrent faults will either do set_page_dirty() before we get around to doing it or vice versa, but dirty state is not lost.

Ok, I really liked this patch, but the more I thought about it, the more I started to doubt the reasons for liking it.

I think we have some core fundamental problem here that this patch is needed at all.

So let's think about this: we apparently have two cases of "clear_page_dirty()":

- the one that really wants to clear the bit unconditionally (Andrew calls this the "must_clean_ptes" case, which I personally find to be a really confusing name, but whatever)

- the other case. The case that doesn't want to really clear the pte dirty bits.

and I thought your patch made sense, because it saved away the pte state in the page dirty state, and that matches my mental model, but the more I think about it, the less sense that whole "the other case" situation makes AT ALL.

Why does "the other case" exist at all? If you want to clear the dirty page flag, what is _ever_ the reason for not wanting to drop PTE dirty information? In other words, what possible reason can there ever be for saying "I want this page to be clean", while at the same time saying "but if it was dirty in the page tables, don't forget about that state".

So I absolutely detested Andrew's original patch, because that one made zero sense at all even from a code standpoint. With your patch on top, it all suddenly makes sense: at least you don't just leave dirty pages in the PTE's with a "struct page" that is marked clean, and the end result is undeniably at least _consistent_.

So Andrew's patch I can't stand, because the whole point of it seems to be to leave the system in an inconsistent state (dirty in the pte's but marked "clean"), and if we want to have that state, then we should just revert _everything_ to the 2.6.18 situation, and not play these games at all.

Andrew's patch with your patch on top makes me happy, because now we're at least honoring all the basic rules (we don't get into an inconsistent state), so on a local level it all makes sense. HOWEVER, I then don't actually understand how it could ever actually make sense to ask for "please clean the page, but don't actually clean it".

So _I_ think that we should add a honking huge WARN_ON() for this case. Ie, do your patch, but instead of re-dirtying the page:

+ if (!must_clean_ptes && cleaned)
+ set_page_dirty(page);

we would do

+ if (!must_clean_ptes && cleaned) {
+ WARN_ON(1);
+ set_page_dirty(page);
+ }

and ask the people who see this problem to see if they get the WARN_ON() (assuming it _fixes_ their data corruption).

Because whoever calls "clean_dirty_page()" without actually wanting to clean the PTE's is really a bug: those dirty PTE's had better not exist.

Or maybe the WARN_ON() just points out _why_ somebody would want to do something this insane. Right now I just can't see why it's a valid thing to do.

Maybe I'm still confused.

Linus

#53 lore raw
Quoted 5 lines · Linus Torvalds

Andrei,
could you try Peter's patch (on top of Andrew's patch - it depends on
it, and wouldn't work on an unmodified -git kernel, but add the WARN_ON() I mention in this email? You seem to be able to reproduce this easily.. Thanks)

I finally beat yum into submission and I hope to have rtorrent compiled shortly.

Quoted 7 lines · Peter Zijlstra
Quoted 5 lines

This should be safe; page_mkclean walks the rmap and flips the pte's under the pte lock and records the dirty state while iterating. Concurrent faults will either do set_page_dirty() before we get around to doing it or vice versa, but dirty state is not lost.

Quoted 5 lines

Ok, I really liked this patch, but the more I thought about it, the more I started to doubt the reasons for liking it.

I think we have some core fundamental problem here that this patch is needed at all.

I agree, but I suspect this is like the buffered write deadlock Nick is working on, in that it will require some proper filesystem surgery to get right. Having the kernel working in the meantime has my preference ;-)

Quoted 6 lines

So let's think about this: we apparently have two cases of "clear_page_dirty()":

- the one that really wants to clear the bit unconditionally (Andrew calls this the "must_clean_ptes" case, which I personally find to be a really confusing name, but whatever)

I'm probably worse with names so I'm not even going to try and fix that.

Quoted 8 lines

- the other case. The case that doesn't want to really clear the pte dirty bits.

and I thought your patch made sense, because it saved away the pte state in the page dirty state, and that matches my mental model, but the more I think about it, the less sense that whole "the other case" situation makes AT ALL.

Quoted 5 lines

Why does "the other case" exist at all? If you want to clear the dirty page flag, what is _ever_ the reason for not wanting to drop PTE dirty information? In other words, what possible reason can there ever be for saying "I want this page to be clean", while at the same time saying "but if it was dirty in the page tables, don't forget about that state".

I have tried to get my head around this, and have so far failed. Andrews mail with the patch (great-grandparent to this mail) was the one that made most sense explaining it afaics.

Quoted 12 lines

So I absolutely detested Andrew's original patch, because that one made zero sense at all even from a code standpoint. With your patch on top, it all suddenly makes sense: at least you don't just leave dirty pages in the PTE's with a "struct page" that is marked clean, and the end result is undeniably at least _consistent_.

So Andrew's patch I can't stand, because the whole point of it seems to be to leave the system in an inconsistent state (dirty in the pte's but marked "clean"), and if we want to have that state, then we should just revert _everything_ to the 2.6.18 situation, and not play these games at all.

Quoted 5 lines

Andrew's patch with your patch on top makes me happy, because now we're at least honoring all the basic rules (we don't get into an inconsistent state), so on a local level it all makes sense. HOWEVER, I then don't actually understand how it could ever actually make sense to ask for "please clean the page, but don't actually clean it".

Somehow it looses track of actual page content dirtyness when it does the page buffer game.

Is this because page buffers are used to do sub-page sized writes without RMW cycles?

Cannot this case be avoided when the page is mapped, because at that point the whole page will be resident anyway.

Quoted 16 lines

So _I_ think that we should add a honking huge WARN_ON() for this case. Ie, do your patch, but instead of re-dirtying the page:

mm/page-writeback.c +2 -0
+                       if (!must_clean_ptes && cleaned)
+                               set_page_dirty(page);

we would do

(quoted diff) +4 -0
+                       if (!must_clean_ptes && cleaned) {
+                               WARN_ON(1);
+                               set_page_dirty(page);
+                       }

and ask the people who see this problem to see if they get the WARN_ON() (assuming it _fixes_ their data corruption).

Quoted 6 lines

Because whoever calls "clean_dirty_page()" without actually wanting to clean the PTE's is really a bug: those dirty PTE's had better not exist.

Or maybe the WARN_ON() just points out _why_ somebody would want to do something this insane. Right now I just can't see why it's a valid thing to do.

Maybe, but I think Nick's mail here:
http://lkml.org/lkml/2006/12/18/59

shows a trace like that. I'm guessing that if we do the WARN_ON() some folks might get a lot of output, perhaps WARN_ON_ONCE() ?

#54 lore raw
Quoted 9 lines · Peter Zijlstra
Quoted 4 lines

Or maybe the WARN_ON() just points out _why_ somebody would want to do something this insane. Right now I just can't see why it's a valid thing to do.

Maybe, but I think Nick's mail here:
http://lkml.org/lkml/2006/12/18/59

shows a trace like that.

Sure, but I actually think that "try_to_free_buffers()" was buggy in the first place, shouldn't have done what it did at all (it has NO business clearing dirty data), and should be fixed with my other simple and clean patch that just removes the crap.

But sadly, Andrei said that he still saw data corruption, which implies that the problem had nothing to do with "try_to_free_buffers()" at all.

(On that note: Andrei - if you do test this out, I'd suggest applying my patch too - the one that you already tested. It won't apply cleanly on top of Andrew's patch, but it should be trivial to apply by hand, since you really just want to remove the whole "if (ret) {...}" sequence. I realize that it didn't make any difference for you, but applying that patch is probably a good idea just to remove the noise for a codepath that you already showed to not matter)

Quoted 2 lines

I'm guessing that if we do the WARN_ON() some folks might get a lot of output, perhaps WARN_ON_ONCE() ?

Well, I'd rather get lots of noise to see all the paths that can cause this. We've been concentrating mainly on one (try_to_free_buffers()), but that one was already shown not to matter or at least not to be the _whole_ issue, so..

Linus

#55 lore raw
Quoted 8 lines

(On that note: Andrei - if you do test this out, I'd suggest applying my patch too - the one that you already tested. It won't apply cleanly on top of Andrew's patch, but it should be trivial to apply by hand, since you really just want to remove the whole "if (ret) {...}" sequence. I realize that it didn't make any difference for you, but applying that patch is probably a good idea just to remove the noise for a codepath that you already showed to not matter)

I applied Linus patch, Andrew patch, Peter Zijlstra patches(the last two). All unified patch is attached. I tested and I have no corruption.

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..760442f 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 1)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..7b87875 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 1);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..47a6b62 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..d65ba84 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..561d702 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -866,7 +866,9 @@ int test_clear_page_dirty(struct page *p
* page is locked, which pins the address_space */
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned)
+ set_page_dirty(page);
dec_zone_page_state(page, NR_FILE_DIRTY);
}
return 1;

mm/rmap.c +1 -1
index d8a842a..3f9061e 100644
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/*entry = pte_mkclean(entry);*/
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);
mm/truncate.c +2 -2
index 9bfb8e8..cafa843 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 1))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
Quoted 10 lines
Quoted 3 lines

I'm guessing that if we do the WARN_ON() some folks might get a lot of output, perhaps WARN_ON_ONCE() ?

Well, I'd rather get lots of noise to see all the paths that can cause this. We've been concentrating mainly on one (try_to_free_buffers()), but that one was already shown not to matter or at least not to be the _whole_ issue, so..

Linus

#56 lore raw

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 13 lines · Andrei Popa
mm/rmap.c +1 -1
index d8a842a..3f9061e 100644
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/*entry = pte_mkclean(entry);*/
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

please drop this chunk, this will always make the problem go away.

#57 lore raw
In reply to Andrei Popa
Quoted 3 lines · Andrei Popa

I applied Linus patch, Andrew patch, Peter Zijlstra patches(the last two). All unified patch is attached. I tested and I have no corruption.

That wasn't very interesting, because you also had the patch that just disabled "page_mkclean_one()" entirely:

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 13 lines
mm/rmap.c +1 -1
index d8a842a..3f9061e 100644
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/*entry = pte_mkclean(entry);*/
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

The above patch is bad. It's always going to hide the bug, but it hides it by just not doing anything at all. So any patch combination that contains that patch will probably _always_ fix your problem, but it won't be an interesting patch..

So can you remove that small fragment? Also, it would be nice if you added the WARN_ON() to this sequence in mm/page-writeback.c:

+ if (!must_clean_ptes && cleaned)
+ set_page_dirty(page);

just make it do a WARN_ON() if this ever triggers.

Then, IF the corruption is gone, we'd love to see the WARN_ON results..

Linus

#58 lore raw
Quoted 34 lines · Linus Torvalds
Quoted 3 lines · Andrei Popa

I applied Linus patch, Andrew patch, Peter Zijlstra patches(the last two). All unified patch is attached. I tested and I have no corruption.

That wasn't very interesting, because you also had the patch that just disabled "page_mkclean_one()" entirely:

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 13 lines
mm/rmap.c +1 -1
index d8a842a..3f9061e 100644
@@ -448,7 +448,7 @@ static int page_mkclean_one(struct page 
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	/*entry = pte_mkclean(entry);*/
 	entry = pte_wrprotect(entry);
 	ptep_establish(vma, address, pte, entry);
 	lazy_mmu_prot_update(entry);

The above patch is bad. It's always going to hide the bug, but it hides it by just not doing anything at all. So any patch combination that contains that patch will probably _always_ fix your problem, but it won't be an interesting patch..

So can you remove that small fragment? Also, it would be nice if you added the WARN_ON() to this sequence in mm/page-writeback.c:

(quoted diff) +2 -0
+                       if (!must_clean_ptes && cleaned)
+                               set_page_dirty(page);
Quoted 5 lines

just make it do a WARN_ON() if this ever triggers.

Then, IF the corruption is gone, we'd love to see the WARN_ON results..

Linus

I dropped that patch and added WARN_ON(1), the unified patch is attached.

I got corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

In dmesg there is no message from WARN_ON(1), my .config is attached.

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..760442f 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 1)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..7b87875 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 1);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..47a6b62 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..d65ba84 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 1);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..f7e0cc8 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -866,7 +866,12 @@ int test_clear_page_dirty(struct page *p
* page is locked, which pins the address_space */
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
return 1;

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..cafa843 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 1))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);

#
# Automatically generated make config: don't edit # Linux kernel version: 2.6.20-rc1
# Sun Dec 17 01:52:12 2006
#
CONFIG_X86_32=y
CONFIG_GENERIC_TIME=y
CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_SEMAPHORE_SLEEPERS=y
CONFIG_X86=y
CONFIG_MMU=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y
CONFIG_GENERIC_BUG=y
CONFIG_GENERIC_HWEIGHT=y CONFIG_ARCH_MAY_HAVE_PC_FDC=y
CONFIG_DMI=y CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"

#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32

#
# General setup
#
CONFIG_LOCALVERSION=""
# CONFIG_LOCALVERSION_AUTO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
# CONFIG_IPC_NS is not set
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_TASKSTATS is not set
# CONFIG_UTS_NS is not set
# CONFIG_AUDIT is not set
CONFIG_IKCONFIG=y
# CONFIG_IKCONFIG_PROC is not set
# CONFIG_CPUSETS is not set
# CONFIG_SYSFS_DEPRECATED is not set
# CONFIG_RELAY is not set
CONFIG_INITRAMFS_SOURCE=""
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
# CONFIG_KALLSYMS_ALL is not set
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SHMEM=y
CONFIG_SLAB=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
# CONFIG_SLOB is not set

#
# Loadable module support
#
# CONFIG_MODULES is not set
CONFIG_STOP_MACHINE=y

#
# Block layer
#
CONFIG_BLOCK=y
# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
# CONFIG_LSF is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=y
CONFIG_IOSCHED_DEADLINE=y
CONFIG_IOSCHED_CFQ=y
# CONFIG_DEFAULT_AS is not set
# CONFIG_DEFAULT_DEADLINE is not set
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"

#
# Processor type and features
#
CONFIG_SMP=y
CONFIG_X86_PC=y
# CONFIG_X86_ELAN is not set
# CONFIG_X86_VOYAGER is not set
# CONFIG_X86_NUMAQ is not set
# CONFIG_X86_SUMMIT is not set
# CONFIG_X86_BIGSMP is not set
# CONFIG_X86_VISWS is not set
# CONFIG_X86_GENERICARCH is not set
# CONFIG_X86_ES7000 is not set
# CONFIG_PARAVIRT is not set
# CONFIG_M386 is not set
# CONFIG_M486 is not set
# CONFIG_M586 is not set
# CONFIG_M586TSC is not set
# CONFIG_M586MMX is not set
# CONFIG_M686 is not set
# CONFIG_MPENTIUMII is not set
# CONFIG_MPENTIUMIII is not set
CONFIG_MPENTIUMM=y
# CONFIG_MCORE2 is not set
# CONFIG_MPENTIUM4 is not set
# CONFIG_MK6 is not set
# CONFIG_MK7 is not set
# CONFIG_MK8 is not set
# CONFIG_MCRUSOE is not set
# CONFIG_MEFFICEON is not set
# CONFIG_MWINCHIPC6 is not set
# CONFIG_MWINCHIP2 is not set
# CONFIG_MWINCHIP3D is not set
# CONFIG_MGEODEGX1 is not set
# CONFIG_MGEODE_LX is not set
# CONFIG_MCYRIXIII is not set
# CONFIG_MVIAC3_2 is not set
# CONFIG_X86_GENERIC is not set
CONFIG_X86_CMPXCHG=y
CONFIG_X86_XADD=y
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_RWSEM_XCHGADD_ALGORITHM=y
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_X86_WP_WORKS_OK=y
CONFIG_X86_INVLPG=y
CONFIG_X86_BSWAP=y
CONFIG_X86_POPAD_OK=y
CONFIG_X86_CMPXCHG64=y
CONFIG_X86_GOOD_APIC=y
CONFIG_X86_INTEL_USERCOPY=y
CONFIG_X86_USE_PPRO_CHECKSUM=y
CONFIG_X86_TSC=y
CONFIG_HPET_TIMER=y
CONFIG_HPET_EMULATE_RTC=y
CONFIG_NR_CPUS=8
# CONFIG_SCHED_SMT is not set
CONFIG_SCHED_MC=y
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
CONFIG_PREEMPT_BKL=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_X86_IO_APIC=y
CONFIG_X86_MCE=y
CONFIG_X86_MCE_NONFATAL=y
CONFIG_X86_MCE_P4THERMAL=y
CONFIG_VM86=y
# CONFIG_TOSHIBA is not set
# CONFIG_I8K is not set
# CONFIG_X86_REBOOTFIXUPS is not set
# CONFIG_MICROCODE is not set
# CONFIG_X86_MSR is not set
# CONFIG_X86_CPUID is not set

#
# Firmware Drivers
#
# CONFIG_EDD is not set
# CONFIG_DELL_RBU is not set
# CONFIG_DCDBAS is not set
# CONFIG_NOHIGHMEM is not set
CONFIG_HIGHMEM4G=y
# CONFIG_HIGHMEM64G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_HIGHMEM=y
CONFIG_ARCH_FLATMEM_ENABLE=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
CONFIG_ARCH_POPULATES_NODE_MAP=y
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_SPARSEMEM_STATIC=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_RESOURCES_64BIT is not set
# CONFIG_HIGHPTE is not set
# CONFIG_MATH_EMULATION is not set
CONFIG_MTRR=y
# CONFIG_EFI is not set
CONFIG_IRQBALANCE=y
# CONFIG_SECCOMP is not set
# CONFIG_HZ_100 is not set
# CONFIG_HZ_250 is not set
# CONFIG_HZ_300 is not set
CONFIG_HZ_1000=y
CONFIG_HZ=1000
# CONFIG_KEXEC is not set
# CONFIG_CRASH_DUMP is not set
# CONFIG_RELOCATABLE is not set
CONFIG_PHYSICAL_ALIGN=0x100000
CONFIG_HOTPLUG_CPU=y
# CONFIG_COMPAT_VDSO is not set
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y

#
# Power management options (ACPI, APM)
#
CONFIG_PM=y
# CONFIG_PM_LEGACY is not set
# CONFIG_PM_DEBUG is not set
# CONFIG_PM_SYSFS_DEPRECATED is not set
CONFIG_SOFTWARE_SUSPEND=y
CONFIG_PM_STD_PARTITION=""
CONFIG_SUSPEND_SMP=y

#
# ACPI (Advanced Configuration and Power Interface) Support #
CONFIG_ACPI=y
CONFIG_ACPI_SLEEP=y
CONFIG_ACPI_SLEEP_PROC_FS=y
# CONFIG_ACPI_SLEEP_PROC_SLEEP is not set CONFIG_ACPI_AC=y
CONFIG_ACPI_BATTERY=y
CONFIG_ACPI_BUTTON=y
CONFIG_ACPI_VIDEO=y
CONFIG_ACPI_HOTKEY=y
CONFIG_ACPI_FAN=y
# CONFIG_ACPI_DOCK is not set CONFIG_ACPI_PROCESSOR=y
CONFIG_ACPI_HOTPLUG_CPU=y
CONFIG_ACPI_THERMAL=y
# CONFIG_ACPI_ASUS is not set
# CONFIG_ACPI_IBM is not set
# CONFIG_ACPI_TOSHIBA is not set
# CONFIG_ACPI_CUSTOM_DSDT is not set CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_SYSTEM=y
CONFIG_X86_PM_TIMER=y
CONFIG_ACPI_CONTAINER=y

#
# APM (Advanced Power Management) BIOS Support
#
# CONFIG_APM is not set

#
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=y
# CONFIG_CPU_FREQ_DEBUG is not set CONFIG_CPU_FREQ_STAT=y
# CONFIG_CPU_FREQ_STAT_DETAILS is not set CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y # CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set CONFIG_CPU_FREQ_GOV_PERFORMANCE=y CONFIG_CPU_FREQ_GOV_POWERSAVE=y CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=y CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y

#
# CPUFreq processor drivers
#
CONFIG_X86_ACPI_CPUFREQ=y
# CONFIG_X86_POWERNOW_K6 is not set
# CONFIG_X86_POWERNOW_K7 is not set
# CONFIG_X86_POWERNOW_K8 is not set
# CONFIG_X86_GX_SUSPMOD is not set
CONFIG_X86_SPEEDSTEP_CENTRINO=y
CONFIG_X86_SPEEDSTEP_CENTRINO_ACPI=y
# CONFIG_X86_SPEEDSTEP_CENTRINO_TABLE is not set
CONFIG_X86_SPEEDSTEP_ICH=y
# CONFIG_X86_SPEEDSTEP_SMI is not set
# CONFIG_X86_P4_CLOCKMOD is not set
# CONFIG_X86_CPUFREQ_NFORCE2 is not set
# CONFIG_X86_LONGRUN is not set
# CONFIG_X86_LONGHAUL is not set

#

# shared options
#
# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
CONFIG_X86_SPEEDSTEP_LIB=y
# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set

#
# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
#
CONFIG_PCI=y
# CONFIG_PCI_GOBIOS is not set
# CONFIG_PCI_GOMMCONFIG is not set
# CONFIG_PCI_GODIRECT is not set
CONFIG_PCI_GOANY=y
CONFIG_PCI_BIOS=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
# CONFIG_PCIEPORTBUS is not set
CONFIG_PCI_MSI=y
# CONFIG_PCI_MULTITHREAD_PROBE is not set
# CONFIG_PCI_DEBUG is not set
# CONFIG_HT_IRQ is not set
CONFIG_ISA_DMA_API=y
# CONFIG_ISA is not set
# CONFIG_MCA is not set
# CONFIG_SCx200 is not set

#
# PCCARD (PCMCIA/CardBus) support
#
# CONFIG_PCCARD is not set

#
# PCI Hotplug Support
#
# CONFIG_HOTPLUG_PCI is not set

#
# Executable file formats
#
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_AOUT=y
CONFIG_BINFMT_MISC=y

#
# Networking
#
CONFIG_NET=y

#
# Networking options
#
# CONFIG_NETDEBUG is not set
CONFIG_PACKET=y
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
# CONFIG_NET_KEY is not set
CONFIG_INET=y
# CONFIG_IP_MULTICAST is not set
# CONFIG_IP_ADVANCED_ROUTER is not set
CONFIG_IP_FIB_HASH=y
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
# CONFIG_INET_DIAG is not set
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
# CONFIG_INET6_XFRM_TUNNEL is not set
# CONFIG_INET6_TUNNEL is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETFILTER is not set

#
# DCCP Configuration (EXPERIMENTAL)
#
# CONFIG_IP_DCCP is not set

#
# SCTP Configuration (EXPERIMENTAL)
#
# CONFIG_IP_SCTP is not set

#
# TIPC Configuration (EXPERIMENTAL)
#
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_BRIDGE is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set

#
# QoS and/or fair queueing
#
# CONFIG_NET_SCHED is not set

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_IRDA is not set
CONFIG_BT=y
CONFIG_BT_L2CAP=y
CONFIG_BT_SCO=y
CONFIG_BT_RFCOMM=y
CONFIG_BT_RFCOMM_TTY=y
CONFIG_BT_BNEP=y
# CONFIG_BT_BNEP_MC_FILTER is not set
# CONFIG_BT_BNEP_PROTO_FILTER is not set
CONFIG_BT_HIDP=y

#
# Bluetooth device drivers
#
CONFIG_BT_HCIUSB=y
# CONFIG_BT_HCIUSB_SCO is not set
# CONFIG_BT_HCIUART is not set
# CONFIG_BT_HCIBCM203X is not set
# CONFIG_BT_HCIBPA10X is not set
# CONFIG_BT_HCIBFUSB is not set
# CONFIG_BT_HCIVHCI is not set
# CONFIG_IEEE80211 is not set
CONFIG_WIRELESS_EXT=y

#
# Device Drivers
#

#
# Generic Driver Options
#
# CONFIG_STANDALONE is not set
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
CONFIG_FW_LOADER=y
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_SYS_HYPERVISOR is not set

#
# Connector - unified userspace <-> kernelspace linker #
# CONFIG_CONNECTOR is not set

#
# Memory Technology Devices (MTD)
#
# CONFIG_MTD is not set

#
# Parallel port support
#
# CONFIG_PARPORT is not set

#
# Plug and Play support
#
CONFIG_PNP=y
# CONFIG_PNP_DEBUG is not set

#
# Protocols
#
CONFIG_PNPACPI=y

#
# Block devices
#
CONFIG_BLK_DEV_FD=y
# CONFIG_BLK_CPQ_DA is not set
# CONFIG_BLK_CPQ_CISS_DA is not set
# CONFIG_BLK_DEV_DAC960 is not set
# CONFIG_BLK_DEV_UMEM is not set
# CONFIG_BLK_DEV_COW_COMMON is not set
CONFIG_BLK_DEV_LOOP=y
# CONFIG_BLK_DEV_CRYPTOLOOP is not set
# CONFIG_BLK_DEV_NBD is not set
# CONFIG_BLK_DEV_SX8 is not set
# CONFIG_BLK_DEV_UB is not set
# CONFIG_BLK_DEV_RAM is not set
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_CDROM_PKTCDVD is not set
# CONFIG_ATA_OVER_ETH is not set

#
# Misc devices
#
# CONFIG_IBM_ASM is not set
# CONFIG_SGI_IOC4 is not set
# CONFIG_TIFM_CORE is not set
# CONFIG_MSI_LAPTOP is not set

#
# ATA/ATAPI/MFM/RLL support
#
CONFIG_IDE=y
CONFIG_BLK_DEV_IDE=y

#
# Please see Documentation/ide.txt for help/info on IDE drives #
# CONFIG_BLK_DEV_IDE_SATA is not set
# CONFIG_BLK_DEV_HD_IDE is not set CONFIG_BLK_DEV_IDEDISK=y
CONFIG_IDEDISK_MULTI_MODE=y
CONFIG_BLK_DEV_IDECD=y
# CONFIG_BLK_DEV_IDETAPE is not set
# CONFIG_BLK_DEV_IDEFLOPPY is not set CONFIG_BLK_DEV_IDESCSI=y
# CONFIG_IDE_TASK_IOCTL is not set

#
# IDE chipset support/bugfixes
#
CONFIG_IDE_GENERIC=y
# CONFIG_BLK_DEV_CMD640 is not set
# CONFIG_BLK_DEV_IDEPNP is not set
CONFIG_BLK_DEV_IDEPCI=y
CONFIG_IDEPCI_SHARE_IRQ=y
# CONFIG_BLK_DEV_OFFBOARD is not set
CONFIG_BLK_DEV_GENERIC=y
# CONFIG_BLK_DEV_OPTI621 is not set
# CONFIG_BLK_DEV_RZ1000 is not set
CONFIG_BLK_DEV_IDEDMA_PCI=y
# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
CONFIG_IDEDMA_PCI_AUTO=y
# CONFIG_IDEDMA_ONLYDISK is not set
# CONFIG_BLK_DEV_AEC62XX is not set
# CONFIG_BLK_DEV_ALI15X3 is not set
# CONFIG_BLK_DEV_AMD74XX is not set
# CONFIG_BLK_DEV_ATIIXP is not set
# CONFIG_BLK_DEV_CMD64X is not set
# CONFIG_BLK_DEV_TRIFLEX is not set
# CONFIG_BLK_DEV_CY82C693 is not set
# CONFIG_BLK_DEV_CS5520 is not set
# CONFIG_BLK_DEV_CS5530 is not set
# CONFIG_BLK_DEV_CS5535 is not set
# CONFIG_BLK_DEV_HPT34X is not set
# CONFIG_BLK_DEV_HPT366 is not set
# CONFIG_BLK_DEV_JMICRON is not set
# CONFIG_BLK_DEV_SC1200 is not set
CONFIG_BLK_DEV_PIIX=y
# CONFIG_BLK_DEV_IT821X is not set
# CONFIG_BLK_DEV_NS87415 is not set
# CONFIG_BLK_DEV_PDC202XX_OLD is not set
# CONFIG_BLK_DEV_PDC202XX_NEW is not set
# CONFIG_BLK_DEV_SVWKS is not set
# CONFIG_BLK_DEV_SIIMAGE is not set
# CONFIG_BLK_DEV_SIS5513 is not set
# CONFIG_BLK_DEV_SLC90E66 is not set
# CONFIG_BLK_DEV_TRM290 is not set
# CONFIG_BLK_DEV_VIA82CXXX is not set
# CONFIG_IDE_ARM is not set
CONFIG_BLK_DEV_IDEDMA=y
# CONFIG_IDEDMA_IVB is not set
CONFIG_IDEDMA_AUTO=y
# CONFIG_BLK_DEV_HD is not set

#
# SCSI device support
#
# CONFIG_RAID_ATTRS is not set
CONFIG_SCSI=y
# CONFIG_SCSI_TGT is not set
# CONFIG_SCSI_NETLINK is not set
CONFIG_SCSI_PROC_FS=y

#
# SCSI support type (disk, tape, CD-ROM)
#
CONFIG_BLK_DEV_SD=y
# CONFIG_CHR_DEV_ST is not set
# CONFIG_CHR_DEV_OSST is not set
CONFIG_BLK_DEV_SR=y
# CONFIG_BLK_DEV_SR_VENDOR is not set
CONFIG_CHR_DEV_SG=y
# CONFIG_CHR_DEV_SCH is not set

#
# Some SCSI devices (e.g. CD jukebox) support multiple LUNs #
CONFIG_SCSI_MULTI_LUN=y
# CONFIG_SCSI_CONSTANTS is not set
# CONFIG_SCSI_LOGGING is not set
# CONFIG_SCSI_SCAN_ASYNC is not set

#
# SCSI Transports
#
# CONFIG_SCSI_SPI_ATTRS is not set
# CONFIG_SCSI_FC_ATTRS is not set
# CONFIG_SCSI_ISCSI_ATTRS is not set
# CONFIG_SCSI_SAS_ATTRS is not set
# CONFIG_SCSI_SAS_LIBSAS is not set

#
# SCSI low-level drivers
#
# CONFIG_ISCSI_TCP is not set
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
# CONFIG_SCSI_AIC94XX is not set
# CONFIG_SCSI_DPT_I2O is not set
# CONFIG_SCSI_ADVANSYS is not set
# CONFIG_SCSI_ARCMSR is not set
# CONFIG_MEGARAID_NEWGEN is not set
# CONFIG_MEGARAID_LEGACY is not set
# CONFIG_MEGARAID_SAS is not set
# CONFIG_SCSI_HPTIOP is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_IPS is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_STEX is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
# CONFIG_SCSI_QLA_FC is not set
# CONFIG_SCSI_QLA_ISCSI is not set
# CONFIG_SCSI_LPFC is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_NSP32 is not set
# CONFIG_SCSI_DEBUG is not set
# CONFIG_SCSI_SRP is not set

#
# Serial ATA (prod) and Parallel ATA (experimental) drivers #
CONFIG_ATA=y
CONFIG_SATA_AHCI=y
# CONFIG_SATA_SVW is not set
CONFIG_ATA_PIIX=y
# CONFIG_SATA_MV is not set
# CONFIG_SATA_NV is not set
# CONFIG_PDC_ADMA is not set
# CONFIG_SATA_QSTOR is not set
# CONFIG_SATA_PROMISE is not set
# CONFIG_SATA_SX4 is not set
# CONFIG_SATA_SIL is not set
# CONFIG_SATA_SIL24 is not set
# CONFIG_SATA_SIS is not set
# CONFIG_SATA_ULI is not set
# CONFIG_SATA_VIA is not set
# CONFIG_SATA_VITESSE is not set CONFIG_SATA_INTEL_COMBINED=y
# CONFIG_PATA_ALI is not set
# CONFIG_PATA_AMD is not set
# CONFIG_PATA_ARTOP is not set
# CONFIG_PATA_ATIIXP is not set
# CONFIG_PATA_CMD64X is not set
# CONFIG_PATA_CS5520 is not set
# CONFIG_PATA_CS5530 is not set
# CONFIG_PATA_CS5535 is not set
# CONFIG_PATA_CYPRESS is not set
# CONFIG_PATA_EFAR is not set
# CONFIG_ATA_GENERIC is not set
# CONFIG_PATA_HPT366 is not set
# CONFIG_PATA_HPT37X is not set
# CONFIG_PATA_HPT3X2N is not set
# CONFIG_PATA_HPT3X3 is not set
# CONFIG_PATA_IT821X is not set
# CONFIG_PATA_JMICRON is not set
# CONFIG_PATA_TRIFLEX is not set
# CONFIG_PATA_MARVELL is not set
# CONFIG_PATA_MPIIX is not set
# CONFIG_PATA_OLDPIIX is not set
# CONFIG_PATA_NETCELL is not set
# CONFIG_PATA_NS87410 is not set
# CONFIG_PATA_OPTI is not set
# CONFIG_PATA_OPTIDMA is not set
# CONFIG_PATA_PDC_OLD is not set
# CONFIG_PATA_RADISYS is not set
# CONFIG_PATA_RZ1000 is not set
# CONFIG_PATA_SC1200 is not set
# CONFIG_PATA_SERVERWORKS is not set
# CONFIG_PATA_PDC2027X is not set
# CONFIG_PATA_SIL680 is not set
# CONFIG_PATA_SIS is not set
# CONFIG_PATA_VIA is not set
# CONFIG_PATA_WINBOND is not set

#
# Multi-device support (RAID and LVM)
#
# CONFIG_MD is not set

#
# Fusion MPT device support
#
# CONFIG_FUSION is not set
# CONFIG_FUSION_SPI is not set
# CONFIG_FUSION_FC is not set
# CONFIG_FUSION_SAS is not set

#
# IEEE 1394 (FireWire) support
#
CONFIG_IEEE1394=y

#
# Subsystem Options
#
# CONFIG_IEEE1394_VERBOSEDEBUG is not set
# CONFIG_IEEE1394_OUI_DB is not set
# CONFIG_IEEE1394_EXTRA_CONFIG_ROMS is not set
# CONFIG_IEEE1394_EXPORT_FULL_API is not set

#
# Device Drivers
#

#
# Texas Instruments PCILynx requires I2C
#
CONFIG_IEEE1394_OHCI1394=y

#
# Protocol Drivers
#
# CONFIG_IEEE1394_VIDEO1394 is not set
CONFIG_IEEE1394_SBP2=y
# CONFIG_IEEE1394_SBP2_PHYS_DMA is not set
# CONFIG_IEEE1394_ETH1394 is not set
# CONFIG_IEEE1394_DV1394 is not set
CONFIG_IEEE1394_RAWIO=y

#
# I2O device support
#
# CONFIG_I2O is not set

#
# Network device support
#
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_NET_SB1000 is not set

#
# ARCnet devices
#
# CONFIG_ARCNET is not set

#
# PHY device support
#
# CONFIG_PHYLIB is not set

#
# Ethernet (10 or 100Mbit)
#
CONFIG_NET_ETHERNET=y
CONFIG_MII=y
# CONFIG_HAPPYMEAL is not set
# CONFIG_SUNGEM is not set
# CONFIG_CASSINI is not set
# CONFIG_NET_VENDOR_3COM is not set

#
# Tulip family network device support
#
# CONFIG_NET_TULIP is not set
# CONFIG_HP100 is not set
CONFIG_NET_PCI=y
# CONFIG_PCNET32 is not set
# CONFIG_AMD8111_ETH is not set
# CONFIG_ADAPTEC_STARFIRE is not set
# CONFIG_B44 is not set
# CONFIG_FORCEDETH is not set
# CONFIG_DGRS is not set
# CONFIG_EEPRO100 is not set
CONFIG_E100=y
# CONFIG_FEALNX is not set
# CONFIG_NATSEMI is not set
# CONFIG_NE2K_PCI is not set
# CONFIG_8139CP is not set
# CONFIG_8139TOO is not set
# CONFIG_SIS900 is not set
# CONFIG_EPIC100 is not set
# CONFIG_SUNDANCE is not set
# CONFIG_TLAN is not set
# CONFIG_VIA_RHINE is not set

#
# Ethernet (1000 Mbit)
#
# CONFIG_ACENIC is not set
# CONFIG_DL2K is not set
# CONFIG_E1000 is not set
# CONFIG_NS83820 is not set
# CONFIG_HAMACHI is not set
# CONFIG_YELLOWFIN is not set
# CONFIG_R8169 is not set
# CONFIG_SIS190 is not set
# CONFIG_SKGE is not set
# CONFIG_SKY2 is not set
# CONFIG_SK98LIN is not set
# CONFIG_VIA_VELOCITY is not set
# CONFIG_TIGON3 is not set
# CONFIG_BNX2 is not set
# CONFIG_QLA3XXX is not set

#
# Ethernet (10000 Mbit)
#
# CONFIG_CHELSIO_T1 is not set
# CONFIG_IXGB is not set
# CONFIG_S2IO is not set
# CONFIG_MYRI10GE is not set
# CONFIG_NETXEN_NIC is not set

#
# Token Ring devices
#
# CONFIG_TR is not set

#
# Wireless LAN (non-hamradio)
#
CONFIG_NET_RADIO=y
# CONFIG_NET_WIRELESS_RTNETLINK is not set

#
# Obsolete Wireless cards support (pre-802.11)
#
# CONFIG_STRIP is not set

#
# Wireless 802.11b ISA/PCI cards support
#
# CONFIG_IPW2100 is not set
# CONFIG_IPW2200 is not set
# CONFIG_AIRO is not set
# CONFIG_HERMES is not set
# CONFIG_ATMEL is not set

#
# Prism GT/Duette 802.11(a/b/g) PCI/Cardbus support #
# CONFIG_PRISM54 is not set
# CONFIG_USB_ZD1201 is not set
# CONFIG_HOSTAP is not set CONFIG_NET_WIRELESS=y

#
# Wan interfaces
#
# CONFIG_WAN is not set
# CONFIG_FDDI is not set
# CONFIG_HIPPI is not set
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NET_FC is not set
# CONFIG_SHAPER is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set

#
# ISDN subsystem
#
# CONFIG_ISDN is not set

#
# Telephony Support
#
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
CONFIG_INPUT_MOUSEDEV_PSAUX=y
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1280
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=800
# CONFIG_INPUT_JOYDEV is not set
# CONFIG_INPUT_TSDEV is not set
# CONFIG_INPUT_EVDEV is not set
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
CONFIG_KEYBOARD_ATKBD=y
# CONFIG_KEYBOARD_SUNKBD is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
CONFIG_INPUT_MOUSE=y
CONFIG_MOUSE_PS2=y
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TOUCHSCREEN is not set
CONFIG_INPUT_MISC=y
# CONFIG_INPUT_PCSPKR is not set
CONFIG_INPUT_WISTRON_BTNS=y
# CONFIG_INPUT_UINPUT is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_I8042=y
# CONFIG_SERIO_SERPORT is not set
# CONFIG_SERIO_CT82C710 is not set
# CONFIG_SERIO_PCIPS2 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_GAMEPORT is not set

#
# Character devices
#
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
# CONFIG_SERIAL_NONSTANDARD is not set

#
# Serial drivers
#
# CONFIG_SERIAL_8250 is not set

#
# Non-8250 serial port support
#
# CONFIG_SERIAL_JSM is not set
CONFIG_UNIX98_PTYS=y
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=256

#
# IPMI
#
# CONFIG_IPMI_HANDLER is not set

#
# Watchdog Cards
#
# CONFIG_WATCHDOG is not set
CONFIG_HW_RANDOM=y
CONFIG_HW_RANDOM_INTEL=y
# CONFIG_HW_RANDOM_AMD is not set
# CONFIG_HW_RANDOM_GEODE is not set
# CONFIG_HW_RANDOM_VIA is not set
CONFIG_NVRAM=y
CONFIG_RTC=y
# CONFIG_DTLK is not set
# CONFIG_R3964 is not set
# CONFIG_APPLICOM is not set
# CONFIG_SONYPI is not set
CONFIG_AGP=y
# CONFIG_AGP_ALI is not set
# CONFIG_AGP_ATI is not set
# CONFIG_AGP_AMD is not set
# CONFIG_AGP_AMD64 is not set
CONFIG_AGP_INTEL=y
# CONFIG_AGP_NVIDIA is not set
# CONFIG_AGP_SIS is not set
# CONFIG_AGP_SWORKS is not set
# CONFIG_AGP_VIA is not set
# CONFIG_AGP_EFFICEON is not set
CONFIG_DRM=y
# CONFIG_DRM_TDFX is not set
# CONFIG_DRM_R128 is not set
# CONFIG_DRM_RADEON is not set
# CONFIG_DRM_I810 is not set
# CONFIG_DRM_I830 is not set
CONFIG_DRM_I915=y
# CONFIG_DRM_MGA is not set
# CONFIG_DRM_SIS is not set
# CONFIG_DRM_VIA is not set
# CONFIG_DRM_SAVAGE is not set
# CONFIG_MWAVE is not set
# CONFIG_PC8736x_GPIO is not set
# CONFIG_NSC_GPIO is not set
# CONFIG_CS5535_GPIO is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_HPET is not set
# CONFIG_HANGCHECK_TIMER is not set

#
# TPM devices
#
# CONFIG_TCG_TPM is not set
# CONFIG_TELCLOCK is not set

#
# I2C support
#
# CONFIG_I2C is not set

#
# SPI support
#
# CONFIG_SPI is not set
# CONFIG_SPI_MASTER is not set

#
# Dallas's 1-wire bus
#
# CONFIG_W1 is not set

#
# Hardware Monitoring support
#
# CONFIG_HWMON is not set
# CONFIG_HWMON_VID is not set

#
# Multimedia devices
#
# CONFIG_VIDEO_DEV is not set

#
# Digital Video Broadcasting Devices
#
# CONFIG_DVB is not set
# CONFIG_USB_DABUSB is not set

#
# Graphics support
#
# CONFIG_FIRMWARE_EDID is not set
CONFIG_FB=y
CONFIG_FB_CFB_FILLRECT=y
CONFIG_FB_CFB_COPYAREA=y
CONFIG_FB_CFB_IMAGEBLIT=y
# CONFIG_FB_MACMODES is not set
# CONFIG_FB_BACKLIGHT is not set
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set
# CONFIG_FB_CIRRUS is not set
# CONFIG_FB_PM2 is not set
# CONFIG_FB_CYBER2000 is not set
# CONFIG_FB_ARC is not set
# CONFIG_FB_ASILIANT is not set
# CONFIG_FB_IMSTT is not set
# CONFIG_FB_VGA16 is not set
CONFIG_FB_VESA=y
# CONFIG_FB_HGA is not set
# CONFIG_FB_S1D13XXX is not set
# CONFIG_FB_NVIDIA is not set
# CONFIG_FB_RIVA is not set
CONFIG_FB_I810=y
CONFIG_FB_I810_GTF=y
# CONFIG_FB_I810_I2C is not set
CONFIG_FB_INTEL=y
# CONFIG_FB_INTEL_DEBUG is not set
# CONFIG_FB_INTEL_I2C is not set
# CONFIG_FB_MATROX is not set
# CONFIG_FB_RADEON is not set
# CONFIG_FB_ATY128 is not set
# CONFIG_FB_ATY is not set
# CONFIG_FB_SAVAGE is not set
# CONFIG_FB_SIS is not set
# CONFIG_FB_NEOMAGIC is not set
# CONFIG_FB_KYRO is not set
# CONFIG_FB_3DFX is not set
# CONFIG_FB_VOODOO1 is not set
# CONFIG_FB_CYBLA is not set
# CONFIG_FB_TRIDENT is not set
# CONFIG_FB_GEODE is not set
# CONFIG_FB_VIRTUAL is not set

#
# Console display driver support
#
CONFIG_VGA_CONSOLE=y
# CONFIG_VGACON_SOFT_SCROLLBACK is not set
CONFIG_VIDEO_SELECT=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
# CONFIG_FONTS is not set
CONFIG_FONT_8x8=y
CONFIG_FONT_8x16=y

#
# Logo configuration
#
# CONFIG_LOGO is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_DEVICE=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_DEVICE=y

#
# Sound
#
CONFIG_SOUND=y

#
# Advanced Linux Sound Architecture
#
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_SEQUENCER=y
# CONFIG_SND_SEQ_DUMMY is not set
# CONFIG_SND_MIXER_OSS is not set
# CONFIG_SND_PCM_OSS is not set
# CONFIG_SND_SEQUENCER_OSS is not set
CONFIG_SND_RTCTIMER=y
CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y
# CONFIG_SND_DYNAMIC_MINORS is not set
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set

#
# Generic devices
#
CONFIG_SND_AC97_CODEC=y
# CONFIG_SND_DUMMY is not set
# CONFIG_SND_VIRMIDI is not set
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_SERIAL_U16550 is not set
# CONFIG_SND_MPU401 is not set

#
# PCI devices
#
# CONFIG_SND_AD1889 is not set
# CONFIG_SND_ALS300 is not set
# CONFIG_SND_ALS4000 is not set
# CONFIG_SND_ALI5451 is not set
# CONFIG_SND_ATIIXP is not set
# CONFIG_SND_ATIIXP_MODEM is not set
# CONFIG_SND_AU8810 is not set
# CONFIG_SND_AU8820 is not set
# CONFIG_SND_AU8830 is not set
# CONFIG_SND_AZT3328 is not set
# CONFIG_SND_BT87X is not set
# CONFIG_SND_CA0106 is not set
# CONFIG_SND_CMIPCI is not set
# CONFIG_SND_CS4281 is not set
# CONFIG_SND_CS46XX is not set
# CONFIG_SND_CS5535AUDIO is not set
# CONFIG_SND_DARLA20 is not set
# CONFIG_SND_GINA20 is not set
# CONFIG_SND_LAYLA20 is not set
# CONFIG_SND_DARLA24 is not set
# CONFIG_SND_GINA24 is not set
# CONFIG_SND_LAYLA24 is not set
# CONFIG_SND_MONA is not set
# CONFIG_SND_MIA is not set
# CONFIG_SND_ECHO3G is not set
# CONFIG_SND_INDIGO is not set
# CONFIG_SND_INDIGOIO is not set
# CONFIG_SND_INDIGODJ is not set
# CONFIG_SND_EMU10K1 is not set
# CONFIG_SND_EMU10K1X is not set
# CONFIG_SND_ENS1370 is not set
# CONFIG_SND_ENS1371 is not set
# CONFIG_SND_ES1938 is not set
# CONFIG_SND_ES1968 is not set
# CONFIG_SND_FM801 is not set
CONFIG_SND_HDA_INTEL=y
# CONFIG_SND_HDSP is not set
# CONFIG_SND_HDSPM is not set
# CONFIG_SND_ICE1712 is not set
# CONFIG_SND_ICE1724 is not set
CONFIG_SND_INTEL8X0=y
CONFIG_SND_INTEL8X0M=y
# CONFIG_SND_KORG1212 is not set
# CONFIG_SND_MAESTRO3 is not set
# CONFIG_SND_MIXART is not set
# CONFIG_SND_NM256 is not set
# CONFIG_SND_PCXHR is not set
# CONFIG_SND_RIPTIDE is not set
# CONFIG_SND_RME32 is not set
# CONFIG_SND_RME96 is not set
# CONFIG_SND_RME9652 is not set
# CONFIG_SND_SONICVIBES is not set
# CONFIG_SND_TRIDENT is not set
# CONFIG_SND_VIA82XX is not set
# CONFIG_SND_VIA82XX_MODEM is not set
# CONFIG_SND_VX222 is not set
# CONFIG_SND_YMFPCI is not set
# CONFIG_SND_AC97_POWER_SAVE is not set

#
# USB devices
#
# CONFIG_SND_USB_AUDIO is not set
# CONFIG_SND_USB_USX2Y is not set

#
# Open Sound System
#
# CONFIG_SOUND_PRIME is not set
CONFIG_AC97_BUS=y

#
# HID Devices
#
# CONFIG_HID is not set

#
# USB support
#
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set

#
# Miscellaneous USB options
#
# CONFIG_USB_DEVICEFS is not set
# CONFIG_USB_BANDWIDTH is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MULTITHREAD_PROBE is not set
# CONFIG_USB_OTG is not set

#
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=y
# CONFIG_USB_EHCI_SPLIT_ISO is not set
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_OHCI_HCD is not set
CONFIG_USB_UHCI_HCD=y
# CONFIG_USB_SL811_HCD is not set

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
# CONFIG_USB_PRINTER is not set

#
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' #

#

# may also be needed; see USB_STORAGE Help for more information
#
CONFIG_USB_STORAGE=y
# CONFIG_USB_STORAGE_DEBUG is not set
# CONFIG_USB_STORAGE_DATAFAB is not set
# CONFIG_USB_STORAGE_FREECOM is not set
# CONFIG_USB_STORAGE_ISD200 is not set
# CONFIG_USB_STORAGE_DPCM is not set
# CONFIG_USB_STORAGE_USBAT is not set
# CONFIG_USB_STORAGE_SDDR09 is not set
# CONFIG_USB_STORAGE_SDDR55 is not set
# CONFIG_USB_STORAGE_JUMPSHOT is not set
# CONFIG_USB_STORAGE_ALAUDA is not set
# CONFIG_USB_STORAGE_KARMA is not set
# CONFIG_USB_LIBUSUAL is not set

#
# USB Input Devices
#

#
# USB HID Boot Protocol drivers
#
# CONFIG_USB_KBD is not set
# CONFIG_USB_MOUSE is not set
# CONFIG_USB_AIPTEK is not set
# CONFIG_USB_WACOM is not set
# CONFIG_USB_ACECAD is not set
# CONFIG_USB_KBTAB is not set
# CONFIG_USB_POWERMATE is not set
# CONFIG_USB_TOUCHSCREEN is not set
# CONFIG_USB_YEALINK is not set
# CONFIG_USB_XPAD is not set
# CONFIG_USB_ATI_REMOTE is not set
# CONFIG_USB_ATI_REMOTE2 is not set
# CONFIG_USB_KEYSPAN_REMOTE is not set
# CONFIG_USB_APPLETOUCH is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set
# CONFIG_USB_MICROTEK is not set

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
# CONFIG_USB_USBNET_MII is not set
# CONFIG_USB_USBNET is not set
# CONFIG_USB_MON is not set

#
# USB port drivers
#

#
# USB Serial Converter support
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
# CONFIG_USB_AUERSWALD is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYPRESS_CY7C63 is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_PHIDGET is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_FTDI_ELAN is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_SISUSBVGA is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set

#
# USB DSL modem support
#

#
# USB Gadget Support
#
# CONFIG_USB_GADGET is not set

#
# MMC/SD Card support
#
# CONFIG_MMC is not set

#
# LED devices
#
# CONFIG_NEW_LEDS is not set

#
# LED drivers
#

#
# LED Triggers
#

#
# InfiniBand support
#
# CONFIG_INFINIBAND is not set

#
# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) #
# CONFIG_EDAC is not set

#
# Real Time Clock
#
# CONFIG_RTC_CLASS is not set

#
# DMA Engine support
#
# CONFIG_DMA_ENGINE is not set

#
# DMA Clients
#

#
# DMA Devices
#

#
# Virtualization
#
# CONFIG_KVM is not set

#
# File systems
#
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_EXT2_FS_XIP is not set
CONFIG_EXT3_FS=y
CONFIG_EXT3_FS_XATTR=y
# CONFIG_EXT3_FS_POSIX_ACL is not set
# CONFIG_EXT3_FS_SECURITY is not set
# CONFIG_EXT4DEV_FS is not set
CONFIG_JBD=y
# CONFIG_JBD_DEBUG is not set
CONFIG_FS_MBCACHE=y
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_FS_POSIX_ACL is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_INOTIFY is not set
# CONFIG_QUOTA is not set
CONFIG_DNOTIFY=y
# CONFIG_AUTOFS_FS is not set
CONFIG_AUTOFS4_FS=y
# CONFIG_FUSE_FS is not set

#
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=y
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
CONFIG_ZISOFS_FS=y
CONFIG_UDF_FS=y
CONFIG_UDF_NLS=y

#
# DOS/FAT/NT Filesystems
#
CONFIG_FAT_FS=y
CONFIG_MSDOS_FS=y
CONFIG_VFAT_FS=y
CONFIG_FAT_DEFAULT_CODEPAGE=437
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
CONFIG_NTFS_FS=y
# CONFIG_NTFS_DEBUG is not set
# CONFIG_NTFS_RW is not set

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_HUGETLB_PAGE is not set
CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set

#
# Miscellaneous filesystems
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_CRAMFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_SYSV_FS is not set
CONFIG_UFS_FS=y
# CONFIG_UFS_FS_WRITE is not set
# CONFIG_UFS_DEBUG is not set

#
# Network File Systems
#
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_SMB_FS is not set
CONFIG_CIFS=y
# CONFIG_CIFS_STATS is not set
# CONFIG_CIFS_WEAK_PW_HASH is not set
# CONFIG_CIFS_XATTR is not set
# CONFIG_CIFS_DEBUG2 is not set
# CONFIG_CIFS_EXPERIMENTAL is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set
# CONFIG_9P_FS is not set

#
# Partition Types
#
CONFIG_PARTITION_ADVANCED=y
# CONFIG_ACORN_PARTITION is not set
# CONFIG_OSF_PARTITION is not set
# CONFIG_AMIGA_PARTITION is not set
# CONFIG_ATARI_PARTITION is not set
# CONFIG_MAC_PARTITION is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_BSD_DISKLABEL=y
# CONFIG_MINIX_SUBPARTITION is not set
# CONFIG_SOLARIS_X86_PARTITION is not set
# CONFIG_UNIXWARE_DISKLABEL is not set
# CONFIG_LDM_PARTITION is not set
# CONFIG_SGI_PARTITION is not set
# CONFIG_ULTRIX_PARTITION is not set
# CONFIG_SUN_PARTITION is not set
# CONFIG_KARMA_PARTITION is not set
# CONFIG_EFI_PARTITION is not set

#
# Native Language Support
#
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
CONFIG_NLS_CODEPAGE_437=y
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
# CONFIG_NLS_ASCII is not set
CONFIG_NLS_ISO8859_1=y
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set

#
# Distributed Lock Manager
#
# CONFIG_DLM is not set

#
# Instrumentation Support
#
# CONFIG_PROFILING is not set

#
# Kernel hacking
#
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
# CONFIG_PRINTK_TIME is not set
# CONFIG_ENABLE_MUST_CHECK is not set
CONFIG_MAGIC_SYSRQ=y
# CONFIG_UNUSED_SYMBOLS is not set
# CONFIG_DEBUG_FS is not set
# CONFIG_HEADERS_CHECK is not set
CONFIG_DEBUG_KERNEL=y
CONFIG_LOG_BUF_SHIFT=14
# CONFIG_DETECT_SOFTLOCKUP is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_PREEMPT is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_RWSEMS is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_KOBJECT is not set
# CONFIG_DEBUG_HIGHMEM is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_LIST is not set
# CONFIG_FRAME_POINTER is not set
# CONFIG_FORCED_INLINING is not set
# CONFIG_RCU_TORTURE_TEST is not set
CONFIG_EARLY_PRINTK=y
# CONFIG_DEBUG_STACKOVERFLOW is not set
# CONFIG_DEBUG_STACK_USAGE is not set

#
# Page alloc debug is incompatible with Software Suspend on i386 #
# CONFIG_DEBUG_RODATA is not set
CONFIG_4KSTACKS=y
CONFIG_X86_FIND_SMP_CONFIG=y
CONFIG_X86_MPPARSE=y
CONFIG_DOUBLEFAULT=y

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY is not set

#
# Cryptographic options
#
CONFIG_CRYPTO=y
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_MANAGER=y
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_MD4 is not set
CONFIG_CRYPTO_MD5=y
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
# CONFIG_CRYPTO_WP512 is not set
# CONFIG_CRYPTO_TGR192 is not set
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_ECB is not set
# CONFIG_CRYPTO_CBC is not set
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_TWOFISH is not set
# CONFIG_CRYPTO_TWOFISH_586 is not set
# CONFIG_CRYPTO_SERPENT is not set
CONFIG_CRYPTO_AES=y
CONFIG_CRYPTO_AES_586=y
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
# CONFIG_CRYPTO_TEA is not set
CONFIG_CRYPTO_ARC4=y
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_ANUBIS is not set
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_MICHAEL_MIC=y
# CONFIG_CRYPTO_CRC32C is not set

#
# Hardware crypto devices
#
# CONFIG_CRYPTO_DEV_PADLOCK is not set
# CONFIG_CRYPTO_DEV_GEODE is not set

#
# Library routines
#
CONFIG_BITREVERSE=y
CONFIG_CRC_CCITT=y
CONFIG_CRC16=y
CONFIG_CRC32=y
CONFIG_LIBCRC32C=y
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_PLIST=y
CONFIG_IOMAP_COPY=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_GENERIC_PENDING_IRQ=y
CONFIG_X86_SMP=y
CONFIG_X86_HT=y
CONFIG_X86_BIOS_REBOOT=y
CONFIG_X86_TRAMPOLINE=y
CONFIG_KTIME_SCALAR=y

#59 lore raw
Quoted 6 lines · Andrei Popa

I dropped that patch and added WARN_ON(1), the unified patch is attached.

I got corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

Ok. That is actually _very_ interesting.

It's interesting because (a) the corruption obviously goes away with the one-liner that effectively disables "page_mkclean_one()".

So that tells us that yes, it's a PTE dirty bit that matters.

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

- there is another caller of page cleaning that should have done the same thing (we could check that by just doing this all _inside_ the page_mkclean() thing)

OR:

- page_mkclean_one() is simply buggy.

And I'm starting to wonder about the second case. But it all LOOKS really fine - I can't see anything wrong there (it uses the extremely conservative "ptep_get_and_clear()", and seems to flush everything right too, through "ptep_establish()").

Linus

#60 lore raw
Quoted 3 lines · Linus Torvalds

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

Forget that. There's a third case, which is much more likely:

- Andrew's patch had a ", 1" where it _should_ have had a ", 0".

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

The only case that _definitely_ would want ",1" is actually the case that already calls page_mkclean() directly: clear_page_dirty_for_io(). So no other ", 1" is valid, and that one that needed it already avoided even calling the "test_clear_page_dirty()" function, because it did it all by hand.

What happens for you in that case?

Linus

#61 lore raw
Quoted 19 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

Forget that. There's a third case, which is much more likely:

- Andrew's patch had a ", 1" where it _should_ have had a ", 0".

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

The only case that _definitely_ would want ",1" is actually the case that already calls page_mkclean() directly: clear_page_dirty_for_io(). So no other ", 1" is valid, and that one that needed it already avoided even calling the "test_clear_page_dirty()" function, because it did it all by hand.

Quoted 3 lines

What happens for you in that case?

Linus

I have file corruption.

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..760442f 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..7b87875 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..47a6b62 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..d65ba84 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..f7e0cc8 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -866,7 +866,12 @@ int test_clear_page_dirty(struct page *p
* page is locked, which pins the address_space */
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
return 1;

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..cafa843 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
#62 lore raw
Quoted 20 lines · Andrei Popa
Quoted 19 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

Forget that. There's a third case, which is much more likely:

- Andrew's patch had a ", 1" where it _should_ have had a ", 0".

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

The only case that _definitely_ would want ",1" is actually the case that already calls page_mkclean() directly: clear_page_dirty_for_io(). So no other ", 1" is valid, and that one that needed it already avoided even calling the "test_clear_page_dirty()" function, because it did it all by hand.

Quoted 5 lines
Quoted 3 lines

What happens for you in that case?

Linus

I have file corruption.

No idea whether this can be a data point or not, but here it goes... my P2P box is about to turn 5 days old while running nonstop one or both of aMule 2.1.3 and BitTorrent 4.4.0 on ext3 mounted w/default options on both IDE and USB disks. Zero corruption.

AMD K7-800, 512MB RAM, PREEMPT/UP kernel,
2.6.19-git20 on top of up-to-date FC6.

--alessandro

"...when I get it, I _get_ it"

(Lara Eidemiller)

#63 lore raw
Quoted 9 lines · Alessandro Suardi

No idea whether this can be a data point or not, but here it goes... my P2P box is about to turn 5 days old while running nonstop one or both of aMule 2.1.3 and BitTorrent 4.4.0 on ext3 mounted w/default options on both IDE and USB disks. Zero corruption.

AMD K7-800, 512MB RAM, PREEMPT/UP kernel,
2.6.19-git20 on top of up-to-date FC6.

It _looks_ like PREEMPT/SMP is one common configuration.

It might also be that the blocksize of the filesystem matters. 4kB filesystems are fundamentally simpler than 1kB filesystems, for example. You can tell at least with "/sbin/dumpe2fs -h /dev/..." or something.

Andrei - one thing that might be interesting to see: when corruption occurs, can you get the corrupted file somehow? And compare it with a known-good copy to see what the corruption looks like?

Linus

#64 lore raw
Quoted 14 lines · Linus Torvalds
Quoted 9 lines · Alessandro Suardi

No idea whether this can be a data point or not, but here it goes... my P2P box is about to turn 5 days old while running nonstop one or both of aMule 2.1.3 and BitTorrent 4.4.0 on ext3 mounted w/default options on both IDE and USB disks. Zero corruption.

AMD K7-800, 512MB RAM, PREEMPT/UP kernel,
2.6.19-git20 on top of up-to-date FC6.

It _looks_ like PREEMPT/SMP is one common configuration.

Quoted 7 lines

It might also be that the blocksize of the filesystem matters. 4kB filesystems are fundamentally simpler than 1kB filesystems, for example. You can tell at least with "/sbin/dumpe2fs -h /dev/..." or something.

Andrei - one thing that might be interesting to see: when corruption occurs, can you get the corrupted file somehow? And compare it with a known-good copy to see what the corruption looks like?

the corrupted file has a chink full with zeros

http://193.226.119.62/corruption0.jpg
http://193.226.119.62/corruption1.jpg

#65 lore raw
Quoted 5 lines · Andrei Popa

the corrupted file has a chink full with zeros

http://193.226.119.62/corruption0.jpg
http://193.226.119.62/corruption1.jpg

Thanks. Yup, filled with zeroes, and the corruption stops (but does _not_ start) at a page boundary.

That _does_ look very much like it was filled in linearly, then written out to disk when it was in the middle of the page, and then we simply lost the further writes that should also have gone on to that page. All consistent with dropping a dirty bit somewhere in the middle of the page updates.

Which we kind of knew must be the issue anyway, but it's good to know that the corruption pattern is consistent with what we're trying to figure out.

Linus

#66 lore raw
In reply to Andrei Popa
Quoted 7 lines · Andrei Popa
Quoted 5 lines

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

What happens for you in that case?

I have file corruption.

Magic. And btw, _thanks_ for being such a great tester.

So now I have one more thng for you to try, it you can bother:

There's exactly two call sites that call "page_mkclean()" (an dthat is the only thing in turn that calls "page_mkclean_one()", which we already determined will cause the corruption).

Both of them do

	if (mapping_cap_account_dirty(mapping)) {
			..

things, although they do slightly different things inside that if in your patched kernel.

Can you just TOTALLY DISABLE that case for the test_clear_page_dirty() case? Just do an "#if 0 .. #endif" around that whole if-statement, leaving the _only_ thing that actually calls "page_mkclean()" to be the "clear_page_dirty_for_io()" call.

Do you still see corruption?

Linus

#67 lore raw
Quoted 26 lines · Linus Torvalds
Quoted 7 lines · Andrei Popa
Quoted 5 lines

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

What happens for you in that case?

I have file corruption.

Magic. And btw, _thanks_ for being such a great tester.

So now I have one more thng for you to try, it you can bother:

There's exactly two call sites that call "page_mkclean()" (an dthat is the only thing in turn that calls "page_mkclean_one()", which we already determined will cause the corruption).

Both of them do

	if (mapping_cap_account_dirty(mapping)) {
			..

things, although they do slightly different things inside that if in your patched kernel.

Quoted 6 lines

Can you just TOTALLY DISABLE that case for the test_clear_page_dirty() case? Just do an "#if 0 .. #endif" around that whole if-statement, leaving the _only_ thing that actually calls "page_mkclean()" to be the "clear_page_dirty_for_io()" call.

Do you still see corruption?

nope, no file corruption at all.

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..2d8bbbb 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..9f82cd0 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..5e29b37 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..44ac434 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..f2a157d 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -857,6 +857,8 @@ int test_clear_page_dirty(struct page *p
return TestClearPageDirty(page);

write_lock_irqsave(&mapping->tree_lock, flags);
+
+#if 0
if (TestClearPageDirty(page)) {
radix_tree_tag_clear(&mapping->page_tree,
page_index(page), PAGECACHE_TAG_DIRTY);
@@ -866,11 +868,19 @@ int test_clear_page_dirty(struct page *p
* page is locked, which pins the address_space */
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
return 1;
}
+
+#endif
+
write_unlock_irqrestore(&mapping->tree_lock, flags); return 0;
}

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..9a01d9e 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
#68 lore raw
Quoted 5 lines · Andrei Popa
Quoted 5 lines

There's exactly two call sites that call "page_mkclean()" (an dthat is the only thing in turn that calls "page_mkclean_one()", which we already determined will cause the corruption).

Quoted 8 lines
Quoted 6 lines

Can you just TOTALLY DISABLE that case for the test_clear_page_dirty() case? Just do an "#if 0 .. #endif" around that whole if-statement, leaving the _only_ thing that actually calls "page_mkclean()" to be the "clear_page_dirty_for_io()" call.

Do you still see corruption?

nope, no file corruption at all.

Ok. That's interesting, but I think you actually #ifdef'ed out too much:

Quoted 13 lines
(quoted diff) +5 -1
+
+#if 0
 	if (TestClearPageDirty(page)) {
 		radix_tree_tag_clear(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
@@ -866,11 +868,19 @@ int test_clear_page_dirty(struct page *p
 		 * page is locked, which pins the address_space
 		 */
 		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
+			int cleaned = page_mkclean(page);
+			if (!must_clean_ptes && cleaned){
+			WARN_ON(1);

Review int test_clear_page_dirty(struct page *p · line 883

Quoted 10 lines
(quoted diff) +6 -0
+			set_page_dirty(page);
+			}
+
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
 		return 1;
 	}
+
+#endif
+

It was really just the _inner_ "if (mapping_cap_account_dirty(.." statement that I meant you should remove.

Can you try that too?

Linus

#69 lore raw
Quoted 46 lines · Linus Torvalds
Quoted 5 lines · Andrei Popa
Quoted 5 lines

There's exactly two call sites that call "page_mkclean()" (an dthat is the only thing in turn that calls "page_mkclean_one()", which we already determined will cause the corruption).

Quoted 8 lines
Quoted 6 lines

Can you just TOTALLY DISABLE that case for the test_clear_page_dirty() case? Just do an "#if 0 .. #endif" around that whole if-statement, leaving the _only_ thing that actually calls "page_mkclean()" to be the "clear_page_dirty_for_io()" call.

Do you still see corruption?

nope, no file corruption at all.

Ok. That's interesting, but I think you actually #ifdef'ed out too much:

Quoted 13 lines
(quoted diff) +5 -1
+
+#if 0
 	if (TestClearPageDirty(page)) {
 		radix_tree_tag_clear(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
@@ -866,11 +868,19 @@ int test_clear_page_dirty(struct page *p
 		 * page is locked, which pins the address_space
 		 */
 		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
+			int cleaned = page_mkclean(page);
+			if (!must_clean_ptes && cleaned){
+			WARN_ON(1);

Review int test_clear_page_dirty(struct page *p · line 883

Quoted 10 lines
(quoted diff) +6 -0
+			set_page_dirty(page);
+			}
+
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
 		return 1;
 	}
+
+#endif
+

It was really just the _inner_ "if (mapping_cap_account_dirty(.." statement that I meant you should remove.

Quoted 1 line

Can you try that too?

I have file corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..2d8bbbb 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..9f82cd0 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..5e29b37 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..44ac434 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..4ff7f90 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -857,6 +857,7 @@ int test_clear_page_dirty(struct page *p
return TestClearPageDirty(page);

write_lock_irqsave(&mapping->tree_lock, flags);
+
if (TestClearPageDirty(page)) {
radix_tree_tag_clear(&mapping->page_tree,
page_index(page), PAGECACHE_TAG_DIRTY);
@@ -865,12 +866,23 @@ int test_clear_page_dirty(struct page *p
* We can continue to use `mapping' here because the
* page is locked, which pins the address_space */
+
+#if 0
+
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
+#endif
+
return 1;
}
+
write_unlock_irqrestore(&mapping->tree_lock, flags); return 0;
}

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..9a01d9e 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
#70 lore raw
Quoted 6 lines · Andrei Popa
Quoted 6 lines
Quoted 2 lines

nope, no file corruption at all.

Ok. That's interesting, but I think you actually #ifdef'ed out too much:

Quoted 7 lines
Quoted 4 lines

It was really just the _inner_ "if (mapping_cap_account_dirty(.." statement that I meant you should remove.

Can you try that too?

I have file corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

Ok, that's interesting.

So it doesn't seem to be the call to page_mkclean() itself that causes corruption. It looks like Peter's hunch that maybe there's some bug in PG_dirty handling _itself_ might be an idea..

And the reason it only started happening now is that it may just have been _hidden_ by the fact that while we kept the dirty bits in the page tables, we'd end up writing the dirty page _despite_ having lost the PG_dirty bit. So if it's some bad interaction between writable mappings and some other part of the system, we just didn't see it earlier, exactly because we had _lots_ of dirty bits, and it was enough that _one_ of them was right.

If you didn't see corruption when you #ifdef'ed out too much of the "test_clean_page_dirty() function (the _whole_ TestClearPageDirty() if-statement), but you get it when you just comment out the stuff that does the page_mkclean(), that's interesting.

I'm left lookin gat the "radix_tree_tag_clear()" in test_clear_page_dirty().

What happens if you only ifdef out that single thing?

The actual page-cleaning functions make sure to only clear the TAG_DIRTY bit _after_ the page has been marked for writeback. Is there some ordering constraint there, perhaps?

I'm really reaching here. I'm trying to see the pattern, and I'm not seeing it. I'm asking you to test things just to get more of a feel for what triggers the failure, than because I actually have any kind of idea of what the heck is going on.

Andrew, Nick, Hugh - any ideas?

Linus

#71 lore raw
Quoted 6 lines · On Mon, 18 Dec 2006 16:57:30 -0800 (PST) Linus Torvalds <[email protected]>…

What happens if you only ifdef out that single thing?

The actual page-cleaning functions make sure to only clear the TAG_DIRTY bit _after_ the page has been marked for writeback. Is there some ordering constraint there, perhaps?

Quoted 6 lines

I'm really reaching here. I'm trying to see the pattern, and I'm not seeing it. I'm asking you to test things just to get more of a feel for what triggers the failure, than because I actually have any kind of idea of what the heck is going on.

Andrew, Nick, Hugh - any ideas?

If all of test_clear_page_dirty() has been commented out then the page will never become clean hence will never fall out of pagecache, so unless Andrei is doing a reboot before checking for corruption, perhaps the underlying data on-disk is incorrect, but we can't see it.

Andrei, how _are_ you running this test? What's the exact sequence of steps?

In particular, are you doing anything which would cause the corrupted file to be evicted from memory, thus forcing a read from disk? Such as unmounting and then remounting the filesystem?

The point of my question is to check that the data is really incorrect on-disk, or whether it is incorrect in pagecache.

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

Thanks.

#72 lore raw
Quoted 14 lines · Andrew Morton
Quoted 11 lines · On Mon, 18 Dec 2006 16:57:30 -0800 (PST) Linus Torvalds <[email protected]>…

What happens if you only ifdef out that single thing?

The actual page-cleaning functions make sure to only clear the TAG_DIRTY bit _after_ the page has been marked for writeback. Is there some ordering constraint there, perhaps?

I'm really reaching here. I'm trying to see the pattern, and I'm not seeing it. I'm asking you to test things just to get more of a feel for what triggers the failure, than because I actually have any kind of idea of what the heck is going on.

Quoted 6 lines
Quoted 1 line

Andrew, Nick, Hugh - any ideas?

If all of test_clear_page_dirty() has been commented out then the page will never become clean hence will never fall out of pagecache, so unless Andrei is doing a reboot before checking for corruption, perhaps the underlying data on-disk is incorrect, but we can't see it.

if I do a sync and echo 1 > /proc/sys/vm/drop_caches does the reboot is still necesary ?

Quoted 6 lines

Andrei, how _are_ you running this test? What's the exact sequence of steps?

In particular, are you doing anything which would cause the corrupted file to be evicted from memory, thus forcing a read from disk? Such as unmounting and then remounting the filesystem?

I boot linux, I start rtorrent and start the download, while it's downloading I start evolution and i check my mail(my mbox is very large, several hundered megabytes), I close evolution(I use evolution just to have another application witch uses the filesystem and the memory), I start evolution again. I start firefox. The download is complete. Rtorrent says if the hash is good or not. I do a "unrar t qwe.rar" to test that all 84 downloaded rar files are ok and see the result.

Quoted 7 lines

The point of my question is to check that the data is really incorrect on-disk, or whether it is incorrect in pagecache.

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

I will test.

Quoted 2 lines

Thanks.

#73 lore raw
Quoted 15 lines · On Tue, 19 Dec 2006 03:44:51 +0200 Andrei Popa <[email protected]> wrote:
Quoted 14 lines · Andrew Morton
Quoted 11 lines · On Mon, 18 Dec 2006 16:57:30 -0800 (PST) Linus Torvalds <[email protected]>…

What happens if you only ifdef out that single thing?

The actual page-cleaning functions make sure to only clear the TAG_DIRTY bit _after_ the page has been marked for writeback. Is there some ordering constraint there, perhaps?

I'm really reaching here. I'm trying to see the pattern, and I'm not seeing it. I'm asking you to test things just to get more of a feel for what triggers the failure, than because I actually have any kind of idea of what the heck is going on.

Quoted 8 lines
Quoted 6 lines
Quoted 1 line

Andrew, Nick, Hugh - any ideas?

If all of test_clear_page_dirty() has been commented out then the page will never become clean hence will never fall out of pagecache, so unless Andrei is doing a reboot before checking for corruption, perhaps the underlying data on-disk is incorrect, but we can't see it.

if I do a sync and echo 1 > /proc/sys/vm/drop_caches

OK, that works.

Quoted 2 lines

does the reboot is
still necesary ?

It might be necessary to reboot in this case - if we're leaving the pagecache dirty, writing to drop_caches won't remove it. And you probably won't be able to get a clean reboot either.

Quoted 16 lines
Quoted 6 lines

Andrei, how _are_ you running this test? What's the exact sequence of steps?

In particular, are you doing anything which would cause the corrupted file to be evicted from memory, thus forcing a read from disk? Such as unmounting and then remounting the filesystem?

I boot linux, I start rtorrent and start the download, while it's downloading I start evolution and i check my mail(my mbox is very large, several hundered megabytes), I close evolution(I use evolution just to have another application witch uses the filesystem and the memory), I start evolution again. I start firefox. The download is complete. Rtorrent says if the hash is good or not. I do a "unrar t qwe.rar" to test that all 84 downloaded rar files are ok and see the result.

Quoted 1 line
Quoted 8 lines
Quoted 6 lines

The point of my question is to check that the data is really incorrect on-disk, or whether it is incorrect in pagecache.

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

I will test.

ok, thanks.

#74 lore raw
Quoted 8 lines
Quoted 7 lines
Quoted 5 lines

If all of test_clear_page_dirty() has been commented out then the page will never become clean hence will never fall out of pagecache, so unless Andrei is doing a reboot before checking for corruption, perhaps the underlying data on-disk is incorrect, but we can't see it.

if I do a sync and echo 1 > /proc/sys/vm/drop_caches

Quoted 27 lines

OK, that works.

Quoted 2 lines

does the reboot is
still necesary ?

It might be necessary to reboot in this case - if we're leaving the pagecache dirty, writing to drop_caches won't remove it. And you probably won't be able to get a clean reboot either.

Quoted 18 lines
Quoted 6 lines

Andrei, how _are_ you running this test? What's the exact sequence of steps?

In particular, are you doing anything which would cause the corrupted file to be evicted from memory, thus forcing a read from disk? Such as unmounting and then remounting the filesystem?

I boot linux, I start rtorrent and start the download, while it's downloading I start evolution and i check my mail(my mbox is very large, several hundered megabytes), I close evolution(I use evolution just to have another application witch uses the filesystem and the memory), I start evolution again. I start firefox. The download is complete. Rtorrent says if the hash is good or not. I do a "unrar t qwe.rar" to test that all 84 downloaded rar files are ok and see the result.

Quoted 3 lines

The point of my question is to check that the data is really incorrect on-disk, or whether it is incorrect in pagecache.

I rebooted and the files are still broken after reboot(tested twice) so the data is incorrect on disk.

Quoted 6 lines
Quoted 6 lines
Quoted 4 lines

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

I will test.

Will test In a couple of hours, I have some work to do...

Quoted 2 lines

ok, thanks.

#75 lore raw
In reply to Andrew Morton
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

I fave file corruption.

#76 lore raw
Quoted 5 lines · On Tue, 19 Dec 2006 10:05:03 +0200 Andrei Popa <[email protected]> wrote:
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines

Also, it'd be useful if you could determine whether the bug appears with the ext2 filesystem: do s/ext3/ext2/ in /etc/fstab, or boot with rootfstype=ext2 if it's the root filesystem.

I fave file corruption.

Wow. I didn't expect that, because Mark Haber reported that ext3's data=writeback
fixed it. Maybe he didn't run it for long enough?

#78 lore raw
In reply to Andrew Morton
Quoted 2 lines · Andrew Morton

Wow. I didn't expect that, because Mark Haber reported that ext3's data=writeback
fixed it. Maybe he didn't run it for long enough?

My test case is Debian's "aptitude update" running once an hour, and it was always the same file getting corrupted. With 2.6.19, I had this corruption like every third hour (but -only- if run from cron, running from a shell was always fine), data=writeback made the issue disappear for about two days before I booted into 2.6.19.1 without data=writeback (defaults chosen then), after which the issue only shows up like every other day.

So, I feel like out of the loop since rtorrent seems much better in reproducing this.

I notice, though, that both aptitude and rtorrent do downloads from the net, so there might be a relation to tcp/ip and/or the network driver. My box has a Linksys NC100 network card running with the tulip driver.

Greetings
Marc

Signature · 4 lines
-----------------------------------------------------------------------------
Marc Haber         | "I don't trust Computers. They | Mailadresse im Header
Mannheim, Germany  |  lose things."    Winona Ryder | Fon: *49 621 72739834
Nordisch by Nature |  How to make an American Quilt | Fax: *49 621 72739835
#79 lore raw
In reply to Linus Torvalds
Quoted 34 lines · Linus Torvalds
Quoted 6 lines · Andrei Popa
Quoted 6 lines
Quoted 2 lines

nope, no file corruption at all.

Ok. That's interesting, but I think you actually #ifdef'ed out too much:

Quoted 7 lines
Quoted 4 lines

It was really just the _inner_ "if (mapping_cap_account_dirty(.." statement that I meant you should remove.

Can you try that too?

I have file corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

Ok, that's interesting.

So it doesn't seem to be the call to page_mkclean() itself that causes corruption. It looks like Peter's hunch that maybe there's some bug in PG_dirty handling _itself_ might be an idea..

And the reason it only started happening now is that it may just have been _hidden_ by the fact that while we kept the dirty bits in the page tables, we'd end up writing the dirty page _despite_ having lost the PG_dirty bit. So if it's some bad interaction between writable mappings and some other part of the system, we just didn't see it earlier, exactly because we had _lots_ of dirty bits, and it was enough that _one_ of them was right.

If you didn't see corruption when you #ifdef'ed out too much of the "test_clean_page_dirty() function (the _whole_ TestClearPageDirty() if-statement), but you get it when you just comment out the stuff that does the page_mkclean(), that's interesting.

Quoted 4 lines

I'm left lookin gat the "radix_tree_tag_clear()" in test_clear_page_dirty().

What happens if you only ifdef out that single thing?

I have file corruption.

Quoted 5 lines

The actual page-cleaning functions make sure to only clear the TAG_DIRTY bit _after_ the page has been marked for writeback. Is there some ordering constraint there, perhaps?

Quoted 8 lines

I'm really reaching here. I'm trying to see the pattern, and I'm not seeing it. I'm asking you to test things just to get more of a feel for what triggers the failure, than because I actually have any kind of idea of what the heck is going on.

Andrew, Nick, Hugh - any ideas?

Linus

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..2d8bbbb 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..9f82cd0 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..5e29b37 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..44ac434 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..4ff7f90 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -857,6 +857,7 @@ int test_clear_page_dirty(struct page *p
return TestClearPageDirty(page);

write_lock_irqsave(&mapping->tree_lock, flags);
+
if (TestClearPageDirty(page)) {
radix_tree_tag_clear(&mapping->page_tree,
page_index(page), PAGECACHE_TAG_DIRTY);
@@ -865,12 +866,23 @@ int test_clear_page_dirty(struct page *p
* We can continue to use `mapping' here because the
* page is locked, which pins the address_space */
+
+#if 0
+
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
+#endif
+
return 1;
}
+
write_unlock_irqrestore(&mapping->tree_lock, flags); return 0;
}

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..9a01d9e 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..2d8bbbb 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..9f82cd0 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..5e29b37 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..44ac434 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +2 -2
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int

must_clean_ptes)
{
- test_clear_page_dirty(page);
+ test_clear_page_dirty(page, must_clean_ptes);
}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..e6524a6 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
  * Clear a page's dirty flag, while caring for dirty memory

accounting.
* Returns true if the page was previously dirty.
*/
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
struct address_space *mapping = page_mapping(page); unsigned long flags;
@@ -857,20 +857,35 @@ int test_clear_page_dirty(struct page *p
return TestClearPageDirty(page);

write_lock_irqsave(&mapping->tree_lock, flags);
+
if (TestClearPageDirty(page)) {
+
+#if 0
+
radix_tree_tag_clear(&mapping->page_tree,
page_index(page), PAGECACHE_TAG_DIRTY);
+
+#endif
+
write_unlock_irqrestore(&mapping->tree_lock, flags); /*
* We can continue to use `mapping' here because the
* page is locked, which pins the address_space */
+
+
if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
+ int cleaned = page_mkclean(page);
+ if (!must_clean_ptes && cleaned){
+ WARN_ON(1);
+ set_page_dirty(page);
+ }
+
dec_zone_page_state(page, NR_FILE_DIRTY);
}
return 1;
}
+
write_unlock_irqrestore(&mapping->tree_lock, flags); return 0;
}

mm/rmap.c +0 -0
mm/truncate.c +2 -2
index 9bfb8e8..9a01d9e 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);
#80 lore raw
In reply to Andrei Popa
Quoted 25 lines · Andrei Popa
Quoted 24 lines · Linus Torvalds
Quoted 6 lines · Andrei Popa
Quoted 4 lines

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

What happens for you in that case?

I have file corruption.

Magic. And btw, _thanks_ for being such a great tester.

So now I have one more thng for you to try, it you can bother:

There's exactly two call sites that call "page_mkclean()" (an dthat is the only thing in turn that calls "page_mkclean_one()", which we already determined will cause the corruption).

Both of them do

	if (mapping_cap_account_dirty(mapping)) {
			..

things, although they do slightly different things inside that if in your patched kernel.

Quoted 9 lines
Quoted 6 lines

Can you just TOTALLY DISABLE that case for the test_clear_page_dirty() case? Just do an "#if 0 .. #endif" around that whole if-statement, leaving the _only_ thing that actually calls "page_mkclean()" to be the "clear_page_dirty_for_io()" call.

Do you still see corruption?

nope, no file corruption at all.

Goody I says to nobody in particular, I'll go build this...

Quoted 130 lines
fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}

out:

	if (buffers_to_free) {
		struct buffer_head *bh = buffers_to_free;
fs/cifs/file.c +1 -1
index 0f05cab..2d8bbbb 100644
@@ -1245,7 +1245,7 @@ retry:
 				wait_on_page_writeback(page);
 
 			if (PageWriteback(page) ||
-					!test_clear_page_dirty(page)) {
+					!test_clear_page_dirty(page, 0)) {
 				unlock_page(page);
 				break;
 			}
fs/fuse/file.c +1 -1
index 1387749..da2bdb1 100644
@@ -484,7 +484,7 @@ static int fuse_commit_write(struct file
 		spin_unlock(&fc->lock);
 
 		if (offset == 0 && to == PAGE_CACHE_SIZE) {
-			clear_page_dirty(page);
+			clear_page_dirty(page, 0);
 			SetPageUptodate(page);
 		}
 	}
fs/hugetlbfs/inode.c +1 -1
index ed2c223..9f82cd0 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	clear_page_dirty(page, 0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
fs/jfs/jfs_metapage.c +1 -1
index b1a1c72..5e29b37 100644
@@ -773,7 +773,7 @@ #if MPS_PER_PAGE == 1
 
 	/* Retest mp->count since we may have released page lock */
 	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 		ClearPageUptodate(page);
 	}
 #else
fs/reiserfs/stree.c +1 -1
index 47e7027..a97e198 100644
@@ -1459,7 +1459,7 @@ static void unmap_buffers(struct page *p
 				bh = next;
 			} while (bh != head);
 			if (PAGE_SIZE == bh->b_size) {
-				clear_page_dirty(page);
+				clear_page_dirty(page, 0);
 			}
 		}
 	}
fs/xfs/linux-2.6/xfs_aops.c +1 -1
index b56eb75..44ac434 100644
@@ -343,7 +343,7 @@ xfs_start_page_writeback(
 	ASSERT(!PageWriteback(page));
 	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty(page, 0);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);
include/linux/page-flags.h +0 -0
index 4830a3b..175ab3c 100644
@@ -253,13 +253,13 @@ #define ClearPageUncached(page)	clear_bi
 

struct page; /* forward declaration */

include/linux/page-flags.h +1 -1
-int test_clear_page_dirty(struct page *page);
+int test_clear_page_dirty(struct page *page, int must_clean_ptes);

int test_clear_page_writeback(struct page *page); int test_set_page_writeback(struct page *page);

include/linux/page-flags.h +1 -1
-static inline void clear_page_dirty(struct page *page)
+static inline void clear_page_dirty(struct page *page, int
Quoted 1 line

must_clean_ptes)

above looks wrapped to me so I fixed it to one line

Quoted 12 lines

{

include/linux/page-flags.h +1 -1
-	test_clear_page_dirty(page);
+	test_clear_page_dirty(page, must_clean_ptes);

}

static inline void set_page_writeback(struct page *page)

mm/page-writeback.c +0 -0
index 237107c..f2a157d 100644
@@ -848,7 +848,7 @@ EXPORT_SYMBOL(set_page_dirty_lock);
 * Clear a page's dirty flag, while caring for dirty memory
Quoted 1 line

accounting.

Likewise here, malformed patch otherwise

Quoted 53 lines

* Returns true if the page was previously dirty. */

mm/page-writeback.c +1 -1
-int test_clear_page_dirty(struct page *page)
+int test_clear_page_dirty(struct page *page, int must_clean_ptes)
{
	struct address_space *mapping = page_mapping(page);
	unsigned long flags;
mm/page-writeback.c +11 -1
@@ -857,6 +857,8 @@ int test_clear_page_dirty(struct page *p
 		return TestClearPageDirty(page);
 
 	write_lock_irqsave(&mapping->tree_lock, flags);
+
+#if 0
 	if (TestClearPageDirty(page)) {
 		radix_tree_tag_clear(&mapping->page_tree,
 				page_index(page), PAGECACHE_TAG_DIRTY);
@@ -866,11 +868,19 @@ int test_clear_page_dirty(struct page *p
 		 * page is locked, which pins the address_space
 		 */
 		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
+			int cleaned = page_mkclean(page);
+			if (!must_clean_ptes && cleaned){
+			WARN_ON(1);
+			set_page_dirty(page);
+			}
+
 			dec_zone_page_state(page, NR_FILE_DIRTY);
 		}
 		return 1;
 	}
+
+#endif
+
 	write_unlock_irqrestore(&mapping->tree_lock, flags);
 	return 0;
 }
mm/rmap.c +0 -0
mm/truncate.c +1 -1
index 9bfb8e8..9a01d9e 100644
@@ -70,7 +70,7 @@ truncate_complete_page(struct address_sp
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
+	if (test_clear_page_dirty(page, 0))
 		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);

mm/truncate.c int invalidate_inode_pages2_range(struct · line 392

Quoted 8 lines
mm/truncate.c +1 -1
@@ -386,7 +386,7 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
+			was_dirty = test_clear_page_dirty(page, 0);
 			if (!invalidate_complete_page2(mapping, page)) {
 				if (was_dirty)
 					set_page_dirty(page);

I think I must have screwed the moose. Following along in this thread, I'd patched things back and forth till I figured I'd better do a fresh tree, so starting with the full 2.6.19 tarball, I applied the 2.6.20-rc1 patch, then the above patch, which should be the only thing different from what I'm running right now, which is the commented line in rmap.c, otherwise as it unpacked.

But:
In file included from include/linux/mm.h:230,

                 from include/linux/rmap.h:10,
                 from init/main.c:47:

include/linux/page-flags.h:260: error: expected declaration specifiers or ‘...’ before ‘in’
include/linux/page-flags.h: In function ‘clear_page_dirty’:
include/linux/page-flags.h:262: error: ‘must_clean_ptes’ undeclared (first use in this function)
include/linux/page-flags.h:262: error: (Each undeclared identifier is reported only once
include/linux/page-flags.h:262: error: for each function it appears in.) make[1]: *** [init/main.o] Error 1
make: *** [init] Error 2

There were 2 places where this patch is word wrapped, and this was one of them:

-static inline void clear_page_dirty(struct page *page) +static inline void clear_page_dirty(struct page *page, int must_clean_ptes)

The other one was in a comment, which screwed the patch and needed fixed too. Is it fubared someplace else I missed? Or am I in fact being bitten by this bug?

Signature · 7 lines
Cheers, Gene
"There are four boxes to be used in defense of liberty:
 soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
Yahoo.com and AOL/TW attorneys please note, additions to the above
message by Gene Heskett are:
Copyright 2006 by Maurice Eugene Heskett, all rights reserved.
#81 lore raw
In reply to Linus Torvalds
Quoted 9 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

Forget that. There's a third case, which is much more likely:

- Andrew's patch had a ", 1" where it _should_ have had a ", 0".

Quoted 9 lines

This should be fairly easy to test: just change every single ", 1" case in the patch to ", 0".

The only case that _definitely_ would want ",1" is actually the case that already calls page_mkclean() directly: clear_page_dirty_for_io(). So no other ", 1" is valid, and that one that needed it already avoided even calling the "test_clear_page_dirty()" function, because it did it all by hand.

What about the mm/rmap.c one liner, in or out?

Thanks.

Quoted 8 lines

What happens for you in that case?

Linus
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

Signature · 7 lines
Cheers, Gene
"There are four boxes to be used in defense of liberty:
 soap, ballot, jury, and ammo. Please use in that order."
-Ed Howdershelt (Author)
Yahoo.com and AOL/TW attorneys please note, additions to the above
message by Gene Heskett are:
Copyright 2006 by Maurice Eugene Heskett, all rights reserved.
#82 lore raw
Quoted 2 lines · Gene Heskett

What about the mm/rmap.c one liner, in or out?

The one that just removes the "pte_mkclean()"? That's definitely out, it was just a test-patch to verify that the pte dirty bits seemed to matter at all (and they do).

Linus

#83 lore raw
In reply to Linus Torvalds
Quoted 8 lines · On Mon, 18 Dec 2006 12:14:35 -0800 (PST) Linus Torvalds <[email protected]>…

OR:

- page_mkclean_one() is simply buggy.

And I'm starting to wonder about the second case. But it all LOOKS really fine - I can't see anything wrong there (it uses the extremely conservative "ptep_get_and_clear()", and seems to flush everything right too, through "ptep_establish()").

What does the call to page_check_address() in there do?

It'd be good to have a printk in there to see if it's triggering.

Is this all correct for non-linear VMAs? (rtorrent doesn't use MAP_NONLINEAR though).

#84 lore raw
In reply to Linus Torvalds
Quoted 23 lines · Linus Torvalds
Quoted 6 lines · Andrei Popa

I dropped that patch and added WARN_ON(1), the unified patch is attached.

I got corruption: "Hash check on download completion found bad chunks, consider using "safe_sync"."

Ok. That is actually _very_ interesting.

It's interesting because (a) the corruption obviously goes away with the one-liner that effectively disables "page_mkclean_one()".

So that tells us that yes, it's a PTE dirty bit that matters.

But at the same time, it's interesting that it still happens when we try to re-add the dirty bit. That would tell me that it's one of two cases:

- there is another caller of page cleaning that should have done the same thing (we could check that by just doing this all _inside_ the page_mkclean() thing)

Quoted 8 lines

OR:

- page_mkclean_one() is simply buggy.

And I'm starting to wonder about the second case. But it all LOOKS really fine - I can't see anything wrong there (it uses the extremely conservative "ptep_get_and_clear()", and seems to flush everything right too, through "ptep_establish()").

How about this:

we get confused on what PG_dirty tells us, we fall back to pte_dirty, transfer pte_dirty to PG_dirty and clear pte_dirty. Now it happens again, however we don't have pte_dirty to fall back to anymore.

This would explain why disabling pte_mkclean() does make it go away and non of the other tried approaches works.

We really need a way to sort out PG_dirty, independent of pte_dirty.

#85 lore raw
In reply to Linus Torvalds
Quoted 3 lines · Linus Torvalds

OR:

- page_mkclean_one() is simply buggy.

GOLD!

it seems to work with all this (full diff against current git).

/me rebuilds full kernel to make sure...
reboot...
test... pff the tension...
yay, still good!

Andrei; would you please verify.

The magic seems to be in the extra tlb flush after clearing the dirty bit. Just too bad ptep_clear_flush_dirty() needs ptep not entry.

drivers/connector/connector.c +1 -2
index 5e7cd45..2b8893b 100644
@@ -135,8 +135,7 @@ static int cn_call_callback(struct cn_msg *msg, void (*destruct_data)(void *), v
 	spin_lock_bh(&dev->cbdev->queue_lock);
 	list_for_each_entry(__cbq, &dev->cbdev->queue_list, callback_entry) {
 		if (cn_cb_equal(&__cbq->id.id, &msg->id)) {
-			if (likely(!test_bit(WORK_STRUCT_PENDING,
-					     &__cbq->work.work.management) &&
+			if (likely(!delayed_work_pending(&__cbq->work) &&
 					__cbq->data.ddata == NULL)) {
 				__cbq->data.callback_priv = msg;
 
fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
mm/memory.c +28 -0
index c00bac6..60e0945 100644
@@ -1842,6 +1842,33 @@ void unmap_mapping_range(struct address_space *mapping,
 }
 EXPORT_SYMBOL(unmap_mapping_range);
 
+static void check_last_page(struct address_space *mapping, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+	struct page *page;
+
+	if (!mapping)
+		return;
+	offset = size & ~PAGE_MASK;
+	if (!offset)
+		return;
+	index = size >> PAGE_SHIFT;
+	page = find_lock_page(mapping, index);
+	if (page) {
+		unsigned int check = 0;
+		unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+		do {
+			check += kaddr[offset++];
+		} while (offset < PAGE_SIZE);
+		kunmap_atomic(kaddr, KM_USER0);
+		unlock_page(page);
+		page_cache_release(page);
+		if (check)
+			printk(KERN_ERR "%s: BADNESS: truncate check %u\n", current->comm, check);
+	}
+}
+
 /**
  * vmtruncate - unmap mappings "freed" by truncate() syscall
  * @inode: inode of the file used
@@ -1875,6 +1902,7 @@ do_expand:
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
 		goto out_big;
+	check_last_page(mapping, inode->i_size);
 	i_size_write(inode, offset);
 
 out_truncate:
mm/page-writeback.c +1 -1
index 237107c..f561e72 100644
@@ -957,7 +957,7 @@ int test_set_page_writeback(struct page *page)
 EXPORT_SYMBOL(test_set_page_writeback);
 
 /*
- * Return true if any of the pages in the mapping are marged with the
+ * Return true if any of the pages in the mapping are marked with the
  * passed tag.
  */
 int mapping_tagged(struct address_space *mapping, int tag)
mm/rmap.c +9 -8
index d8a842a..900229a 100644
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *ptep, entry;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -440,22 +440,23 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 	if (address == -EFAULT)
 		goto out;
 
-	pte = page_check_address(page, mm, address, &ptl);
-	if (!pte)
+	ptep = page_check_address(page, mm, address, &ptl);
+	if (!ptep)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
+	if (!pte_dirty(*ptep) && !pte_write(*ptep))
 		goto unlock;
 
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
+	entry = ptep_get_and_clear(mm, address, ptep);
 	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
+	ptep_establish(vma, address, ptep, entry);
+	ret = ptep_clear_flush_dirty(vma, address, ptep) ||
+		page_test_and_clear_dirty(page);
 	lazy_mmu_prot_update(entry);
 	ret = 1;
 
 unlock:
-	pte_unmap_unlock(pte, ptl);
+	pte_unmap_unlock(ptep, ptl);
 out:
 	return ret;
 }
#86 lore raw
Quoted 6 lines · Peter Zijlstra
Quoted 3 lines · Linus Torvalds

OR:

- page_mkclean_one() is simply buggy.

GOLD!

Ok. I was looking at that, and I wondered..

However, if that works, then I _think_ the correct sequence is the following..

The rule should be:
- we flush the tlb _after_ we have cleared it, but _before_ we insert the new entry.

But I dunno. These things are damn subtle. Does this patch fix it for you?

I actually suspect we should do this as an arch-specific macro, and totally replace the current "ptep_clear_flush_dirty()" with one that does "ptep_clear_flush_dirty_and_set_wp()".

Because what I'd _really_ prefer to do on x86 (and probably on most other sane architectures) is to do

- atomically replace the pte with the EXACT SAME ONE, but one that has the writable bit clear.

	bit_clear(_PAGE_BIT_RW, &(ptep)->pte_low);

- flush the TLB, making sure that all CPU's will no longer write to it:

	flush_tlb_page(vma, address);

- finally, just fetch-and-clear the dirty bit (and since it's no longer writable, nobody should be settign it any more)

	ret = bit_clear(__PAGE_BIT_DIRTY, &(ptep)->pte_low);

and now we should be all done.

But the "ptep_get_and_clear() + flush_tlb_page()" sequence should hopefully also work.

Pls test.

Linus

----

mm/rmap.c +2 -1
index d8a842a..eec8706 100644
@@ -448,9 +448,10 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 		goto unlock;
 
 	entry = ptep_get_and_clear(mm, address, pte);
+	flush_tlb_page(vma, address);
 	entry = pte_mkclean(entry);
 	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
+	set_pte_at(mm, address, pte, entry);
 	lazy_mmu_prot_update(entry);
 	ret = 1;
 
#87 lore raw
Quoted 11 lines · Linus Torvalds
Quoted 6 lines · Peter Zijlstra
Quoted 3 lines · Linus Torvalds

OR:

- page_mkclean_one() is simply buggy.

GOLD!

Ok. I was looking at that, and I wondered..

Quoted 8 lines

However, if that works, then I _think_ the correct sequence is the following..

The rule should be:
- we flush the tlb _after_ we have cleared it, but _before_ we insert the new entry.

But I dunno. These things are damn subtle. Does this patch fix it for you?

I will try, but I had a look around the different architectures implementation of ptep_clear_flush_dirty() and saw that not all do the actual flush. So if we go down this road perhaps we should introduce another per arch function that does the potential flush. like flush_tlb_on_clear_dirty() or something like that.

Then we could write:

  entry = ptep_get_and_clear(mm, address, ptep)
  flush_tlb_on_clear_dirty(vma, address);
  entry = pte_mkclean(entry);
  entry = pte_wrprotect(entry);
  set_pte_at(mm, address, ptep, entry);
Quoted 14 lines

I actually suspect we should do this as an arch-specific macro, and totally replace the current "ptep_clear_flush_dirty()" with one that does "ptep_clear_flush_dirty_and_set_wp()".

Because what I'd _really_ prefer to do on x86 (and probably on most other sane architectures) is to do

- atomically replace the pte with the EXACT SAME ONE, but one that has the writable bit clear.

	bit_clear(_PAGE_BIT_RW, &(ptep)->pte_low);

- flush the TLB, making sure that all CPU's will no longer write to it:

Quoted 8 lines
	flush_tlb_page(vma, address);

- finally, just fetch-and-clear the dirty bit (and since it's no longer writable, nobody should be settign it any more)

	ret = bit_clear(__PAGE_BIT_DIRTY, &(ptep)->pte_low);

and now we should be all done.

Hmm, should we not flush after clearing the dirty bit? That is, why does ptep_clear_flush_dirty() need a flush after clearing that bit? does it leak through in the tlb copy?

Also, what is this page_test_and_clear_dirty() business, that seems to be exclusively s390 btw. However they do seem to need this.

Quoted 2 lines

But the "ptep_get_and_clear() + flush_tlb_page()" sequence should hopefully also work.

Yeah, probably, not optimally so on some archs that don't actually need the flush though. And as above, I wonder about s390.

(added our s390 friends to the CC list)

#88 lore raw
Quoted 5 lines · Peter Zijlstra

I will try, but I had a look around the different architectures implementation of ptep_clear_flush_dirty() and saw that not all do the actual flush. So if we go down this road perhaps we should introduce another per arch function that does the potential flush. like flush_tlb_on_clear_dirty() or something like that.

never mind, we do need an unconditional flush for changing the protection too.

#89 lore raw
In reply to Peter Zijlstra
Quoted 4 lines

Hmm, should we not flush after clearing the dirty bit? That is, why does ptep_clear_flush_dirty() need a flush after clearing that bit? does it leak through in the tlb copy?

afaics you need to
1) clear
2) flush
3) check and go to 1) if needed

to be race free.

#90 lore raw

PATCH 1/1 mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

fix page_mkclean_one()

it had several issues:
- it failed to flush the cache
- it failed to flush the tlb
- it failed to do s390 (s390 guys, please verify this is now correct)

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

---
 mm/rmap.c |   29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)
mm/rmap.c +15 -14
Index: linux-2.6/mm/rmap.c
===================================================================
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page 
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *ptep;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -440,22 +440,23 @@ static int page_mkclean_one(struct page 
 	if (address == -EFAULT)
 		goto out;
 
-	pte = page_check_address(page, mm, address, &ptl);
-	if (!pte)
+	ptep = page_check_address(page, mm, address, &ptl);
+	if (!ptep)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
-
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+	while (pte_dirty(*ptep) || pte_write(*ptep)) {
+		pte_t entry = ptep_get_and_clear(mm, address, ptep);
+		flush_cache_page(vma, address, pte_pfn(entry));
+		flush_tlb_page(vma, address);
+		(void)page_test_and_clear_dirty(page); /* do the s390 thing */
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		set_pte_at(vma, address, ptep, entry);
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
-	pte_unmap_unlock(pte, ptl);
+	pte_unmap_unlock(ptep, ptl);
 out:
 	return ret;
 }
#91 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 49 lines · Peter Zijlstra

fix page_mkclean_one()

it had several issues:
- it failed to flush the cache
- it failed to flush the tlb
- it failed to do s390 (s390 guys, please verify this is now correct)

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

---
 mm/rmap.c |   29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

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

mm/rmap.c +5 -12
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page
 {
        struct mm_struct *mm = vma->vm_mm;
        unsigned long address;
-       pte_t *pte, entry;
+       pte_t *ptep;
        spinlock_t *ptl;
        int ret = 0;
 
@@ -440,22 +440,23 @@ static int page_mkclean_one(struct page
        if (address == -EFAULT)
                goto out;
 
-       pte = page_check_address(page, mm, address, &ptl);
-       if (!pte)
+       ptep = page_check_address(page, mm, address, &ptl);
+       if (!ptep)
                goto out;
 
-       if (!pte_dirty(*pte) && !pte_write(*pte))
-               goto unlock;
-
-       entry = ptep_get_and_clear(mm, address, pte);
-       entry = pte_mkclean(entry);
-       entry = pte_wrprotect(entry);
-       ptep_establish(vma, address, pte, entry);
-       lazy_mmu_prot_update(entry);
-       ret = 1;
+       while (pte_dirty(*ptep) || pte_write(*ptep)) {
+               pte_t entry = ptep_get_and_clear(mm, address, ptep);

mm/rmap.c static int page_mkclean_one(struct page · line 458

Quoted 10 lines
mm/rmap.c +9 -0
+               flush_cache_page(vma, address, pte_pfn(entry));
+               flush_tlb_page(vma, address);
+               (void)page_test_and_clear_dirty(page); /* do the s390 thing */
+               entry = pte_wrprotect(entry);
+               entry = pte_mkclean(entry);
+               set_pte_at(vma, address, ptep, entry);
+               lazy_mmu_prot_update(entry);
+               ret = 1;
+       }
 

Having the assignment of "ret = 1;" inside the loop seems a little pointless. Perhaps gcc can optimize it, but still, that assignment really only needs to happen once outside the loop.

Quoted 7 lines
mm/rmap.c +1 -2
-unlock:
-       pte_unmap_unlock(pte, ptl);
+       pte_unmap_unlock(ptep, ptl);
 out:
        return ret;
 }
Signature · 3 lines
Jesper Juhl <[email protected]>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html
#92 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 55 lines · Jesper Juhl
Quoted 54 lines · Peter Zijlstra

fix page_mkclean_one()

it had several issues:
- it failed to flush the cache
- it failed to flush the tlb
- it failed to do s390 (s390 guys, please verify this is now correct)

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

---
 mm/rmap.c |   29 +++++++++++++++--------------
 1 file changed, 15 insertions(+), 14 deletions(-)

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

mm/rmap.c +10 -12
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page
 {
        struct mm_struct *mm = vma->vm_mm;
        unsigned long address;
-       pte_t *pte, entry;
+       pte_t *ptep;
        spinlock_t *ptl;
        int ret = 0;
 
@@ -440,22 +440,23 @@ static int page_mkclean_one(struct page
        if (address == -EFAULT)
                goto out;
 
-       pte = page_check_address(page, mm, address, &ptl);
-       if (!pte)
+       ptep = page_check_address(page, mm, address, &ptl);
+       if (!ptep)
                goto out;
 
-       if (!pte_dirty(*pte) && !pte_write(*pte))
-               goto unlock;
-
-       entry = ptep_get_and_clear(mm, address, pte);
-       entry = pte_mkclean(entry);
-       entry = pte_wrprotect(entry);
-       ptep_establish(vma, address, pte, entry);
-       lazy_mmu_prot_update(entry);
-       ret = 1;
+       while (pte_dirty(*ptep) || pte_write(*ptep)) {
+               pte_t entry = ptep_get_and_clear(mm, address, ptep);
+               flush_cache_page(vma, address, pte_pfn(entry));
+               flush_tlb_page(vma, address);
+               (void)page_test_and_clear_dirty(page); /* do the s390 thing */
+               entry = pte_wrprotect(entry);
+               entry = pte_mkclean(entry);
Quoted 8 lines

mm/rmap.c static int page_mkclean_one(struct page · line 458

Quoted 5 lines
mm/rmap.c +4 -0
+               set_pte_at(vma, address, ptep, entry);
+               lazy_mmu_prot_update(entry);
+               ret = 1;
+       }
 

Having the assignment of "ret = 1;" inside the loop seems a little pointless. Perhaps gcc can optimize it, but still, that assignment really only needs to happen once outside the loop.

Sure, but I was hoping gcc was smart enough. Placing it outside the loop would require an extra if stmt. Also the chance this loop will actually be traversed more than once is _very_ small.

#93 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 9 lines · Peter Zijlstra
Quoted 3 lines · Jesper Juhl

Having the assignment of "ret = 1;" inside the loop seems a little pointless. Perhaps gcc can optimize it, but still, that assignment really only needs to happen once outside the loop.

Sure, but I was hoping gcc was smart enough. Placing it outside the loop would require an extra if stmt. Also the chance this loop will actually be traversed more than once is _very_ small.

allright - I just spotted it and thought I'd point it out :-)

Signature · 3 lines
Jesper Juhl <[email protected]>
Don't top-post  http://www.catb.org/~esr/jargon/html/T/top-post.html
Plain text mails only, please      http://www.expita.com/nomime.html
#94 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

fix page_mkclean_one()

Congratulations on getting to the bottom of it, Peter (if you have:
I haven't digested enough of the thread to tell). I'm mostly offline at present, no time for dialogue, I'll throw out a few remarks and run...

Quoted 3 lines

it had several issues:
- it failed to flush the cache

It's unclear to me why it should need to flush the cache, but I don't know much about that, and mprotect does flush the cache in advance - I think others will tell you that if it does need to be flushed, it must be flushed while there's still a valid pte (on some arches at least).

Quoted 1 line

- it failed to flush the tlb

Eh? It flushed the TLB inside ptep_establish, didn't it? I guess you mean you've found a race before it flushed the TLB.

Quoted 1 line

- it failed to do s390 (s390 guys, please verify this is now correct)

Hmm, I thought we cleared it with them back at the time.

Quoted 2 lines

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

Yikes. Well, please compare with mprotect's change_pte_range. I think I took that as the relevant standard when checking your implementation, and back then satisfied myself that what you were doing was equivalent. If page_mkclean_one is now agreed to be significantly defective, then I suspect change_pte_range is also; perhaps others too.

(But I haven't found time to do more than skim through the thread, I've not thought through the issues at all: I am surprised that it's now found defective, we looked at it long and hard back then.)

And trivial point: please undo those distracting "pte" to "ptep" mods:
if you want to call pte pointers ptep, throughout rmap.c and throughout mm, that's another patch entirely (which I won't welcome, but others may).

Hugh

#95 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 6 lines · Hugh Dickins
Quoted 2 lines · Peter Zijlstra

fix page_mkclean_one()

Congratulations on getting to the bottom of it, Peter (if you have:
I haven't digested enough of the thread to tell).

Well, I thought I understood, you just shattered that.

Quoted 2 lines

I'm mostly offline at
present, no time for dialogue, I'll throw out a few remarks and run...

I wondered where you were ;-) Enjoy your time away from the computer.

Quoted 7 lines
Quoted 3 lines

it had several issues:
- it failed to flush the cache

It's unclear to me why it should need to flush the cache, but I don't know much about that, and mprotect does flush the cache in advance - I think others will tell you that if it does need to be flushed,

I was still thinking about why exactly, but indeed since mprotect does I thought it prudent to also do it.

Quoted 2 lines

it must
be flushed while there's still a valid pte (on some arches at least).

Ah, good point, makes sense I guess.

Quoted 4 lines
Quoted 1 line

- it failed to flush the tlb

Eh? It flushed the TLB inside ptep_establish, didn't it? I guess you mean you've found a race before it flushed the TLB.

Hmm, quite right indeed. I missed that. So moving the flush inside the pte cleared section closed a race. It seems I must have a long hard look at these architecture manuals...

Quoted 3 lines
Quoted 1 line

- it failed to do s390 (s390 guys, please verify this is now correct)

Hmm, I thought we cleared it with them back at the time.

/me queries mail folder...
can't seem to find it.

Quoted 8 lines
Quoted 2 lines

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

Yikes. Well, please compare with mprotect's change_pte_range. I think I took that as the relevant standard when checking your implementation, and back then satisfied myself that what you were doing was equivalent. If page_mkclean_one is now agreed to be significantly defective, then I suspect change_pte_range is also; perhaps others too.

Arjan argued that mprotect and msync would mostly race with themselves in userspace.

Quoted 3 lines

(But I haven't found time to do more than skim through the thread, I've not thought through the issues at all: I am surprised that it's now found defective, we looked at it long and hard back then.)

---

page_mkclean_one() fix

it had several issues:
- it failed to flush the cache
- a race wrt tlb flushing
- it failed to do s390 (s390 guys, please verify this is now correct)

Also, clear in a loop to ensure SMP safeness as suggested by Arjan.

---
 mm/rmap.c |   23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)
mm/rmap.c +13 -10
Index: linux-2.6/mm/rmap.c
===================================================================
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page 
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *pte;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -444,17 +444,20 @@ static int page_mkclean_one(struct page 
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	while (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_get_and_clear(mm, address, pte);
+		flush_tlb_page(vma, address);
+		(void)page_test_and_clear_dirty(page); /* do the s390 thing */
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		set_pte_at(vma, address, pte, entry);
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
#96 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · Peter Zijlstra

page_mkclean_one() fix

This patch doesn't fix my problem (apt segfaults on ARM because its database is corrupted).

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#97 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · Martin Michlmayr
Quoted 1 line · Peter Zijlstra

page_mkclean_one() fix

This patch doesn't fix my problem (apt segfaults on ARM because its database is corrupted).

Can you remind us:
- your ARM is UP, right? Do you have PREEMPT on?
- This is probably a stupid question, but you did make sure that the database was ok (with some rebuild command) and that you didn't have preexisting corruption?

Anyway, the page_mkclean_one() fixes (along with _most_ things we've looked at) shouldn't matter on UP, at least certainly not without PREEMPT.

Linus

#98 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 2 lines · Linus Torvalds

Can you remind us:
- your ARM is UP, right? Do you have PREEMPT on?

It's UP and PREEMPT is not set. I used 2.6.19 plus the patch that has been posted.

Quoted 3 lines

- This is probably a stupid question, but you did make sure that the database was ok (with some rebuild command) and that you didn't have preexisting corruption?

Yes, my test case is to install Debian on the ARM machine so the database is created fresh. While the corruption always triggers during a fresh installation, it's much harder to see in a running system. Some people see it on their system but I haven't found a 100% working recipe to reproduce it yet given a working system; doing a new installation seems to trigger it all the time though.

Quoted 3 lines

Anyway, the page_mkclean_one() fixes (along with _most_ things we've looked at) shouldn't matter on UP, at least certainly not without PREEMPT.

Hmm. So what about UP without PREEMPT then...

Maybe the following information is helpful in some way: remember how I said that we have applied 6 mm patches to 2.6.18 in Debian? According to Gordon Farquharson, who's helping me a great deal with testing installation on this ARM machine (Linksys NSLU2), the corruption doesn't always show up when you only apply mm-tracking-shared-dirty-pages.patch to 2.6.18 but it shows up all the time with all six patches applied. As a reminder, the 6 patches we apply are:

mm-tracking-shared-dirty-pages.patch
mm-balance-dirty-pages.patch
mm-optimize-mprotect.patch
mm-install_page-cleanup.patch
mm-do_wp_page-fixup.patch
mm-msync-cleanup.patch

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#99 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 6 lines · Martin Michlmayr
Quoted 4 lines

Anyway, the page_mkclean_one() fixes (along with _most_ things we've looked at) shouldn't matter on UP, at least certainly not without PREEMPT.

Hmm. So what about UP without PREEMPT then...

So that's why I've been harping on the fact that I think we simply do really wrong things with PG_dirty at times, and that I find it confusing that there's

- clear_page_dirty_for_io(): this one makes sense. The name makes sense, and the implementation makes sense (which is _not_ the same thing as "works", of course - "makes sense" does not mean "no bugs" ;).

- test_clear_page_dirty: this one makes no sense WHATSOEVER, except as a buggy way to do the "_for_io()" case.. This makes sense neither from a concept angle _or_ an implementation angle (the whole "test_" part is
nonsense: why would anybody care? What operation does this? What can it do if the page is dirty? It also has no sensible thing it can do to the page tables.

- clear_page_dirty(): this one makes sense only as a "cancel" operation, for vmtruncate and friends (it's different from the "_for_io()" case in several ways:
(a) we should have unmapped such pages forcibly _anyway_, so looking at the PTE's make no sense.
(b) because we're not starting IO, we don't have the "mark for writeback" case, and we need to clear the dirty tags from the radix trees etc since the writeback logic won't do it for us.
The _implementation_ of "clear_page_dirty()" doesn't make sense, but the concept does.

I've repeated that theory a few times, but neither Andrew nor Nick seem to really believe in it. So I'll just repeat it once more, only to be shot down. I think we have three operations, one of which is totally idiotic and senseless, and one of which is just badly implemented.

Quoted 7 lines

Maybe the following information is helpful in some way: remember how I said that we have applied 6 mm patches to 2.6.18 in Debian? According to Gordon Farquharson, who's helping me a great deal with testing installation on this ARM machine (Linksys NSLU2), the corruption doesn't always show up when you only apply mm-tracking-shared-dirty-pages.patch to 2.6.18 but it shows up all the time with all six patches applied.

I think the "it hapepns occasionally with just the first patch" is the really important part. The other patches really are likely to just change writeback timing behaviour (_especially_ the "tracking-shared-dirty-pages" patch), but if it happens occasionally even with the first one, that's the one that almost certainly introduced the real problem.

And my argument above is actually that the "real problem" goes a hell of a lot further back in time, but it didn't use to be a problem because we just considered dirty bits in the page tables to be something _completely_ independent of the "page dirty" status, so historically, it just didn't matter that we had insane implementations and senseless operations.

Linus

#100 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · Linus Torvalds

So that's why I've been harping on the fact that I think we simply do really wrong things with PG_dirty at times [ ... ]

Ok, I'll just put my money where my mouth is, and suggest a patch like THIS instead.

This one clears up all the issues I find irritating:

- "test_clear_page_dirty()" is insane, both conceptually and as an implementation. "Give me a 'C', give me an 'R', give me an 'A', give me a 'P'".

So rip out that mindfart entirely.

- "clear_page_dirty()" is badly named, and should be about CANCELLING the dirty bit, and must never be called with pages mapped anyway. So throw that out too, and replace it with a new function:

	void cancel_dirty_page(struct page *page, unsigned int accounting_size);

- "clear_page_dirty_for_io()" is fine.

And with that, I then either rip out any old users of "test_clear_page_dirty()" or "clear_page_dirty()", and if appropriate (and it's realy lonly appropriate for "truncate()", I replace them with the new "cancel_dirty_page()". Most of the time, they should just be deleted entirely.

NOTE NOTE NOTE! I _only_ did enough to make things compile for my particular configuration. That means that right now the following filesystems are broken with this patch (because they use the totally broken old crap):

CIFS, FUSE, JFS, ReiserFS, XFS

and I don't know exactly what they need to be fixed. But most likely their usage was insane and pointless anyway (looking at the ReiserFS case, for example, that was DEFINITELY the case. I can't even imagine what the heck it thinks it is doing).

Anyway, I'm not at all guaranteeing that this solves anything at all. I _do_ guarantee that this is a h*ll of a lot saner than what we had before.

[ This also includes a few of my older patches, I didn't bother to sort
them out, and the fs/buffer.c patch is required because it got rid of one of the insane uses of test_clear_page_dirty().

So this goes directly on top of current -git, with no other changes in the tree. ]

Nick, Hugh, Peter, Andrew? Comments?

Martin, Andrei, does this make any difference for your corruption cases?

Linus

---

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/hugetlbfs/inode.c +1 -1
index ed2c223..4f4cd13 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct file *file,
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	cancel_dirty_page(page, /* No IO accounting for huge pages? */0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
include/linux/page-flags.h +2 -6
index 4830a3b..350878a 100644
@@ -253,15 +253,11 @@ static inline void SetPageUptodate(struct page *page)
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+extern void cancel_dirty_page(struct page *page, unsigned int account_size);
+
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
-{
-	test_clear_page_dirty(page);
-}
-
 static inline void set_page_writeback(struct page *page)
 {
 	test_set_page_writeback(page);
mm/memory.c +28 -0
index c00bac6..79cecab 100644
@@ -1842,6 +1842,33 @@ void unmap_mapping_range(struct address_space *mapping,
 }
 EXPORT_SYMBOL(unmap_mapping_range);
 
+static void check_last_page(struct address_space *mapping, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+	struct page *page;
+
+	if (!mapping)
+		return;
+	offset = size & ~PAGE_MASK;
+	if (!offset)
+		return;
+	index = size >> PAGE_SHIFT;
+	page = find_lock_page(mapping, index);
+	if (page) {
+		unsigned int check = 0;
+		unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+		do {
+			check += kaddr[offset++];
+		} while (offset < PAGE_SIZE);
+		kunmap_atomic(kaddr,KM_USER0);
+		unlock_page(page);
+		page_cache_release(page);
+		if (check)
+			printk("%s: BADNESS: truncate check %u\n", current->comm, check);
+	}
+}
+
 /**
  * vmtruncate - unmap mappings "freed" by truncate() syscall
  * @inode: inode of the file used
@@ -1875,6 +1902,7 @@ do_expand:
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
 		goto out_big;
+	check_last_page(mapping, inode->i_size);
 	i_size_write(inode, offset);
 
 out_truncate:
mm/page-writeback.c +0 -32
index 237107c..b3a198c 100644
@@ -845,38 +845,6 @@ int set_page_dirty_lock(struct page *page)
 EXPORT_SYMBOL(set_page_dirty_lock);
 
 /*
- * Clear a page's dirty flag, while caring for dirty memory accounting. 
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
-	struct address_space *mapping = page_mapping(page);
-	unsigned long flags;
-
-	if (!mapping)
-		return TestClearPageDirty(page);
-
-	write_lock_irqsave(&mapping->tree_lock, flags);
-	if (TestClearPageDirty(page)) {
-		radix_tree_tag_clear(&mapping->page_tree,
-				page_index(page), PAGECACHE_TAG_DIRTY);
-		write_unlock_irqrestore(&mapping->tree_lock, flags);
-		/*
-		 * We can continue to use `mapping' here because the
-		 * page is locked, which pins the address_space
-		 */
-		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
-			dec_zone_page_state(page, NR_FILE_DIRTY);
-		}
-		return 1;
-	}
-	write_unlock_irqrestore(&mapping->tree_lock, flags);
-	return 0;
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
-/*
  * Clear a page's dirty flag, while caring for dirty memory accounting.
  * Returns true if the page was previously dirty.
  *
mm/truncate.c +17 -8
index 9bfb8e8..bf9e296 100644
@@ -51,6 +51,20 @@ static inline void truncate_partial_page(struct page *page, unsigned partial)
 		do_invalidatepage(page, partial);
 }
 
+void cancel_dirty_page(struct page *page, unsigned int account_size)
+{
+	/* If we're cancelling the page, it had better not be mapped any more */
+	if (page_mapped(page)) {
+		static unsigned int warncount;
+
+		WARN_ON(++warncount < 5);
+	}
+		
+	if (TestClearPageDirty(page) && account_size)
+		task_io_account_cancelled_write(account_size);
+}
+
+
 /*
  * 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
@@ -70,8 +84,8 @@ truncate_complete_page(struct address_space *mapping, struct page *page)
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
-		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
+
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
 	remove_from_page_cache(page);
@@ -350,7 +364,6 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
 		for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
 			struct page *page = pvec.pages[i];
 			pgoff_t page_index;
-			int was_dirty;
 
 			lock_page(page);
 			if (page->mapping != mapping) {
@@ -386,12 +399,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
-			if (!invalidate_complete_page2(mapping, page)) {
-				if (was_dirty)
-					set_page_dirty(page);
+			if (!invalidate_complete_page2(mapping, page))
 				ret = -EIO;
-			}
 			unlock_page(page);
 		}
 		pagevec_release(&pvec);
#101 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · Linus Torvalds

Nick, Hugh, Peter, Andrew? Comments?

Hooray! I'm all for this cleanup. Let us see where this road leads..

#102 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 11 lines · Linus Torvalds

NOTE NOTE NOTE! I _only_ did enough to make things compile for my particular configuration. That means that right now the following filesystems are broken with this patch (because they use the totally broken old crap):

CIFS, FUSE, JFS, ReiserFS, XFS

and I don't know exactly what they need to be fixed. But most likely their usage was insane and pointless anyway (looking at the ReiserFS case, for example, that was DEFINITELY the case. I can't even imagine what the heck it thinks it is doing).

Here's a patch to get rid of clear_page_dirty() from jfs. I'm not convinced it was totally broken, but I'm not convinced it wasn't. Either way, I don't think that bit of code was particularly beneficial.

Feel free to apply this patch independent of your patch if you really think that jfs's use of clear_page_dirty is crap, or I can push it through -mm first.

This patch removes some questionable code that attempted to make a no-longer-used page easier to reclaim.

Calling metapage_writepage against such a page will not result in any I/O being performed, so removing this code shouldn't be a big deal.

fs/jfs/jfs_metapage.c +1 -14
diff -Nurp linux-orig/fs/jfs/jfs_metapage.c linux/fs/jfs/jfs_metapage.c
@@ -764,22 +764,9 @@ void release_metapage(struct metapage * 
 	} else if (mp->lsn)	/* discard_metapage doesn't remove it */
 		remove_from_logsync(mp);
 
-#if MPS_PER_PAGE == 1
-	/*
-	 * If we know this is the only thing in the page, we can throw
-	 * the page out of the page cache.  If pages are larger, we
-	 * don't want to do this.
-	 */
-
-	/* Retest mp->count since we may have released page lock */
-	if (test_bit(META_discard, &mp->flag) && !mp->count) {
-		clear_page_dirty(page);
-		ClearPageUptodate(page);
-	}
-#else
 	/* Try to keep metapages from using up too much memory */
 	drop_metapage(page, mp);
-#endif
+
 	unlock_page(page);
 	page_cache_release(page);
 }
#103 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · Dave Kleikamp

This patch removes some questionable code that attempted to make a no-longer-used page easier to reclaim.

If so, "cancel_dirty_page()" may actually be the right thing to use, but only if you can guarantee that the page isn't mapped anywhere (and from the name of the function I guess it's not something that you'll ever map?)

So the JFS code _looks_ like you could just replace the

	clear_page_dirty(page);

with

	cancel_dirty_page(page, PAGE_CACHE_SIZE);

(where that second parameter is just used for statistics - it updates the "cancelled IO" byte-counts if CONFIG_TASK_IO_ACCOUNTING is set - so the number doesn't really matter, you could make it zero if you never want the thing to show up in the IO accounting).

Linus

#104 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 9 lines · Linus Torvalds
Quoted 3 lines · Dave Kleikamp

This patch removes some questionable code that attempted to make a no-longer-used page easier to reclaim.

If so, "cancel_dirty_page()" may actually be the right thing to use, but only if you can guarantee that the page isn't mapped anywhere (and from the name of the function I guess it's not something that you'll ever map?)

That's correct. It can't be mapped. It's a private mapping only used for metadata.

I'm really not sure the code in question is having the intended effect. Maybe one of the gurus on cc: can take a look at the code and tell me if it's worth keeping. I apologize in advance if it makes anyone lose their lunch.

Quoted 4 lines

So the JFS code _looks_ like you could just replace the

	clear_page_dirty(page);
Quoted 8 lines

with

	cancel_dirty_page(page, PAGE_CACHE_SIZE);

(where that second parameter is just used for statistics - it updates the "cancelled IO" byte-counts if CONFIG_TASK_IO_ACCOUNTING is set - so the number doesn't really matter, you could make it zero if you never want the thing to show up in the IO accounting).

I'm not sure whether zero or PAGE_CACHE_SIZE would be better. The situation is where some page of metadata is no longer used, say shrinking a directory tree or truncating a file and throwing out the extent tree.

Thanks,
Shaggy

Signature · 2 lines
David Kleikamp
IBM Linux Technology Center
#105 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds

I think this is also needed:

---
 mm/truncate.c |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)
mm/truncate.c +1 -6
Index: linux-2.6/mm/truncate.c
===================================================================
@@ -320,19 +320,14 @@ invalidate_complete_page2(struct address
 	if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL))
 		return 0;
 
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
 	lock_page_ref_irq(page);
-	if (PageDirty(page))
-		goto failed;
-
 	BUG_ON(PagePrivate(page));
 	__remove_from_page_cache(page);
 	unlock_page_ref_irq(page);
 	ClearPageUptodate(page);
 	page_cache_release(page);	/* pagecache ref */
 	return 1;
-failed:
-	unlock_page_ref_irq(page);
-	return 0;
 }
 
 /**
#106 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · Peter Zijlstra

I think this is also needed:

See also:
http://marc.theaimsgroup.com/?l=linux-kernel&m=116603599904278&w=2

Quoted 36 lines
---
 mm/truncate.c |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

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

mm/truncate.c +1 -7
@@ -320,19 +320,14 @@ invalidate_complete_page2(struct address
 	if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL))
 		return 0;
 
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
 	lock_page_ref_irq(page);
-	if (PageDirty(page))
-		goto failed;
-
 	BUG_ON(PagePrivate(page));
 	__remove_from_page_cache(page);
 	unlock_page_ref_irq(page);
 	ClearPageUptodate(page);
 	page_cache_release(page);	/* pagecache ref */
 	return 1;
-failed:
-	unlock_page_ref_irq(page);
-	return 0;
 }
 
 /**
 
 
-

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#107 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 2 lines · Peter Zijlstra

I think this is also needed:

Yeah, that looks about right. Although I think it should go above the "try_to_release_page()", because right now we do that "ttrp()" with the dirty bit set, and we should let the low-level filesystem just know that it's simply not interesting any more (and, indeed, "try_to_free_buffers()" too, for that matter).

Anyway, I think that's a detail. I'd rather know whether this all actually makes any difference what-so-ever to the corruption behaviour of Andrei &co.

Maybe the UP ARM case is some strange dcache alias issue with PIO IDE, and the only reason that started showing up at the same time is the different IO loads. Who knows.

[ Although I think you may have been on the right track with that dcache
flushing stuff in "page_mkclean()".. It might not have been quite all there, but I think we should go back and look very closely at page_mkclean() regardless of any other issues! ]

So far, my whole "cancel_dirty_page/clean_page_dirty_for_io" patch has really been just a "try to make the code _look_ sane. I don't think we have a single report that the patch actually makes any difference yet.

Linus

#108 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 10 lines · Linus Torvalds
Quoted 2 lines · Peter Zijlstra

I think this is also needed:

Yeah, that looks about right. Although I think it should go above the "try_to_release_page()", because right now we do that "ttrp()" with the dirty bit set, and we should let the low-level filesystem just know that it's simply not interesting any more (and, indeed, "try_to_free_buffers()" too, for that matter).

That makes NFS unhappy, see nfs_release_page().

Quoted 3 lines

Anyway, I think that's a detail. I'd rather know whether this all actually makes any difference what-so-ever to the corruption behaviour of Andrei &co.

Yeah, I have to tinker with my test setup to make it fail again. Maybe I have to add more seeds, that seemed to make a difference, it was impossible to trigger with a single seed.

FWIW I also added some scribble past i_size checks in nobh_writepage() and block_write_full_page().

FWIW2 I straced rtorrent for a bit and it does an aweful lot of mmap calls and relatively few msync(MS_ASYNC);munmap(), and no truncate apart from creating sparse files at the beginning.

Quoted 8 lines

Maybe the UP ARM case is some strange dcache alias issue with PIO IDE, and the only reason that started showing up at the same time is the different IO loads. Who knows.

[ Although I think you may have been on the right track with that dcache
flushing stuff in "page_mkclean()".. It might not have been quite all there, but I think we should go back and look very closely at page_mkclean() regardless of any other issues! ]

current version

---
 mm/rmap.c |   23 +++++++++++++----------
 1 file changed, 13 insertions(+), 10 deletions(-)
mm/rmap.c +13 -10
Index: linux-2.6/mm/rmap.c
===================================================================
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page 
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *pte;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page 
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	while (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_clear_flush(vma, address, pte);
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		ptep_establish(vma, address, pte, entry);
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
@@ -489,6 +490,8 @@ int page_mkclean(struct page *page)
 		if (mapping)
 			ret = page_mkclean_file(mapping, page);
 	}
+	if (page_test_and_clear_dirty(page))
+		ret = 1;
 
 	return ret;
 }
Quoted 3 lines

So far, my whole "cancel_dirty_page/clean_page_dirty_for_io" patch has really been just a "try to make the code _look_ sane. I don't think we have a single report that the patch actually makes any difference yet.

I failed to compile a kernel with that patch (100% iowait and a bunch of processes stuck in D state), but sysrq-t was borked (only numbers no symbols) have yet to retry - I noticed you kicked the unwinder?.

#109 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · Peter Zijlstra

current version

Nitpicking ..

Quoted 10 lines
mm/rmap.c +2 -3
@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	while (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
-	entry = ptep_get_and_clear(mm, address, pte);

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 10 lines
mm/rmap.c +5 -5
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_clear_flush(vma, address, pte);
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		ptep_establish(vma, address, pte, entry);

Now you are flushing the tlb twice. ptep_clear_flush clears the pte and flushes the tlb, ptep_establish sets the new pte and flushes the tlb. Not good. Use set_pte_at instead of the ptep_establish.

Quoted 8 lines
mm/rmap.c +3 -1
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
Signature · 8 lines
blue skies,
  Martin.

Martin Schwidefsky
Linux for zSeries Development & Services
IBM Deutschland Entwicklung GmbH

"Reality continues to ruin my life." - Calvin.
#110 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 14 lines · Martin Schwidefsky
Quoted 1 line · Peter Zijlstra

current version

Nitpicking ..

Quoted 9 lines
mm/rmap.c +2 -2
@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	while (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
Quoted 15 lines

mm/rmap.c static int page_mkclean_one(struct page · line 454

Quoted 11 lines
mm/rmap.c +5 -6
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_clear_flush(vma, address, pte);
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		ptep_establish(vma, address, pte, entry);

Now you are flushing the tlb twice. ptep_clear_flush clears the pte and flushes the tlb, ptep_establish sets the new pte and flushes the tlb. Not good. Use set_pte_at instead of the ptep_establish.

Yeah, sorry, I already noticed and corrected that :-|

Also, I'm dubious about the while thing and stuck a WARN_ON(ret) thing at the beginning of the loop. flush_tlb_page() does IPI the other cpus to flush their tlb too, so there should not be a SMP race, Arjan?

Quoted 9 lines
Quoted 8 lines
mm/rmap.c +3 -1
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
#111 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 9 lines · Peter Zijlstra
Quoted 3 lines

Now you are flushing the tlb twice. ptep_clear_flush clears the pte and flushes the tlb, ptep_establish sets the new pte and flushes the tlb. Not good. Use set_pte_at instead of the ptep_establish.

Yeah, sorry, I already noticed and corrected that :-|

Also, I'm dubious about the while thing and stuck a WARN_ON(ret) thing at the beginning of the loop. flush_tlb_page() does IPI the other cpus to flush their tlb too, so there should not be a SMP race, Arjan?

The while loop is protected by the pte lock and flush_tlb_page has to remove the tlbs on all cpus. So yes, I think the while loop is not necessary.

Signature · 8 lines
blue skies,
  Martin.

Martin Schwidefsky
Linux for zSeries Development & Services
IBM Deutschland Entwicklung GmbH

"Reality continues to ruin my life." - Calvin.
#112 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 4 lines · Peter Zijlstra

Also, I'm dubious about the while thing and stuck a WARN_ON(ret) thing at the beginning of the loop. flush_tlb_page() does IPI the other cpus to flush their tlb too, so there should not be a SMP race, Arjan?

Now, the reason I think the loop may be needed is:

CPU#0 CPU#1
----- -----
load old PTE entry
clear dirty and WP bits
write to page using old PTE NOT CHECKING that the new one is write-protected, and just setting the dirty bit blindly (but atomically)
flush_tlb_page()
TLB flushed, but we now have a page that is marked dirty and unwritable in the page tables, and we will mark it clean in "struct page *"

Now, the scary thing is, IF a CPU does this, then the way we do all this, we may actually have the following sequence:

	CPU#0				CPU#1
	-----				-----
					load old PTE entry
	ptep_clear_flush():
					atomic "set dirty bit" sequence
					PTEP now contains 0000040 !!!
	flush_tlb_page();
					TLB flushed, but PTEP is still 
					"dirty zero"
	write the clear/readonly PTE
					THE DIRTY BIT WAS LOST!

which might actually explain this bug.

I personally _thought_ that Intel CPU's don't actually do an "set dirty bit atomically" sequence, but more of a "set dirty bit but trap if the TLB is nonpresent" thing, but I have absolutely no proof for that.

Anyway, IF this is the case, then the following patch may or may not fix things. It avoids things by never overwriting a PTE entry, not even the "cleared" one. It always does an atomic "xchg()" with a valid new entry, and looks at the old bits.

What do you guys think? Does something like this work out for S/390 too? I tried to make that "ptep_flush_dirty()" concept work for architectures that hide the dirty bit somewhere else too, but..

It actually simplifies the architecture-specific code (you just need to implement a trivial "ptep_exchange()" and "ptep_flush_dirty()" macro), but I only did x86-64 and i386, and while I've booted with this, I haven't really given the thing a lot of really _deep_ thought.

But I think this might be safer, as per above.. And it _might_ actually explain the problem. Exactly because the "ptep_clear() + blindly assign to ptep" might lose a dirty bit that was written by another CPU.

But this really does depend on what a CPU does when it marks a page dirty. Does it just blindly write the dirty bit? Or does it actually _validate_ that the old page table entry was still present and writable?

This patch makes no assumptions. It should work even if a CPU just writes the dirty bit blindly, and the only expectation is that the page tables can be accessed atomically (which had _better_ be true on any SMP architecture)

Arjan, can you please check within Intel, and ask what the "proper" sequence for doing something like this is?

Linus

----
commit 301d2d53ca0e5d2f61b1c1c259da410c7ee6d6a7

Author: Linus Torvalds <[email protected]>
Date:   Thu Dec 21 11:11:05 2006 -0800

Rewrite the page table "clear dirty and writable" accesses

This is much simpler for most architectures, and allows us to do the dirty and writable clear in a single operation without any races or any double flushes.

It's also much more careful: we never overwrite the old dirty bits at any time, and always make sure to do atomic memory ops to exchange and see the old value.

include/asm-generic/pgtable.h +0 -25
index 9d774d0..8879f1d 100644
@@ -61,31 +61,6 @@ do {				  					  \
 })
 #endif
 
-#ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY
-#define ptep_test_and_clear_dirty(__vma, __address, __ptep)		\
-({									\
-	pte_t __pte = *__ptep;						\
-	int r = 1;							\
-	if (!pte_dirty(__pte))						\
-		r = 0;							\
-	else								\
-		set_pte_at((__vma)->vm_mm, (__address), (__ptep),	\
-			   pte_mkclean(__pte));				\
-	r;								\
-})
-#endif
-
-#ifndef __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH
-#define ptep_clear_flush_dirty(__vma, __address, __ptep)		\
-({									\
-	int __dirty;							\
-	__dirty = ptep_test_and_clear_dirty(__vma, __address, __ptep);	\
-	if (__dirty)							\
-		flush_tlb_page(__vma, __address);			\
-	__dirty;							\
-})
-#endif
-
 #ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
 #define ptep_get_and_clear(__mm, __address, __ptep)			\
 ({									\
include/asm-i386/pgtable.h +14 -12
index e6a4723..b61d6f9 100644
@@ -300,18 +300,20 @@ do {									\
 	flush_tlb_page(vma, address);					\
 } while (0)
 
-#define __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH
-#define ptep_clear_flush_dirty(vma, address, ptep)			\
-({									\
-	int __dirty;							\
-	__dirty = pte_dirty(*(ptep));					\
-	if (__dirty) {							\
-		clear_bit(_PAGE_BIT_DIRTY, &(ptep)->pte_low);		\
-		pte_update_defer((vma)->vm_mm, (address), (ptep));	\
-		flush_tlb_page(vma, address);				\
-	}								\
-	__dirty;							\
-})
+/*
+ * "ptep_exchange()" can be used to atomically change a set of
+ * page table protection bits, returning the old ones (the dirty
+ * and accessed bits in particular, since they are set by hw).
+ *
+ * "ptep_flush_dirty()" then returns the dirty status of the
+ * page (on x86-64, we just look at the dirty bit in the returned
+ * pte, but some other architectures have the dirty bits in
+ * other places than the page tables).
+ */
+#define ptep_exchange(vma, address, ptep, old, new) \
+	(old).pte_low = xchg(&(ptep)->pte_low, (new).pte_low);
+#define ptep_flush_dirty(vma, address, ptep, old) \
+	pte_dirty(old)
 
 #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
 #define ptep_clear_flush_young(vma, address, ptep)			\
include/asm-x86_64/pgtable.h +14 -6
index 59901c6..07754b5 100644
@@ -283,12 +283,20 @@ static inline pte_t pte_clrhuge(pte_t pte)	{ set_pte(&pte, __pte(pte_val(pte) &
 
 struct vm_area_struct;
 
-static inline int ptep_test_and_clear_dirty(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
-{
-	if (!pte_dirty(*ptep))
-		return 0;
-	return test_and_clear_bit(_PAGE_BIT_DIRTY, &ptep->pte);
-}
+/*
+ * "ptep_exchange()" can be used to atomically change a set of
+ * page table protection bits, returning the old ones (the dirty
+ * and accessed bits in particular, since they are set by hw).
+ *
+ * "ptep_flush_dirty()" then returns the dirty status of the
+ * page (on x86-64, we just look at the dirty bit in the returned
+ * pte, but some other architectures have the dirty bits in
+ * other places than the page tables).
+ */
+#define ptep_exchange(vma, address, ptep, old, new) \
+	(old).pte = xchg(&(ptep)->pte, (new).pte);
+#define ptep_flush_dirty(vma, address, ptep, old) \
+	pte_dirty(old)
 
 static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, unsigned long addr, pte_t *ptep)
 {
mm/rmap.c +19 -17
index d8a842a..a028803 100644
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *ptep;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -440,22 +440,24 @@ static int page_mkclean_one(struct page *page, struct vm_area_struct *vma)
 	if (address == -EFAULT)
 		goto out;
 
-	pte = page_check_address(page, mm, address, &ptl);
-	if (!pte)
-		goto out;
-
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
-
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
-
-unlock:
-	pte_unmap_unlock(pte, ptl);
+	ptep = page_check_address(page, mm, address, &ptl);
+	if (ptep) {
+		pte_t old, new;
+
+		old = *ptep;
+		new = pte_wrprotect(pte_mkclean(old));
+		if (!pte_same(old, new)) {
+			for (;;) {
+				flush_cache_page(vma, address, page_to_pfn(page));
+				ptep_exchange(vma, address, ptep, old, new);
+				if (pte_same(old, new))
+					break;
+				ret |= ptep_flush_dirty(vma, address, ptep, old);
+				flush_tlb_page(vma, address);
+			}
+		}
+		pte_unmap_unlock(pte, ptl);
+	}
 out:
 	return ret;
 }
#113 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · Linus Torvalds

What do you guys think? Does something like this work out for S/390 too? I tried to make that "ptep_flush_dirty()" concept work for architectures that hide the dirty bit somewhere else too, but..

For s390 there are two aspects to consider:
1) the pte values are 100% software controlled. They only change because a cpu stored a value to it or issued one of the specialized instructions (csp, ipte and idte). The ptep_flush_dirty would be a nop for s390.
2) ptep_exchange is a bit dangerous. For s390 we need a lock that protects the software controlled updates of the ptes. The reason is the ipte instruction. It is implemented by the machine microcode in a non-atomic way in regard to the memory. It reads the byte of the pte that contains the invalid bit, flushes the tlb entries for it and then writes back the byte with the invalid bit set. The microcode makes sure that this pte cannot be used for form a new tlb on any cpu while the ipte is in progress.
That means a compare-and-swap semantics on ptes won't work together with the ipte optimization. As long as there is the pte lock that protects all software accesses to the pte we are fine. But if any code expects that ptep_exchange does something like an xchg things break.

Signature · 8 lines
blue skies,
  Martin.

Martin Schwidefsky
Linux for zSeries Development & Services
IBM Deutschland Entwicklung GmbH

"Reality continues to ruin my life." - Calvin.
#114 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · Martin Schwidefsky

For s390 there are two aspects to consider:
1) the pte values are 100% software controlled.

That's fine. In that situation, you shouldn't need any atomic ops at all, I think all our sw page-table operations are already done under the pte lock.

The reason x86 needs to be careful is exactly the fact that the hardware will obviously do a lot on its own, and the hardware is _not_ going to honor our page table locking ;)

In an all-sw situation, a lot of this should be easier. S390 has _other_ things that are inconvenient (the strange "dirty bit is not in the page tables" thing that makes it look different from everybody else), but hey, it's a balance..

So for s390, ptep_exchange() in my example should be able to be a simple "load old value and store new one", assuming everybody honors the pte lock (and they _should_).

Linus

maintainer
#115 lore raw

Re: [PATCH] mm: fix page_mkclean_one

From: Linus Torvalds <[email protected]>
Date: Wed, 27 Dec 2006 16:42:40 -0800 (PST)

Quoted 3 lines

That's fine. In that situation, you shouldn't need any atomic ops at all, I think all our sw page-table operations are already done under the pte lock.

This is true, but there is one subtlety to this I want to point out in passing.

That lock can possibly only protect a page of PTEs.

When NR_CPUS >= CONFIG_SPLIT_PTLOCK_CPUS, the locking is done per page of PTEs, not for all of the page tables of an address space at once.

What this means is that it's really difficult to forcefully block out all page table operations for a given mm, and I actually needed to do something like this on sparc64 (when growing the TLB lookup hash table, you can't let any PTEs change state while the table is changing). For my case, I added a spinlock to mm->context since actually what I need is to block modifications to the hash table itself during PTE changes.

#116 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 1 line · Peter Zijlstra

I think this is also needed:

NAK

invalidate_inode_pages2() should _not_ be pretending that dirty pages are clean. This patch is incorrect both for the NFS usage and for the directIO usage.

In the latter case, if someone has the page mmapped, resulting in the page getting marked as dirty _after_ a directIO write, then it would be wrong to discard that data. Only dirty data from _before_ the directIO write should needs to be discarded (and that is achieved by unmapping, then cleaning the page prior to the directIO call)...

For the NFS case, the race is a bit more tricky, since you have the "unstable write" case which means that the page is neither marked as dirty, nor is entirely clean ('cos we don't know that the server has committed the data to permanent storage yet).

Cheers
Trond

Quoted 36 lines
---
 mm/truncate.c |    7 +------
 1 file changed, 1 insertion(+), 6 deletions(-)

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

mm/truncate.c +1 -7
@@ -320,19 +320,14 @@ invalidate_complete_page2(struct address
 	if (PagePrivate(page) && !try_to_release_page(page, GFP_KERNEL))
 		return 0;
 
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
 	lock_page_ref_irq(page);
-	if (PageDirty(page))
-		goto failed;
-
 	BUG_ON(PagePrivate(page));
 	__remove_from_page_cache(page);
 	unlock_page_ref_irq(page);
 	ClearPageUptodate(page);
 	page_cache_release(page);	/* pagecache ref */
 	return 1;
-failed:
-	unlock_page_ref_irq(page);
-	return 0;
 }
 
 /**
 
 
-

To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/

#117 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 15 lines · Trond Myklebust
Quoted 1 line · Peter Zijlstra

I think this is also needed:

NAK

invalidate_inode_pages2() should _not_ be pretending that dirty pages are clean. This patch is incorrect both for the NFS usage and for the directIO usage.

In the latter case, if someone has the page mmapped, resulting in the page getting marked as dirty _after_ a directIO write, then it would be wrong to discard that data. Only dirty data from _before_ the directIO write should needs to be discarded (and that is achieved by unmapping, then cleaning the page prior to the directIO call)...

Quoted 4 lines

For the NFS case, the race is a bit more tricky, since you have the "unstable write" case which means that the page is neither marked as dirty, nor is entirely clean ('cos we don't know that the server has committed the data to permanent storage yet).

Then this patch:
http://kernel.org/pub/linux/kernel/people/akpm/patches/2.6/2.6.20-rc1/2.6.20-rc1-mm1/broken-out/nfs-fix-nr_file_dirty-underflow.patch

is equally wrong, right?

#118 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 32 lines · Linus Torvalds
Quoted 3 lines · Linus Torvalds

So that's why I've been harping on the fact that I think we simply do really wrong things with PG_dirty at times [ ... ]

Ok, I'll just put my money where my mouth is, and suggest a patch like THIS instead.

This one clears up all the issues I find irritating:

- "test_clear_page_dirty()" is insane, both conceptually and as an implementation. "Give me a 'C', give me an 'R', give me an 'A', give me a 'P'".

So rip out that mindfart entirely.

- "clear_page_dirty()" is badly named, and should be about CANCELLING the dirty bit, and must never be called with pages mapped anyway. So throw that out too, and replace it with a new function:

	void cancel_dirty_page(struct page *page, unsigned int accounting_size);

- "clear_page_dirty_for_io()" is fine.

And with that, I then either rip out any old users of "test_clear_page_dirty()" or "clear_page_dirty()", and if appropriate (and it's realy lonly appropriate for "truncate()", I replace them with the new "cancel_dirty_page()". Most of the time, they should just be deleted entirely.

Quoted 6 lines

NOTE NOTE NOTE! I _only_ did enough to make things compile for my particular configuration. That means that right now the following filesystems are broken with this patch (because they use the totally broken old crap):

CIFS, FUSE, JFS, ReiserFS, XFS

XFS appears to call clear_page_dirty to get the mapping tree dirty tag set correctly at the same time the page dirty flag is cleared. I note that this can be done by set_page_writeback() if we clear the dirty flag on the page first when we are writing back the entire page.

Hence it seems to me that the XFS call to clear_page_dirty() could easily be substituted by clear_page_dirty_for_io() followed by a call to set_page_writeback() to get the mapping tree tags set correctly after the page has been marked clean.

Does this make sense (even without the posted patch)?

---
 fs/xfs/linux-2.6/xfs_aops.c |    4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
fs/xfs/linux-2.6/xfs_aops.c +2 -2
Index: 2.6.x-xfs-new/fs/xfs/linux-2.6/xfs_aops.c
===================================================================
@@ -340,9 +340,9 @@ xfs_start_page_writeback(
 {
 	ASSERT(PageLocked(page));
 	ASSERT(!PageWriteback(page));
-	set_page_writeback(page);
 	if (clear_dirty)
-		clear_page_dirty(page);
+		clear_page_dirty_for_io(page);
+	set_page_writeback(page);
 	unlock_page(page);
 	if (!buffers) {
 		end_page_writeback(page);

Cheers,

Dave.

Signature · 3 lines
Dave Chinner
Principal Engineer
SGI Australian Software Group
#119 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · David Chinner

XFS appears to call clear_page_dirty to get the mapping tree dirty tag set correctly at the same time the page dirty flag is cleared. I note that this can be done by set_page_writeback() if we clear the dirty flag on the page first when we are writing back the entire page.

Yes. I think the XFS routine should just use "clear_page_dirty_fir_io()", since that matches what it actually wants to do (surprise surprise, it's going to write it out).

HOWEVER. Why is it conditional? Can somebody who understands XFS tell me why "clear_dirty" would ever be 0? I can grep the sources, and I see that it's an unconditional 1 in one call-site, but then in the other one it does

	xfs_start_page_writeback(page, wbc, !page_dirty, count);

and that part just blows my mind. Why would you do a xfs_start_page_writeback() and _not_ write the page out? Is this for a partial-page-only case?

Anyway, your patch looks fine. It seems to be the right thing to do. I'm just wondering why we're not always cleaning the whole page, and why we'd not set it unconditionally dirty?

Linus

#120 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 10 lines · Linus Torvalds
Quoted 5 lines · David Chinner

XFS appears to call clear_page_dirty to get the mapping tree dirty tag set correctly at the same time the page dirty flag is cleared. I note that this can be done by set_page_writeback() if we clear the dirty flag on the page first when we are writing back the entire page.

Yes. I think the XFS routine should just use "clear_page_dirty_fir_io()", since that matches what it actually wants to do (surprise surprise, it's going to write it out).

Yup ;)

Quoted 6 lines

HOWEVER. Why is it conditional? Can somebody who understands XFS tell me why "clear_dirty" would ever be 0? I can grep the sources, and I see that it's an unconditional 1 in one call-site, but then in the other one it does

	xfs_start_page_writeback(page, wbc, !page_dirty, count);

page dirty starts at the number of dirty buffers on the page, and as we map each dirty buffer into the I/O we decrement the page dirty count.

Hence if we map all of the buffers into the I/O, we are cleaning the entire page and hence we can clear the dirty state on the page.

Quoted 3 lines

and that part just blows my mind. Why would you do a xfs_start_page_writeback() and _not_ write the page out? Is this for a partial-page-only case?

Yes, partial-page-only case when doing speculative write clustering. We'll hit this when an extent boundary is not page aligned (fs block size < page size case) and we need to issue at least two separate I/Os to clean the page. Because we leave the page dirty and we are working ahead of the index in generic_writepages() we'll get the rest of the page flushed when we return back to generic_writepages() as the page is still dirty in the mapping tree....

Quoted 1 line

Anyway, your patch looks fine. It seems to be the right thing to do.

Ok, thanks, Linus.

Cheers,

Dave.

Signature · 3 lines
Dave Chinner
Principal Engineer
SGI Australian Software Group
#121 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 28 lines · On Wed, 20 Dec 2006 11:50:50 -0800 (PST) Linus Torvalds <[email protected]>…

Ok, I'll just put my money where my mouth is, and suggest a patch like THIS instead.

...

fs/buffer.c +1 -7
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *page)
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *page)
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.

fs/buffer.c int try_to_free_buffers(struct page *page) · line 2863

Quoted 10 lines
fs/buffer.c +0 -10
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}

I think this will be OK, because vmscan has just run ->writepage anyway. But we will need to make changes to truncate_complete_page() - make it run cancel_dirty_page() before it runs do_invalidatepage().

I don't think there's anything preventing zap_pte_range() or perhaps a pagefault from coming in and dirtying this page after we've tested PageDirty().

That could leave us with a dirty, non-uptodate page with no buffers, which is very bad. But this situation is hopefully impossible, because if the page is not uptodate then the first thing a pagefault will do is bring it uptodate, which involves locking it. And if zap_pte_range() is looking at this page, it is uptodate.

If the page _was_ uptodate and the zap_pte_range() race happens, we'll end up with with either a dirty page with dirty buffers or a dirty uptodate page with no buffers, both of which are OK.

fs/buffer.c int try_to_free_buffers(struct page *page)

Quoted 12 lines
fs/buffer.c +12 -0
+void cancel_dirty_page(struct page *page, unsigned int account_size)
+{
+	/* If we're cancelling the page, it had better not be mapped any more */
+	if (page_mapped(page)) {
+		static unsigned int warncount;
+
+		WARN_ON(++warncount < 5);
+	}
+		
+	if (TestClearPageDirty(page) && account_size)
+		task_io_account_cancelled_write(account_size);
+}

This doesn't clear the radix-tree dirty tags. I'm not sure what effect that would have on a truncated mapping. Perhaps just a bit of extra work in radix-tree lookup during writeback.

If we _know_ that this page is about to be removed from pagecache then radix_tree_delete() will delete the tags for us anyway, but invalidate_inode_pages2() can decide to back out.

fs/buffer.c int invalidate_inode_pages2_range(struct address_space *mapping, · line 404

Quoted 12 lines
fs/buffer.c +1 -5
@@ -386,12 +399,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
-			if (!invalidate_complete_page2(mapping, page)) {
-				if (was_dirty)
-					set_page_dirty(page);
+			if (!invalidate_complete_page2(mapping, page))
 				ret = -EIO;
-			}
 			unlock_page(page);

Well, it used to.

invalidate_complete_page2() is pretty gruesome. We're handling the case where someone went and redirtied the page (and hence its buffers) after the invalidate_inode_pages2() caller (generic_file_direct_IO) synced it to disk.

I'd prefer to just fail the direct-io if someone did that, but then people's tests fail and they whine.

It's tempting to just truncate the damn page and discard the user's data - the app is being silly. But that would permit access to uninitialised disk blocks.

With your change I think what'll happen is that we'll correctly handle the case where the page and its buffers are dirty (it gets left in place), but we'll needlessy fail in the case where the page is dirty but the buffers are clean. How important that will be in practice I do not know. People will get -EIOs where they used not to.

A suitable fix for that might to be to simply not return -EIO here. So some thread went and dirtied a pagecache page after generic_file_direct_IO() synced the data. Big deal, that's your own fault. Usually the disk will end up getting a copy of the dirtied pagecache page and rarely it'll get a copy of the direct-io-written page.

#122 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 17 lines · Andrew Morton

mm/truncate.c static inline void truncate_partial_page(struct page *page, unsigned partial) · line 65

Quoted 13 lines
mm/truncate.c +12 -0
+void cancel_dirty_page(struct page *page, unsigned int account_size)
+{
+	/* If we're cancelling the page, it had better not be mapped any more */
+	if (page_mapped(page)) {
+		static unsigned int warncount;
+
+		WARN_ON(++warncount < 5);
+	}
+		
+	if (TestClearPageDirty(page) && account_size)
+		task_io_account_cancelled_write(account_size);
+}

This doesn't clear the radix-tree dirty tags. I'm not sure what effect that would have on a truncated mapping. Perhaps just a bit of extra work in radix-tree lookup during writeback.

This should _only_ be a valid thing to do when we're removing the page from a mapping anyway, so I'd most definitely hope that the code immediately after (or before) will have done a "remove_from_page_cache()"

In which case the tags should not matter.

There is _no_ excuse for cancelling a page and leaving it in the page cache that I can see. Because your page contents will be _indeterminate_.

Quoted 9 lines

Review int invalidate_inode_pages2_range(struct address_space *mapping,

Quoted 1 line
(quoted diff) +0 -0
@@ -386,12 +399,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,

invalidate_complete_page2() is pretty gruesome. We're handling the case where someone went and redirtied the page (and hence its buffers) after the invalidate_inode_pages2() caller (generic_file_direct_IO) synced it to disk.

I'd prefer to just fail the direct-io if someone did that, but then people's tests fail and they whine.

So with my change, afaik, we will just return EIO to the invalidate, and do the write. Which should be ok. In fact, it appears to be the only possibly valid thing to do.

It really boils down to that same thing: if you remove the dirty bit, there is NO CONCEIVABLE GOOD THING YOU CAN DO EXCEPT FOR:
- do the damn IO already ("clear_page_dirty_for_io()")
- truncate the page (unmap and destroy it both from page cache AND from any user-visible filesystem cases)

Anything else is simpyl a bug. Always has been. My patch just makes that very clear.

Quoted 5 lines

With your change I think what'll happen is that we'll correctly handle the case where the page and its buffers are dirty (it gets left in place), but we'll needlessy fail in the case where the page is dirty but the buffers are clean. How important that will be in practice I do not know. People will get -EIOs where they used not to.

People will now get -EIO where they used to get an inconsistent system image. I really think it sounds like an improvement.

Linus

#123 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 7 lines · On Wed, 20 Dec 2006 15:55:14 -0800 (PST) Linus Torvalds <[email protected]>…
Quoted 7 lines

Review int invalidate_inode_pages2_range(struct address_space *mapping,

Quoted 1 line
(quoted diff) +0 -0
@@ -386,12 +399,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,

invalidate_complete_page2() is pretty gruesome. We're handling the case where someone went and redirtied the page (and hence its buffers) after the invalidate_inode_pages2() caller (generic_file_direct_IO) synced it to disk.

Quoted 5 lines
Quoted 2 lines

I'd prefer to just fail the direct-io if someone did that, but then people's tests fail and they whine.

So with my change, afaik, we will just return EIO to the invalidate, and do the write.

The write's already been done by this stage.

Quoted 8 lines

Which should be ok. In fact, it appears to be the only possibly valid thing to do.

It really boils down to that same thing: if you remove the dirty bit, there is NO CONCEIVABLE GOOD THING YOU CAN DO EXCEPT FOR:
- do the damn IO already ("clear_page_dirty_for_io()")
- truncate the page (unmap and destroy it both from page cache AND from any user-visible filesystem cases)

There's also redirty_page_for_writepage().

#124 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · Andrew Morton
Quoted 3 lines

So with my change, afaik, we will just return EIO to the invalidate, and do the write.

The write's already been done by this stage.

Ok, but the end result is the same: you MUST NOT just "cancel" a write. It needs to be done, or the backing store must be actually de-allocated. You can't just say "get rid of it" and think that it can work. Exactly because of security issues, and just the simple fact that reading it back gets random contents.

So I repeat: clearing a dirty bit really only has two valid cases. Not three, like we used to have. And the "cancel" case cannot be conditional:
either you can cancel it or you cannot. There's no

	if (cancel_dirty_page()) {
			..

sequence that makes sense that I can think of.

Quoted 7 lines
Quoted 5 lines

It really boils down to that same thing: if you remove the dirty bit, there is NO CONCEIVABLE GOOD THING YOU CAN DO EXCEPT FOR:
- do the damn IO already ("clear_page_dirty_for_io()")
- truncate the page (unmap and destroy it both from page cache AND from any user-visible filesystem cases)

There's also redirty_page_for_writepage().

_dirtying_ a page makes sense in any situation. You can always dirty them. I'm just saying that you can't just mark them *clean*.

If your point was that the filesystem had better be able to take care of "redirty_page_for_writepage()", then yes, of course. But since it's the filesystem itself that does it, it had _better_ be able to take care of the situation it puts itself into.

Linus

#125 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Btw, I'd really love to hear whether the patch I sent out actually _helps_ at all, or whether we're just discussing something that in the end is just a cleanup..

Martin, Peter, Andrei, pls give it a try. (Martin and Andrei may be talking about different bugs, so _both_ of your experiences definitely matter here).

Linus

#126 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 8 lines · Linus Torvalds

Btw, I'd really love to hear whether the patch I sent out actually _helps_ at all, or whether we're just discussing something that in the end is just a cleanup..

Martin, Peter, Andrei, pls give it a try. (Martin and Andrei may be talking about different bugs, so _both_ of your experiences definitely matter here).

with http://lkml.org/lkml/diff/2006/12/20/204/1
I have corruption: Hash check on download completion found bad chunks, consider using "safe_sync".

Quoted 2 lines

Linus

#127 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 9 lines · Andrei Popa
Quoted 4 lines · Linus Torvalds

Martin, Peter, Andrei, pls give it a try. (Martin and Andrei may be talking about different bugs, so _both_ of your experiences definitely matter here).

with http://lkml.org/lkml/diff/2006/12/20/204/1
I have corruption: Hash check on download completion found bad chunks, consider using "safe_sync".

Gaah. Martin Michlmayr reported that it apparently fixes his ARM corruption.

Now, admittedly I already suspected the issues might be different (if only because of the UP vs SMP/PREEMPT case), but I really had my hopes up after Martin's report, because if anything, _his_ issue might have been a superset of your problem (while obviously any subtle SMP races you might be seeing are definitely not an issue in his case).

Oh well. I think the ARM case is enough of a reason to apply those patches (if it hadn't made any difference at all, I'd have waited until after 2.6.20), and we'll just have to continue on the SMP PREEMPT angle.

Linus

#128 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 10 lines · Linus Torvalds
Quoted 2 lines

There's also redirty_page_for_writepage().

_dirtying_ a page makes sense in any situation. You can always dirty them. I'm just saying that you can't just mark them *clean*.

If your point was that the filesystem had better be able to take care of "redirty_page_for_writepage()", then yes, of course. But since it's the filesystem itself that does it, it had _better_ be able to take care of the situation it puts itself into.

Btw, as an example of something where this may NOT be ok, look at migrate_page_copy().

I'm not at all convinced that "migrate_page_copy()" can work at all. It does:

	...
        if (PageDirty(page)) {
                clear_page_dirty_for_io(page);
                set_page_dirty(newpage);
        }
	...

which is an example of what NOT to do, because it claims to clear the page for IO, but doesn't actually _do_ any IO.

And this is wrong, for many reasons.

For example, it's very possible that the old page is not actually up-to-date, and is only partially dirty using some FS-specific dirty data queues (like NFS does with its dirty data, or buffer-heads can do for local filesystems). When you do

	if (clear_dirty(page))
		set_page_dirty(page);

in generic VM code, that is a BUG. It's an insane operation. It cannot work. It's exactly what I'm trying to avoid.

So page migration is probably broken, but it's no less broken than it always has been. And I don't think many people use it anyway. It might work "by accident" in a lot of situations, but to actually be solid, it really would need to do something fundamentally different, like:

- have a per-mapping "migrate()" function that actually knows HOW to migrate the dirty state from one page to another.

- or, preferably, by just not migrating dirty pages, and just actually doing the writeback on them first.

Again, this is an example of just _incorrect_ code, that thinks that it can "just clear the dirty bit". You can't do that. It's wrong. And it is not wrong just because I say so, but because the operations itself simply is FUNDAMENTALLY not a sensible one.

This is why I keep harping on this issue: there are two cases, and two cases only, when you can clear a page. And no, "migrating the data to another page" was not one of those two cases. The cases are, and will _always_ be: (a) full writeback IO of _all_ the dirty data on the page (and that can only be done by the low-level filesystem, since it's the only one that knows what rules it has followed for marking things dirty) and (b) cancelling dirty data that got truncated and literally removed from the filesystem.

So I don't claim that I fixed all the cases. mm/migrate.c is still broken. Maybe somebody else also uses "clear_page_dirty_for_io()" even though the name very clearly says FOR IO. I didn't check, but I think they're mostly right now.

Linus

#129 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 17 lines · On Wed, 20 Dec 2006 16:43:31 -0800 (PST) Linus Torvalds <[email protected]>…
Quoted 10 lines · Linus Torvalds
Quoted 2 lines

There's also redirty_page_for_writepage().

_dirtying_ a page makes sense in any situation. You can always dirty them. I'm just saying that you can't just mark them *clean*.

If your point was that the filesystem had better be able to take care of "redirty_page_for_writepage()", then yes, of course. But since it's the filesystem itself that does it, it had _better_ be able to take care of the situation it puts itself into.

Btw, as an example of something where this may NOT be ok, look at migrate_page_copy().

Quoted 7 lines

I'm not at all convinced that "migrate_page_copy()" can work at all. It does:

	...
        if (PageDirty(page)) {
                clear_page_dirty_for_io(page);
                set_page_dirty(newpage);

Note that this is referring to different pages.

Quoted 6 lines
        }
	...

which is an example of what NOT to do, because it claims to clear the page for IO, but doesn't actually _do_ any IO.

Quoted 6 lines

And this is wrong, for many reasons.

For example, it's very possible that the old page is not actually up-to-date, and is only partially dirty using some FS-specific dirty data queues (like NFS does with its dirty data, or buffer-heads can do for local filesystems).

afaict the code copes with those things.

Quoted 7 lines

When you do

	if (clear_dirty(page))
		set_page_dirty(page);

in generic VM code, that is a BUG. It's an insane operation. It cannot work. It's exactly what I'm trying to avoid.

These are different pages.

We could view the copy_highpage() in migrate_page_copy() as an "io" operation, only the backing store is a new pagecache page.

It'd be more logical if that copy_highpage() was occurring after the clear_page_dirty_for_io().

I'm not sure why migrate_page_copy() is playing with PageWriteback(newpage). Surely newpage is locked, in which case nobody will be starting writeback on it.

Quoted 7 lines

So page migration is probably broken, but it's no less broken than it always has been. And I don't think many people use it anyway. It might work "by accident" in a lot of situations, but to actually be solid, it really would need to do something fundamentally different, like:

- have a per-mapping "migrate()" function that actually knows HOW to migrate the dirty state from one page to another.

That is how it's presently implemented. You're looking at helper functions which fileystems may point their address_space_operations.migratepage at.

Quoted 7 lines

- or, preferably, by just not migrating dirty pages, and just actually doing the writeback on them first.

Again, this is an example of just _incorrect_ code, that thinks that it can "just clear the dirty bit". You can't do that. It's wrong. And it is not wrong just because I say so, but because the operations itself simply is FUNDAMENTALLY not a sensible one.

The dirty state is being transferred to the new page. The tricky part is handling the cases where these pages are mapped into pagetables. That's what the special migration ptes are there for. I'll let Christoph explain that lot ;)

#130 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 8 lines · Linus Torvalds
Quoted 5 lines

With your change I think what'll happen is that we'll correctly handle the case where the page and its buffers are dirty (it gets left in place), but we'll needlessy fail in the case where the page is dirty but the buffers are clean. How important that will be in practice I do not know. People will get -EIOs where they used not to.

People will now get -EIO where they used to get an inconsistent system image. I really think it sounds like an improvement.

The hell it is. You end up with a corrupted page cache because invalidate_inode_pages2_range() immediately exits without throwing out the pages in the rest of the range.

I can't see that it is the business of invalidate_inode_pages2() to resolve races between ->direct_IO() and pages that are redirtied by mmap(). All it needs to ensure is that pages that clean are discarded, since those are neither consistent with data that the ->directIO() call wrote to the disk nor are they scheduled to be written to disk.

The only case that I can see that is still problematic is NFS because it may have unstable writes (hence the ->launder_page() patch that I posted yesterday).

Trond

#131 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 6 lines · Trond Myklebust

I can't see that it is the business of invalidate_inode_pages2() to resolve races between ->direct_IO() and pages that are redirtied by mmap(). All it needs to ensure is that pages that clean are discarded, since those are neither consistent with data that the ->directIO() call wrote to the disk nor are they scheduled to be written to disk.

Sure, we could happily just remove the -EIO. Alternatively, we could still do all the invalidates over the whole range, and return -EIO at the end of any of the pages weren't invalidated because they had to be written back.

I don't personally care whether we should just return success or something to indicate that there were busy pages, but somebody who _uses_ direct-IO might want to know that the thing didn't throw away everything. If you know such users, can you ask them?

(Maybe "-EAGAIN" is better than "-EIO", since it's not really even a fatal error).

Linus

#132 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 4 lines · Linus Torvalds

Ok, I'll just put my money where my mouth is, and suggest a patch like THIS instead.

Martin, Andrei, does this make any difference for your corruption cases?

Unfortunately, I cannot get the latest git version of the kernel to boot on the ARM machine on which Martin and I are experiencing the apt segfault. After the kernel is finished uncompressing it prints "done, booting the kernel." as expected, but nothing more happens. I have tried both with and without the patch. Hopefully either Andrei or Martin will have better luck at testing this patch than I have had.

Gordon

Signature · 1 line
Gordon Farquharson
#133 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Gordon Farquharson

Unfortunately, I cannot get the latest git version of the kernel to boot on the ARM machine on which Martin and I are experiencing the apt segfault.

Ouch.

Quoted 4 lines

After the kernel is finished uncompressing it prints "done, booting the kernel." as expected, but nothing more happens. I have tried both with and without the patch. Hopefully either Andrei or Martin will have better luck at testing this patch than I have had.

That's obviously a bug worth fixing on its own. Do you know when it started?

That said, I think the patch I sent out should actually work on top of plain 2.6.19 too. I don't think things have changed in this area that much. IOW, you don't _need_ latest -git to test it, you just need a broken kernel ;)

Linus

#134 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 8 lines · Linus Torvalds
Quoted 3 lines

Unfortunately, I cannot get the latest git version of the kernel to boot on the ARM machine on which Martin and I are experiencing the apt segfault.

Ouch.

That's obviously a bug worth fixing on its own. Do you know when it started?

This is a known issue. The following patch has been proposed
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4030/1
although I just notice that it has been marked as "discarded". Apparently Russell King commited a better patch so this should be fixed in git when he sends his next pull request.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#135 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 6 lines · Martin Michlmayr

This is a known issue. The following patch has been proposed
http://www.arm.linux.org.uk/developer/patches/viewpatch.php?id=4030/1
although I just notice that it has been marked as "discarded". Apparently Russell King commited a better patch so this should be fixed in git when he sends his next pull request.

Ahh, ok. Then it might even be in the set of merges I did earlier today (and which should mirror out soon enough, hopefully).

Linus

#136 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 4 lines · Linus Torvalds

That said, I think the patch I sent out should actually work on top of plain 2.6.19 too. I don't think things have changed in this area that much. IOW, you don't _need_ latest -git to test it, you just need a broken kernel ;)

I created a version of your patch that applied to 2.6.19, but it doesn't compile:

mm/built-in.o: In function `cancel_dirty_page':
slab.c:(.text+0x8964): undefined reference to `task_io_account_cancelled_write' make[3]: *** [.tmp_vmlinux1] Error 1

It looks like task_io_account_cancelled_write() was added in

http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=7c3ab7381e79dfc7db14a67c6f4f3285664e1ec2

Can the call to task_io_account_cancelled_write() simply be removed from cancel_dirty_page() for testing the patch with 2.6.19 (since 2.6.19 doesn't seem to have the task I/O accounting) ?

Gordon

Signature · 1 line
Gordon Farquharson
#137 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · On Thu, 21 Dec 2006 02:17:05 -0700 "Gordon Farquharson"…

Can the call to task_io_account_cancelled_write() simply be removed from cancel_dirty_page() for testing the patch with 2.6.19 (since 2.6.19 doesn't seem to have the task I/O accounting) ?

Yes.

#138 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · Andrew Morton
Quoted 3 lines

Can the call to task_io_account_cancelled_write() simply be removed from cancel_dirty_page() for testing the patch with 2.6.19 (since 2.6.19 doesn't seem to have the task I/O accounting) ?

Yes.

I tested 2.6.19 with a version of Linus's patch that applies cleanly to 2.6.19 (patch appended to the end of this email) on ARM and apt-get failed. It did not segfault this time, but instead got stuck for about 20 to 30 minutes and was accessing the hard drive frequently.

Here is some background about the problem we see with apt which may help somebody with knowledge of the apt source code analyse the problem in the context of the patch. When apt-get is first run, it generates pkgcache.bin and srcpkgcache.bin in /var/cache/apt. We have found that these are the files that get corrupted when we apply the patch "mm: tracking shared dirty pages" [1] to 2.6.18. The corruption of these files is what causes apt-get to segfault. I have observed that the normal operation of apt-get is that while apt-get is generating these files, pkgcache.bin grows to 12582912 bytes, and when apt-get finishes, pkgcache.bin is 6425533 bytes and srcpkgcache.bin is 64254483 bytes. This time, when apt-get exited, it had only created pkgcache.bin which was still 12582912 bytes. Also, the patch caused apt to slow down a lot. I ran apt-get -f install after apt had exited, and it took so long that I killed it before it had finished.

I did not try 2.6.20-git, but I presume that this version is what Martin tried earlier. Maybe Linus's patch doesn't work with 2.6.19, because 2.6.19 is missing some other patch.

Gordon

[1] http://www2.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=d08b3851da41d0ee60851f2c75b118e1f7a5fc89

fs/buffer.c 2006-12-21 01:16:31.000000000 -0700 renamed from fs/buffer.c 2006-11-29 14:57:37.000000000 -0700 +1 -12
diff -Naupr linux-2.6.19.orig/fs/buffer.c linux-2.6.19/fs/buffer.c
@@ -2832,7 +2832,7 @@ int try_to_free_buffers(struct page *pag
       int ret = 0;
 
       BUG_ON(!PageLocked(page));
-       if (PageWriteback(page))
+       if (PageDirty(page) || PageWriteback(page))
               return 0;
 
       if (mapping == NULL) {          /* can this still happen? */
@@ -2843,17 +2843,6 @@ int try_to_free_buffers(struct page *pag
       spin_lock(&mapping->private_lock);
       ret = drop_buffers(page, &buffers_to_free);
       spin_unlock(&mapping->private_lock);
-       if (ret) {
-               /*
-                * If the filesystem writes its buffers by hand (eg ext3)
-                * then we can have clean buffers against a dirty page.  We
-                * clean the page here; otherwise later reattachment of buffers
-                * could encounter a non-uptodate page, which is unresolvable.
-                * This only applies in the rare case where try_to_free_buffers
-                * succeeds but the page is not freed.
-                */
-               clear_page_dirty(page);
-       }

out:

       if (buffers_to_free) {
               struct buffer_head *bh = buffers_to_free;

diff -Naupr linux-2.6.19.orig/fs/hugetlbfs/inode.c linux-2.6.19/fs/hugetlbfs/inode.c
--- linux-2.6.19.orig/fs/hugetlbfs/inode.c 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/fs/hugetlbfs/inode.c 2006-12-21 01:15:21.000000000 -0700
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct

static void truncate_huge_page(struct page *page)
{
- clear_page_dirty(page);
+ cancel_dirty_page(page, /* No IO accounting for huge pages? */0);

       ClearPageUptodate(page);
       remove_from_page_cache(page);
       put_page(page);

diff -Naupr linux-2.6.19.orig/include/linux/page-flags.h linux-2.6.19/include/linux/page-flags.h
--- linux-2.6.19.orig/include/linux/page-flags.h 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/include/linux/page-flags.h 2006-12-21
01:15:21.000000000 -0700
@@ -253,15 +253,11 @@ static inline void SetPageUptodate(struc

struct page; /* forward declaration */

-int test_clear_page_dirty(struct page *page);
+extern void cancel_dirty_page(struct page *page, unsigned int account_size); +
int test_clear_page_writeback(struct page *page);
int test_set_page_writeback(struct page *page);

-static inline void clear_page_dirty(struct page *page) -{
- test_clear_page_dirty(page);
-}
-
static inline void set_page_writeback(struct page *page)

{
       test_set_page_writeback(page);
mm/memory.c 2006-12-21 01:15:21.000000000 -0700 renamed from mm/memory.c 2006-11-29 14:57:37.000000000 -0700 +0 -0
diff -Naupr linux-2.6.19.orig/mm/memory.c linux-2.6.19/mm/memory.c
@@ -1832,6 +1832,33 @@ void unmap_mapping_range(struct address_

}
EXPORT_SYMBOL(unmap_mapping_range);

+static void check_last_page(struct address_space *mapping, loff_t size) +{

+       pgoff_t index;
+       unsigned int offset;
+       struct page *page;

+
+ if (!mapping)
+ return;
+ offset = size & ~PAGE_MASK;
+ if (!offset)
+ return;

+       index = size >> PAGE_SHIFT;
+       page = find_lock_page(mapping, index);
+       if (page) {
+               unsigned int check = 0;
+               unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+               do {
+                       check += kaddr[offset++];
+               } while (offset < PAGE_SIZE);
+               kunmap_atomic(kaddr,KM_USER0);
+               unlock_page(page);
+               page_cache_release(page);
+               if (check)

+ printk("%s: BADNESS: truncate check %u\n",
current->comm, check);
+ }
+}
+
/**
* vmtruncate - unmap mappings "freed" by truncate() syscall
* @inode: inode of the file used
@@ -1865,6 +1892,7 @@ do_expand:

               goto out_sig;
       if (offset > inode->i_sb->s_maxbytes)
               goto out_big;
+       check_last_page(mapping, inode->i_size);
       i_size_write(inode, offset);

out_truncate:
diff -Naupr linux-2.6.19.orig/mm/page-writeback.c linux-2.6.19/mm/page-writeback.c
--- linux-2.6.19.orig/mm/page-writeback.c 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/mm/page-writeback.c 2006-12-21 01:26:53.000000000 -0700
@@ -843,39 +843,6 @@ int set_page_dirty_lock(struct page *pag EXPORT_SYMBOL(set_page_dirty_lock);

/*
- * Clear a page's dirty flag, while caring for dirty memory accounting.
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
- struct address_space *mapping = page_mapping(page);
- unsigned long flags;
-
- if (mapping) {
- write_lock_irqsave(&mapping->tree_lock, flags);
- if (TestClearPageDirty(page)) {
- radix_tree_tag_clear(&mapping->page_tree,
- page_index(page),
- PAGECACHE_TAG_DIRTY);
- write_unlock_irqrestore(&mapping->tree_lock, flags);
- /*

-                        * We can continue to use `mapping' here because the
-                        * page is locked, which pins the address_space
-                        */

- if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
- dec_zone_page_state(page, NR_FILE_DIRTY);
- }
- return 1;

-               }
-               write_unlock_irqrestore(&mapping->tree_lock, flags);
-               return 0;

- }
- return TestClearPageDirty(page);
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
-/*
* Clear a page's dirty flag, while caring for dirty memory accounting.
* Returns true if the page was previously dirty.
*

mm/truncate.c 2006-12-21 15:58:18.000000000 -0700 renamed from mm/truncate.c 2006-11-29 14:57:37.000000000 -0700 +0 -0
diff -Naupr linux-2.6.19.orig/mm/truncate.c linux-2.6.19/mm/truncate.c
@@ -50,6 +50,17 @@ static inline void truncate_partial_page
               do_invalidatepage(page, partial);

}

+void cancel_dirty_page(struct page *page, unsigned int account_size) +{
+ /* If we're cancelling the page, it had better not be mapped
any more */+ if (page_mapped(page)) {
+ static unsigned int warncount;
+
+ WARN_ON(++warncount < 5);
+ }
+}
+
+
/*
* 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
@@ -69,7 +80,8 @@ truncate_complete_page(struct address_sp

       if (PagePrivate(page))
               do_invalidatepage(page, 0);

- clear_page_dirty(page);
+ cancel_dirty_page(page, PAGE_CACHE_SIZE);
+

       ClearPageUptodate(page);
       ClearPageMappedToDisk(page);
       remove_from_page_cache(page);
@@ -348,7 +360,6 @@ int invalidate_inode_pages2_range(struct
               for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
                       struct page *page = pvec.pages[i];
                       pgoff_t page_index;

- int was_dirty;

                       lock_page(page);
                       if (page->mapping != mapping) {
@@ -384,12 +395,8 @@ int invalidate_inode_pages2_range(struct
                                         PAGE_CACHE_SIZE, 0);
                               }
                       }

- was_dirty = test_clear_page_dirty(page);
- if (!invalidate_complete_page2(mapping, page)) {
- if (was_dirty)
- set_page_dirty(page);
+ if (!invalidate_complete_page2(mapping, page))

                               ret = -EIO;
-                       }
                       unlock_page(page);
               }
               pagevec_release(&pvec);
Signature · 1 line
Gordon Farquharson
#139 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · Gordon Farquharson

I tested 2.6.19 with a version of Linus's patch that applies cleanly to 2.6.19 (patch appended to the end of this email) on ARM and apt-get failed. It did not segfault this time, but instead got stuck for about 20 to 30 minutes and was accessing the hard drive frequently.

Ok, there's definitely something screwy going on.

Andrew located at least one bug: we run cancel_dirty_page() too late in "truncate_complete_page()", which means that do_invalidatepage() ends up not clearing the page cache.

His patch is appended.

But it sounds like I probably misunderstood something, because I thought that Martin had acknowledged that this patch actually worked for him. Which sounded very similar to your setup (he has a 32M ARM box too, no?)

And your failure sounds a lot like one that David Miller is reporting. At the same time, my own shared file mmap tests on my own machines obviously work fine (I lower the dirty-writeback tresholds to force writeback more easily, and then mmap a file and write and rewrite to it in memory, and truncate it).

Maybe it's mount option issue? I've got data=ordered on my machine, are you perhaps runnign with something else?

Linus

---
commit 3e67c0987d7567ad666641164a153dca9a43b11d

Author: Andrew Morton <[email protected]>
Date:   Thu Dec 21 11:00:33 2006 -0800

[PATCH] truncate: clear page dirtiness before running try_to_free_buffers()

truncate presently invalidates the dirty page's buffer_heads then shoots down the page. But try_to_free_buffers() will now bale out because the page is dirty.

Net effect: the LRU gets filled with dirty pages which have invalidated buffer_heads attached. They have no ->mapping and hence cannot be cleaned. The machine leaks memory at an enormous rate.

Fix this by cleaning the page before running try_to_free_buffers(), so try_to_free_buffers() can do its work.

Also, remember to do dirty-page-acoounting in cancel_dirty_page() so the machine won't wedge up trying to write non-existent dirty pages.

Probably still wrong, but now less so.

mm/truncate.c +5 -4
index bf9e296..89a5c35 100644
@@ -60,11 +60,12 @@ void cancel_dirty_page(struct page *page, unsigned int account_size)
 		WARN_ON(++warncount < 5);
 	}
 		
-	if (TestClearPageDirty(page) && account_size)
+	if (TestClearPageDirty(page) && account_size) {
+		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
+	}
 }
 
-
 /*
  * 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
@@ -81,11 +82,11 @@ truncate_complete_page(struct address_space *mapping, struct page *page)
 	if (page->mapping != mapping)
 		return;
 
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
+
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	cancel_dirty_page(page, PAGE_CACHE_SIZE);
-
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
 	remove_from_page_cache(page);
#140 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 2 lines · Linus Torvalds

But it sounds like I probably misunderstood something, because I thought that Martin had acknowledged that this patch actually worked for him.

That's what I thought too but now I can confirm what Gordon sees. But it's pretty weird. Our testcase is to run Debian installer on the NSLU2 arm device and apt-get would either segfault or hang at this particular spot in the installation (when apt is first run). With your patch, apt works correctly where it normally fails (at least for me). I stopped the installation at this point and repeated it several more times to make sure it's really working. And, yes, I can repeat this result.

This time, however, I let the installer continue and it seems that with your patch apt now works where it failed in the past, but it hangs later on. It's pretty weird because I cannot even kill the process:

sh-3.1# ps aux | grep 31126

root     31126  5.7 20.6  16240  6076 ?        R+   04:45   0:21 apt-get -o APT::Status-Fd=4 -o APT::Keep-Fds::=5 -o APT::Keep-Fds::=6 -q -y -f install popularity-contest
root     31157  0.0  1.6   1516   492 ttyS0    S+   04:51   0:00 grep 31126
sh-3.1# kill -9 31126
sh-3.1# kill -9 31126
sh-3.1# ps aux | grep 31126
root     31126  5.6 20.6  16240  6076 ?        R+   04:45   0:21 apt-get -o APT::Status-Fd=4 -o APT::Keep-Fds::=5 -o APT::Keep-Fds::=6 -q -y -f install popularity-contest
root     31159  0.0  1.6   1516   492 ttyS0    S+   04:51   0:00 grep 31126

sh-3.1#

Quoted 1 line

Which sounded very similar to your setup (he has a 32M ARM box too, no?)

It's the same device, a Linksys NSLU2.

Quoted 1 line

Author: Andrew Morton <[email protected]>

This patch makes it even worse for me.

mm/truncate.c void cancel_dirty_page(struct page *page, unsigned int account_size) · line 66

Quoted 5 lines
mm/truncate.c +3 -1
-	if (TestClearPageDirty(page) && account_size)
+	if (TestClearPageDirty(page) && account_size) {
+		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
+	}

This hunk (on top of git from about 2 days ago and your latest patch) results in the installer hanging right at the start. The Linux kernel boots fine, the debian-installer is loaded into a ramdisk but when ncurses is being started it just hangs. Reverting this hunk makes it start again.

Does that help or confuse you even more?

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#141 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Martin Michlmayr

This time, however, I let the installer continue and it seems that with your patch apt now works where it failed in the past, but it hangs later on. It's pretty weird because I cannot even kill the process:

Okay, it's really weird. So apt-get just hangs doing nothing and I cannot even kill it. I just tried to download strace via wget and immediately when I started wget, the hanging apt-get process continued.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#142 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Martin Michlmayr

Okay, it's really weird. So apt-get just hangs doing nothing and I cannot even kill it. I just tried to download strace via wget and immediately when I started wget, the hanging apt-get process continued.

... and now that we've completed this step, the apt cache has suddenly been reduced (see Gordon's mail for an explanation) and it segfaults:

sh-3.1# ls -l /var/cache/apt/
total 12524
drwxr-xr-x 3 root root 12288 Dec 22 04:41 archives
-rw-r--r-- 1 root root 6426885 Dec 22 05:03 pkgcache.bin -rw-r--r-- 1 root root 6426835 Dec 22 05:03 srcpkgcache.bin sh-3.1# apt-get -f install
Reading package lists... Done
Segmentation faulty tree... 50%

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#143 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Martin Michlmayr
Quoted 2 lines

immediately when I started wget, the hanging apt-get process continued.

... and now that we've completed this step, the apt cache has suddenly been reduced (see Gordon's mail for an explanation) and it segfaults:

One of my questions was why apt-get worked to install the initramfs-tools, the kernel and some other packages but later hung while it was building the cache (which clearly it had built already to install some packages): before the installer offers to install additional packages, it changes the apt sources, which leads to apt rebuilding the cache, and here it hangs.

Remember how I said that downloading a file with wget prompts apt to work again? Apparently any filesystem access will do (I just ran find / > /dev/null). Gordon, can you confirm this?

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#144 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Martin Michlmayr
Quoted 11 lines · Martin Michlmayr

... and now that we've completed this step, the apt cache has suddenly been reduced (see Gordon's mail for an explanation) and it segfaults:

sh-3.1# ls -l /var/cache/apt/
total 12524
drwxr-xr-x 3 root root 12288 Dec 22 04:41 archives

(quoted diff) +0 -2
-rw-r--r-- 1 root root 6426885 Dec 22 05:03 pkgcache.bin
-rw-r--r-- 1 root root 6426835 Dec 22 05:03 srcpkgcache.bin

sh-3.1# apt-get -f install
Reading package lists... Done
Segmentation faulty tree... 50%

I think that we are seeing different manifestations of apt's response to corrupted cache files. There does not appear to be any pattern to which manifestation occurs. Maybe it depends on where in the cache file the corruption is located, i.e. when the corruption occurs. Based on the kernel gurus current knowledge of the problem, would you expect the corruption to occur at the same point in a file, or is it possible that the corruption could occur at different points on successive Debian installer attempts on a UP, non PREEMPT system ?

Gordon

Signature · 1 line
Gordon Farquharson
#145 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Gordon Farquharson

Based on the kernel gurus current knowledge of the problem, would you expect the corruption to occur at the same point in a file, or is it possible that the corruption could occur at different points on successive Debian installer attempts on a UP, non PREEMPT system?

Seems like it can occur anywhere. In fact, some people see apt problems because of filesystem corruption on the NSLU2 after they have already installe Debian. I've only seen this once myself and failed many times to find a reproducible situation.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#146 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Martin Michlmayr
Quoted 8 lines · On Fri, 22 Dec 2006 11:00:04 +0100 Martin Michlmayr <[email protected]> wrote:

Review

Quoted 5 lines
(quoted diff) +3 -1
-	if (TestClearPageDirty(page) && account_size)
+	if (TestClearPageDirty(page) && account_size) {
+		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
+	}

This hunk (on top of git from about 2 days ago and your latest patch) results in the installer hanging right at the start.

You'll need this also:

From: Andrew Morton <[email protected]>

Only (un)account for IO and page-dirtying for devices which have real backing store (ie: not tmpfs or ramdisks).

Cc "David S. Miller" <[email protected]>
Cc Linus Torvalds <[email protected]>

---

 mm/truncate.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
mm/truncate.c renamed from mm/truncate.c~truncate-dirty-memory-accounting-fix +2 -1
diff -puN mm/truncate.c~truncate-dirty-memory-accounting-fix mm/truncate.c
@@ -60,7 +60,8 @@ void cancel_dirty_page(struct page *page
 		WARN_ON(++warncount < 5);
 	}
 		
-	if (TestClearPageDirty(page) && account_size) {
+	if (TestClearPageDirty(page) && account_size &&
+			mapping_cap_account_dirty(page->mapping)) {
 		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
 	}

_

#147 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Andrew Morton
Quoted 2 lines

This hunk (on top of git from about 2 days ago and your latest patch) results in the installer hanging right at the start.

You'll need this also:

It starts again, thanks.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#148 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Andrew Morton

With all three patches I have corruption....

fs/buffer.c +1 -17
index d1f1b54..263f88e 100644
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/hugetlbfs/inode.c +1 -1
index ed2c223..4f4cd13 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	cancel_dirty_page(page, /* No IO accounting for huge pages? */0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
(quoted diff) +0 -0

b/include/asm-generic/pgtable.h
index 9d774d0..8879f1d 100644

include/asm-generic/pgtable.h +0 -25
@@ -61,31 +61,6 @@ ({									\
 })
 #endif
 
-#ifndef __HAVE_ARCH_PTEP_TEST_AND_CLEAR_DIRTY
-#define ptep_test_and_clear_dirty(__vma, __address, __ptep)		\
-({									\
-	pte_t __pte = *__ptep;						\
-	int r = 1;							\
-	if (!pte_dirty(__pte))						\
-		r = 0;							\
-	else								\
-		set_pte_at((__vma)->vm_mm, (__address), (__ptep),	\
-			   pte_mkclean(__pte));				\
-	r;								\
-})
-#endif
-
-#ifndef __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH
-#define ptep_clear_flush_dirty(__vma, __address, __ptep)		\
-({									\
-	int __dirty;							\
-	__dirty = ptep_test_and_clear_dirty(__vma, __address, __ptep);	\
-	if (__dirty)							\
-		flush_tlb_page(__vma, __address);			\
-	__dirty;							\
-})
-#endif
-
 #ifndef __HAVE_ARCH_PTEP_GET_AND_CLEAR
 #define ptep_get_and_clear(__mm, __address, __ptep)			\
 ({									\
include/asm-i386/pgtable.h +14 -12
index e6a4723..b61d6f9 100644
@@ -300,18 +300,20 @@ do {									\
 	flush_tlb_page(vma, address);					\
 } while (0)
 
-#define __HAVE_ARCH_PTEP_CLEAR_DIRTY_FLUSH
-#define ptep_clear_flush_dirty(vma, address, ptep)			\
-({									\
-	int __dirty;							\
-	__dirty = pte_dirty(*(ptep));					\
-	if (__dirty) {							\
-		clear_bit(_PAGE_BIT_DIRTY, &(ptep)->pte_low);		\
-		pte_update_defer((vma)->vm_mm, (address), (ptep));	\
-		flush_tlb_page(vma, address);				\
-	}								\
-	__dirty;							\
-})
+/*
+ * "ptep_exchange()" can be used to atomically change a set of
+ * page table protection bits, returning the old ones (the dirty
+ * and accessed bits in particular, since they are set by hw).
+ *
+ * "ptep_flush_dirty()" then returns the dirty status of the
+ * page (on x86-64, we just look at the dirty bit in the returned
+ * pte, but some other architectures have the dirty bits in
+ * other places than the page tables).
+ */
+#define ptep_exchange(vma, address, ptep, old, new) \
+	(old).pte_low = xchg(&(ptep)->pte_low, (new).pte_low);
+#define ptep_flush_dirty(vma, address, ptep, old) \
+	pte_dirty(old)
 
 #define __HAVE_ARCH_PTEP_CLEAR_YOUNG_FLUSH
 #define ptep_clear_flush_young(vma, address, ptep)			\
include/asm-x86_64/pgtable.h +0 -1
index 59901c6..07754b5 100644
@@ -283,12 +283,20 @@ static inline pte_t pte_clrhuge(pte_t pt
 
 struct vm_area_struct;
 
-static inline int ptep_test_and_clear_dirty(struct vm_area_struct *vma,

unsigned long addr, pte_t *ptep)
-{
- if (!pte_dirty(*ptep))
- return 0;
- return test_and_clear_bit(_PAGE_BIT_DIRTY, &ptep->pte);
-}
+/*
+ * "ptep_exchange()" can be used to atomically change a set of
+ * page table protection bits, returning the old ones (the dirty
+ * and accessed bits in particular, since they are set by hw).
+ *
+ * "ptep_flush_dirty()" then returns the dirty status of the
+ * page (on x86-64, we just look at the dirty bit in the returned
+ * pte, but some other architectures have the dirty bits in
+ * other places than the page tables).
+ */
+#define ptep_exchange(vma, address, ptep, old, new) \
+ (old).pte = xchg(&(ptep)->pte, (new).pte);
+#define ptep_flush_dirty(vma, address, ptep, old) \
+ pte_dirty(old)

static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{

include/linux/page-flags.h +1 -1
index 4830a3b..350878a 100644
@@ -253,15 +253,11 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+extern void cancel_dirty_page(struct page *page, unsigned int

account_size);
+
int test_clear_page_writeback(struct page *page); int test_set_page_writeback(struct page *page);

-static inline void clear_page_dirty(struct page *page) -{
- test_clear_page_dirty(page);
-}
-
static inline void set_page_writeback(struct page *page) {
test_set_page_writeback(page);

mm/memory.c +28 -0
index c00bac6..79cecab 100644
@@ -1842,6 +1842,33 @@ void unmap_mapping_range(struct address_
 }
 EXPORT_SYMBOL(unmap_mapping_range);
 
+static void check_last_page(struct address_space *mapping, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+	struct page *page;
+
+	if (!mapping)
+		return;
+	offset = size & ~PAGE_MASK;
+	if (!offset)
+		return;
+	index = size >> PAGE_SHIFT;
+	page = find_lock_page(mapping, index);
+	if (page) {
+		unsigned int check = 0;
+		unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+		do {
+			check += kaddr[offset++];
+		} while (offset < PAGE_SIZE);
+		kunmap_atomic(kaddr,KM_USER0);
+		unlock_page(page);
+		page_cache_release(page);
+		if (check)
+			printk("%s: BADNESS: truncate check %u\n", current->comm, check);
+	}
+}
+
 /**
  * vmtruncate - unmap mappings "freed" by truncate() syscall
  * @inode: inode of the file used
@@ -1875,6 +1902,7 @@ do_expand:
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
 		goto out_big;
+	check_last_page(mapping, inode->i_size);
 	i_size_write(inode, offset);
 
 out_truncate:
mm/page-writeback.c +0 -1
index 237107c..b3a198c 100644
@@ -845,38 +845,6 @@ int set_page_dirty_lock(struct page *pag
 EXPORT_SYMBOL(set_page_dirty_lock);
 
 /*
- * Clear a page's dirty flag, while caring for dirty memory

accounting.
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
- struct address_space *mapping = page_mapping(page);
- unsigned long flags;
-
- if (!mapping)
- return TestClearPageDirty(page);
-
- write_lock_irqsave(&mapping->tree_lock, flags);
- if (TestClearPageDirty(page)) {
- radix_tree_tag_clear(&mapping->page_tree,
- page_index(page), PAGECACHE_TAG_DIRTY);
- write_unlock_irqrestore(&mapping->tree_lock, flags);
- /*
- * We can continue to use `mapping' here because the
- * page is locked, which pins the address_space
- */
- if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
- dec_zone_page_state(page, NR_FILE_DIRTY);
- }
- return 1;
- }
- write_unlock_irqrestore(&mapping->tree_lock, flags);
- return 0;
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
-/*
* Clear a page's dirty flag, while caring for dirty memory accounting.
* Returns true if the page was previously dirty.
*

mm/rmap.c +19 -17
index d8a842a..a028803 100644
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page 
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *ptep;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -440,22 +440,24 @@ static int page_mkclean_one(struct page 
 	if (address == -EFAULT)
 		goto out;
 
-	pte = page_check_address(page, mm, address, &ptl);
-	if (!pte)
-		goto out;
-
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
-
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
-
-unlock:
-	pte_unmap_unlock(pte, ptl);
+	ptep = page_check_address(page, mm, address, &ptl);
+	if (ptep) {
+		pte_t old, new;
+
+		old = *ptep;
+		new = pte_wrprotect(pte_mkclean(old));
+		if (!pte_same(old, new)) {
+			for (;;) {
+				flush_cache_page(vma, address, page_to_pfn(page));
+				ptep_exchange(vma, address, ptep, old, new);
+				if (pte_same(old, new))
+					break;
+				ret |= ptep_flush_dirty(vma, address, ptep, old);
+				flush_tlb_page(vma, address);
+			}
+		}
+		pte_unmap_unlock(pte, ptl);
+	}
 out:
 	return ret;
 }
mm/truncate.c +3 -0
index 9bfb8e8..4a38dd1 100644
@@ -51,6 +51,22 @@ static inline void truncate_partial_page
 		do_invalidatepage(page, partial);
 }
 
+void cancel_dirty_page(struct page *page, unsigned int account_size)
+{
+	/* If we're cancelling the page, it had better not be mapped any more

*/
+ if (page_mapped(page)) {
+ static unsigned int warncount;
+
+ WARN_ON(++warncount < 5);
+ }
+
+ if (TestClearPageDirty(page) && account_size &&
+ mapping_cap_account_dirty(page->mapping)) {
+ dec_zone_page_state(page, NR_FILE_DIRTY);
+ task_io_account_cancelled_write(account_size);
+ }
+}
+
/*
* 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
@@ -67,11 +83,11 @@ truncate_complete_page(struct address_sp
if (page->mapping != mapping)
return;

+ cancel_dirty_page(page, PAGE_CACHE_SIZE);
+
if (PagePrivate(page))
do_invalidatepage(page, 0);

- if (test_clear_page_dirty(page))
- task_io_account_cancelled_write(PAGE_CACHE_SIZE);
ClearPageUptodate(page); ClearPageMappedToDisk(page); remove_from_page_cache(page);
@@ -350,7 +366,6 @@ int invalidate_inode_pages2_range(struct
for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
struct page *page = pvec.pages[i]; pgoff_t page_index;
- int was_dirty;

lock_page(page);
if (page->mapping != mapping) {
@@ -386,12 +401,8 @@ int invalidate_inode_pages2_range(struct
PAGE_CACHE_SIZE, 0);
}
}
- was_dirty = test_clear_page_dirty(page);
- if (!invalidate_complete_page2(mapping, page)) {
- if (was_dirty)
- set_page_dirty(page);
+ if (!invalidate_complete_page2(mapping, page))
ret = -EIO;
- }
unlock_page(page);
}
pagevec_release(&pvec);

Quoted 42 lines · Andrew Morton
Quoted 8 lines · On Fri, 22 Dec 2006 11:00:04 +0100 Martin Michlmayr <[email protected]> wrote:

Review

Quoted 5 lines
(quoted diff) +3 -1
-	if (TestClearPageDirty(page) && account_size)
+	if (TestClearPageDirty(page) && account_size) {
+		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
+	}

This hunk (on top of git from about 2 days ago and your latest patch) results in the installer hanging right at the start.

You'll need this also:

From: Andrew Morton <[email protected]>

Only (un)account for IO and page-dirtying for devices which have real backing store (ie: not tmpfs or ramdisks).

Cc "David S. Miller" <[email protected]>
Cc Linus Torvalds <[email protected]>

---

 mm/truncate.c |    3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff -puN mm/truncate.c~truncate-dirty-memory-accounting-fix mm/truncate.c

mm/truncate.c renamed from mm/truncate.c~truncate-dirty-memory-accounting-fix +2 -1
@@ -60,7 +60,8 @@ void cancel_dirty_page(struct page *page
 		WARN_ON(++warncount < 5);
 	}
 
-	if (TestClearPageDirty(page) && account_size) {
+	if (TestClearPageDirty(page) && account_size &&
+			mapping_cap_account_dirty(page->mapping)) {
 		dec_zone_page_state(page, NR_FILE_DIRTY);
 		task_io_account_cancelled_write(account_size);
 	}

_

#149 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · Andrei Popa

With all three patches I have corruption....

I've completed one installation with Linus' patch plus the two from Andrew successfully, but I'm currently trying again... but I really need a better testcase since an installation takes about an hour. Andrei, which torrent do you download as a testcase? It would be good if someone could suggest a torrent which is legal and not too large.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#150 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 2 lines · Martin Michlmayr

I've completed one installation with Linus' patch plus the two from Andrew successfully, but I'm currently trying again...

... and it failed.

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
#151 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 5 lines · Martin Michlmayr
Quoted 2 lines · Martin Michlmayr

I've completed one installation with Linus' patch plus the two from Andrew successfully, but I'm currently trying again...

.... and it failed.

Since you are on ARM you might want to try with the page_mkclean_one cleanup patch too.

Arjan agreed that the loop is not needed; we clear the pte, flush on all CPUs and then re-establish the pte. Any race will fault and be serialised on the pte lock.

FWIW - with todays -git and Andrews second cancel_dirty_page() patch:
http://lkml.org/lkml/2006/12/22/49
I am unable to trigger any corruption - I could again earlier by raising the number of seeds from 3 to 6. (am currently at 10 seeds)

From: Peter Zijlstra <[email protected]>

fix page_mkclean_one()

- add flush_cache_page() for all those virtual indexed cache architectures.

- handle s390.

---
 mm/rmap.c |   38 +++++++++++++++++++++++++-------------
 1 file changed, 25 insertions(+), 13 deletions(-)
mm/rmap.c +13 -10
Index: linux-2.6/mm/rmap.c
===================================================================
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page 
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *pte;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page 
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	if (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_clear_flush(vma, address, pte);
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		set_pte_at(vma, address, pte, entry);
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
@@ -489,6 +490,8 @@ int page_mkclean(struct page *page)
 		if (mapping)
 			ret = page_mkclean_file(mapping, page);
 	}
+	if (page_test_and_clear_dirty(page))
+		ret = 1;
 
 	return ret;
 }
#152 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

A cleanup of try_to_unmap. I have not identified any races that this would solve, but for consistencies sake.

Also includes a small s390 optimization by moving page_test_and_clear_dirty() out of the vma iteration.

From: Peter Zijlstra <[email protected]>

We clear the page in the following sequence:
ClearPageDirty - lock ptl, clear pte, unlock ptl

hence we should dirty in the opposite order:
lock ptl, clear pte, unlock ptl - SetPageDirty

try_to_unmap_one violates this by doing the SetPageDirty under the ptl.

Also move page_test_and_clear_dirty() to try_to_unmap().

---
 mm/rmap.c |   10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)
mm/rmap.c +7 -3
Index: linux-2.6/mm/rmap.c
===================================================================
@@ -590,8 +590,6 @@ void page_remove_rmap(struct page *page)
 		 * Leaving it set also helps swapoff to reinstate ptes
 		 * faster for those pages still in swapcache.
 		 */
-		if (page_test_and_clear_dirty(page))
-			set_page_dirty(page);
 		__dec_zone_page_state(page,
 				PageAnon(page) ? NR_ANON_PAGES : NR_FILE_MAPPED);
 	}
@@ -610,6 +608,7 @@ static int try_to_unmap_one(struct page 
 	pte_t pteval;
 	spinlock_t *ptl;
 	int ret = SWAP_AGAIN;
+	struct page *dirty_page = NULL;
 
 	address = vma_address(page, vma);
 	if (address == -EFAULT)
@@ -636,7 +635,7 @@ static int try_to_unmap_one(struct page 
 
 	/* Move the dirty bit to the physical page now the pte is gone. */
 	if (pte_dirty(pteval))
-		set_page_dirty(page);
+		dirty_page = page;
 
 	/* Update high watermark before we lower rss */
 	update_hiwater_rss(mm);
@@ -687,6 +686,8 @@ static int try_to_unmap_one(struct page 
 
 out_unmap:
 	pte_unmap_unlock(pte, ptl);
+	if (dirty_page)
+		set_page_dirty(dirty_page);
 out:
 	return ret;
 }
@@ -918,6 +919,9 @@ int try_to_unmap(struct page *page, int 
 	else
 		ret = try_to_unmap_file(page, migration);
 
+	if (page_test_and_clear_dirty(page))
+		set_page_dirty(page);
+
 	if (!page_mapped(page))
 		ret = SWAP_SUCCESS;
 	return ret;
#153 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 5 lines · Peter Zijlstra

fix page_mkclean_one()

- add flush_cache_page() for all those virtual indexed cache architectures.

I think the flush_cache_page() should be after we've actually flushed it from the TLB and re-inserted it (this is one reason why I did the "ptep_exchange()" version of this). Otherwise somebody can still write to the page _after_ the cache flush..

Quoted 1 line

- handle s390.

Yeah, that looks like the proper way to handle that.

That said, it looks like we still see corruption. You may not, but Martin and Andrei still report problems, even with all the patches (including the last one from Andrew that avoids "dirty" going negative under some circumstances, and explains the "slow and/or never completed" case that Gordon and Martin saw).

The good news is that I think the code now is cleaner and more understandable. The bad news is that nothing we've ever tried seems to have fixed the _problem_.

And I don't think it's page_mkclean(). Especially not since the ARM people are seeing this under UP without PREEMPT. In that kind of schenario, the only possible races tend to be from things that actually block:
"set_page_dirty()" (which blocks on IO in balancing), memory allocations, and obviously doing actual IO.

And it's not a virtual cache problem, since others see it on x86.

Of course, since it's quite possibly two different issues, maybe the virtual cache flush is required in order to force write-back to memory (which in turn is required for the DMA for the actual write!). So the ARM issue certainly could be due to the flush_cache_page() thing...

Linus

#154 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Peter Zijlstra
Quoted 3 lines · Peter Zijlstra
Quoted 1 line

.... and it failed.

Since you are on ARM you might want to try with the page_mkclean_one cleanup patch too.

I've already tried it and it didn't work. I just tried it again together with Linus' patch and the two from Andrew and it still fails. (For reference, the patch is attached.)

Signature · 2 lines
Martin Michlmayr
http://www.cyrius.com/
fs/buffer.c +1 -17
@@ -2834,7 +2834,7 @@ int try_to_free_buffers(struct page *pag
 	int ret = 0;
 
 	BUG_ON(!PageLocked(page));
-	if (PageWriteback(page))
+	if (PageDirty(page) || PageWriteback(page))
 		return 0;
 
 	if (mapping == NULL) {		/* can this still happen? */
@@ -2845,22 +2845,6 @@ int try_to_free_buffers(struct page *pag
 	spin_lock(&mapping->private_lock);
 	ret = drop_buffers(page, &buffers_to_free);
 	spin_unlock(&mapping->private_lock);
-	if (ret) {
-		/*
-		 * If the filesystem writes its buffers by hand (eg ext3)
-		 * then we can have clean buffers against a dirty page.  We
-		 * clean the page here; otherwise later reattachment of buffers
-		 * could encounter a non-uptodate page, which is unresolvable.
-		 * This only applies in the rare case where try_to_free_buffers
-		 * succeeds but the page is not freed.
-		 *
-		 * Also, during truncate, discard_buffer will have marked all
-		 * the page's buffers clean.  We discover that here and clean
-		 * the page also.
-		 */
-		if (test_clear_page_dirty(page))
-			task_io_account_cancelled_write(PAGE_CACHE_SIZE);
-	}
 out:
 	if (buffers_to_free) {
 		struct buffer_head *bh = buffers_to_free;
fs/hugetlbfs/inode.c +1 -1
index ed2c223..4f4cd13 100644
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct
 
 static void truncate_huge_page(struct page *page)
 {
-	clear_page_dirty(page);
+	cancel_dirty_page(page, /* No IO accounting for huge pages? */0);
 	ClearPageUptodate(page);
 	remove_from_page_cache(page);
 	put_page(page);
include/linux/page-flags.h +2 -6
index 4830a3b..350878a 100644
@@ -253,15 +253,11 @@ #define ClearPageUncached(page)	clear_bi
 
 struct page;	/* forward declaration */
 
-int test_clear_page_dirty(struct page *page);
+extern void cancel_dirty_page(struct page *page, unsigned int account_size);
+
 int test_clear_page_writeback(struct page *page);
 int test_set_page_writeback(struct page *page);
 
-static inline void clear_page_dirty(struct page *page)
-{
-	test_clear_page_dirty(page);
-}
-
 static inline void set_page_writeback(struct page *page)
 {
 	test_set_page_writeback(page);
mm/memory.c +28 -0
index c00bac6..79cecab 100644
@@ -1842,6 +1842,33 @@ void unmap_mapping_range(struct address_
 }
 EXPORT_SYMBOL(unmap_mapping_range);
 
+static void check_last_page(struct address_space *mapping, loff_t size)
+{
+	pgoff_t index;
+	unsigned int offset;
+	struct page *page;
+
+	if (!mapping)
+		return;
+	offset = size & ~PAGE_MASK;
+	if (!offset)
+		return;
+	index = size >> PAGE_SHIFT;
+	page = find_lock_page(mapping, index);
+	if (page) {
+		unsigned int check = 0;
+		unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+		do {
+			check += kaddr[offset++];
+		} while (offset < PAGE_SIZE);
+		kunmap_atomic(kaddr,KM_USER0);
+		unlock_page(page);
+		page_cache_release(page);
+		if (check)
+			printk("%s: BADNESS: truncate check %u\n", current->comm, check);
+	}
+}
+
 /**
  * vmtruncate - unmap mappings "freed" by truncate() syscall
  * @inode: inode of the file used
@@ -1875,6 +1902,7 @@ do_expand:
 		goto out_sig;
 	if (offset > inode->i_sb->s_maxbytes)
 		goto out_big;
+	check_last_page(mapping, inode->i_size);
 	i_size_write(inode, offset);
 
 out_truncate:
mm/page-writeback.c +0 -32
index 237107c..b3a198c 100644
@@ -845,38 +845,6 @@ int set_page_dirty_lock(struct page *pag
 EXPORT_SYMBOL(set_page_dirty_lock);
 
 /*
- * Clear a page's dirty flag, while caring for dirty memory accounting. 
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
-	struct address_space *mapping = page_mapping(page);
-	unsigned long flags;
-
-	if (!mapping)
-		return TestClearPageDirty(page);
-
-	write_lock_irqsave(&mapping->tree_lock, flags);
-	if (TestClearPageDirty(page)) {
-		radix_tree_tag_clear(&mapping->page_tree,
-				page_index(page), PAGECACHE_TAG_DIRTY);
-		write_unlock_irqrestore(&mapping->tree_lock, flags);
-		/*
-		 * We can continue to use `mapping' here because the
-		 * page is locked, which pins the address_space
-		 */
-		if (mapping_cap_account_dirty(mapping)) {
-			page_mkclean(page);
-			dec_zone_page_state(page, NR_FILE_DIRTY);
-		}
-		return 1;
-	}
-	write_unlock_irqrestore(&mapping->tree_lock, flags);
-	return 0;
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
-/*
  * Clear a page's dirty flag, while caring for dirty memory accounting.
  * Returns true if the page was previously dirty.
  *
mm/rmap.c +13 -10
index d8a842a..3278b2a 100644
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page
 {
 	struct mm_struct *mm = vma->vm_mm;
 	unsigned long address;
-	pte_t *pte, entry;
+	pte_t *pte;
 	spinlock_t *ptl;
 	int ret = 0;
 
@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page
 	if (!pte)
 		goto out;
 
-	if (!pte_dirty(*pte) && !pte_write(*pte))
-		goto unlock;
+	if (pte_dirty(*pte) || pte_write(*pte)) {
+		pte_t entry;
 
-	entry = ptep_get_and_clear(mm, address, pte);
-	entry = pte_mkclean(entry);
-	entry = pte_wrprotect(entry);
-	ptep_establish(vma, address, pte, entry);
-	lazy_mmu_prot_update(entry);
-	ret = 1;
+		flush_cache_page(vma, address, pte_pfn(*pte));
+		entry = ptep_clear_flush(vma, address, pte);
+		entry = pte_wrprotect(entry);
+		entry = pte_mkclean(entry);
+		set_pte_at(vma, address, pte, entry);
+		lazy_mmu_prot_update(entry);
+		ret = 1;
+	}
 
-unlock:
 	pte_unmap_unlock(pte, ptl);
 out:
 	return ret;
@@ -489,6 +490,8 @@ int page_mkclean(struct page *page)
 		if (mapping)
 			ret = page_mkclean_file(mapping, page);
 	}
+	if (page_test_and_clear_dirty(page))
+		ret = 1;
 
 	return ret;
 }
mm/truncate.c +19 -8
index 9bfb8e8..4a38dd1 100644
@@ -51,6 +51,22 @@ static inline void truncate_partial_page
 		do_invalidatepage(page, partial);
 }
 
+void cancel_dirty_page(struct page *page, unsigned int account_size)
+{
+	/* If we're cancelling the page, it had better not be mapped any more */
+	if (page_mapped(page)) {
+		static unsigned int warncount;
+
+		WARN_ON(++warncount < 5);
+	}
+		
+	if (TestClearPageDirty(page) && account_size &&
+			mapping_cap_account_dirty(page->mapping)) {
+		dec_zone_page_state(page, NR_FILE_DIRTY);
+		task_io_account_cancelled_write(account_size);
+	}
+}
+
 /*
  * 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
@@ -67,11 +83,11 @@ truncate_complete_page(struct address_sp
 	if (page->mapping != mapping)
 		return;
 
+	cancel_dirty_page(page, PAGE_CACHE_SIZE);
+
 	if (PagePrivate(page))
 		do_invalidatepage(page, 0);
 
-	if (test_clear_page_dirty(page))
-		task_io_account_cancelled_write(PAGE_CACHE_SIZE);
 	ClearPageUptodate(page);
 	ClearPageMappedToDisk(page);
 	remove_from_page_cache(page);
@@ -350,7 +366,6 @@ int invalidate_inode_pages2_range(struct
 		for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
 			struct page *page = pvec.pages[i];
 			pgoff_t page_index;
-			int was_dirty;
 
 			lock_page(page);
 			if (page->mapping != mapping) {
@@ -386,12 +401,8 @@ int invalidate_inode_pages2_range(struct
 					  PAGE_CACHE_SIZE, 0);
 				}
 			}
-			was_dirty = test_clear_page_dirty(page);
-			if (!invalidate_complete_page2(mapping, page)) {
-				if (was_dirty)
-					set_page_dirty(page);
+			if (!invalidate_complete_page2(mapping, page))
 				ret = -EIO;
-			}
 			unlock_page(page);
 		}
 		pagevec_release(&pvec);
#155 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 8 lines · Martin Michlmayr
Quoted 3 lines · Peter Zijlstra
Quoted 1 line

.... and it failed.

Since you are on ARM you might want to try with the page_mkclean_one cleanup patch too.

I've already tried it and it didn't work. I just tried it again together with Linus' patch and the two from Andrew and it still fails. (For reference, the patch is attached.)

I can confirm this behaviour with 2.6.19 and the patches mentioned above (cumulative patch for 2.6.19 appended to the end of this email).

Is there any way to provide any debugging information that may help solve the problem ? Would it help to know the nature of the corruption e.g. an analysis of the corruption in the file ? I have previously asked apt developers if they wanted to look at the corrupted cache files, but there were no takers then.

BTW, I decided to try Linus's test program [1] on ARM (I don't think that anybody had tried it on ARM before).

Since we see file corruption with 2.6.18 + [PATCH] mm: tracking shared dirty pages [2], I ran Linus's program on machines with the following setups:

2.6.18 + the following patches
mm: tracking shared dirty pages [2]
mm: balance dirty pages [3]
mm: optimize the new mprotect() code a bit [4]
mm: small cleanup of install_page() [5]
mm: fixup do_wp_page() [6]
mm: msync() cleanup [7]

$ ./mm-test | od -x
0000000 aaaa aaaa aaaa aaaa aaaa 0000 0000 0000
0000020 0000 0000 5555 5555 5555 5555 5555 5555
0000040 5555 5555 5555 5555
0000050

2.6.18 (no mm patches)

$ ./mm-test | od -x
0000000 aaaa aaaa aaaa aaaa aaaa aaaa aaaa aaaa
0000020 aaaa aaaa 5555 5555 5555 5555 5555 5555
0000040 5555 5555 5555 5555
0000050

I don't know if this helps at all.

Gordon

[1] http://lkml.org/lkml/2006/12/19/200
[2] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=d08b3851da41d0ee60851f2c75b118e1f7a5fc89
[3] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=edc79b2a46ed854595e40edcf3f8b37f9f14aa3f
[4] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=c1e6098b23bb46e2b488fe9a26f831f867157483
[5] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=e88dd6c11c5aef74d8b74a062767add53315533b
[6] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=ee6a6457886a80415db209e87033b63f2b06558c
[7] http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=204ec841fbea3e5138168edbc3a76d46747cc987

fs/buffer.c 2006-12-21 01:16:31.000000000 -0700 renamed from fs/buffer.c 2006-11-29 14:57:37.000000000 -0700 +1 -12
diff -Naupr linux-2.6.19.orig/fs/buffer.c linux-2.6.19/fs/buffer.c
@@ -2832,7 +2832,7 @@ int try_to_free_buffers(struct page *pag
       int ret = 0;
 
       BUG_ON(!PageLocked(page));
-       if (PageWriteback(page))
+       if (PageDirty(page) || PageWriteback(page))
               return 0;
 
       if (mapping == NULL) {          /* can this still happen? */
@@ -2843,17 +2843,6 @@ int try_to_free_buffers(struct page *pag
       spin_lock(&mapping->private_lock);
       ret = drop_buffers(page, &buffers_to_free);
       spin_unlock(&mapping->private_lock);
-       if (ret) {
-               /*
-                * If the filesystem writes its buffers by hand (eg ext3)
-                * then we can have clean buffers against a dirty page.  We
-                * clean the page here; otherwise later reattachment of buffers
-                * could encounter a non-uptodate page, which is unresolvable.
-                * This only applies in the rare case where try_to_free_buffers
-                * succeeds but the page is not freed.
-                */
-               clear_page_dirty(page);
-       }

out:

       if (buffers_to_free) {
               struct buffer_head *bh = buffers_to_free;

diff -Naupr linux-2.6.19.orig/fs/hugetlbfs/inode.c linux-2.6.19/fs/hugetlbfs/inode.c
--- linux-2.6.19.orig/fs/hugetlbfs/inode.c 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/fs/hugetlbfs/inode.c 2006-12-21 01:15:21.000000000 -0700
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct

static void truncate_huge_page(struct page *page)
{
- clear_page_dirty(page);
+ cancel_dirty_page(page, /* No IO accounting for huge pages? */0);

       ClearPageUptodate(page);
       remove_from_page_cache(page);
       put_page(page);

diff -Naupr linux-2.6.19.orig/include/linux/page-flags.h linux-2.6.19/include/linux/page-flags.h
--- linux-2.6.19.orig/include/linux/page-flags.h 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/include/linux/page-flags.h 2006-12-21
01:15:21.000000000 -0700
@@ -253,15 +253,11 @@ static inline void SetPageUptodate(struc

struct page; /* forward declaration */

-int test_clear_page_dirty(struct page *page);
+extern void cancel_dirty_page(struct page *page, unsigned int account_size); +
int test_clear_page_writeback(struct page *page);
int test_set_page_writeback(struct page *page);

-static inline void clear_page_dirty(struct page *page) -{
- test_clear_page_dirty(page);
-}
-
static inline void set_page_writeback(struct page *page)

{
       test_set_page_writeback(page);
mm/memory.c 2006-12-21 01:15:21.000000000 -0700 renamed from mm/memory.c 2006-11-29 14:57:37.000000000 -0700 +0 -0
diff -Naupr linux-2.6.19.orig/mm/memory.c linux-2.6.19/mm/memory.c
@@ -1832,6 +1832,33 @@ void unmap_mapping_range(struct address_

}
EXPORT_SYMBOL(unmap_mapping_range);

+static void check_last_page(struct address_space *mapping, loff_t size) +{

+       pgoff_t index;
+       unsigned int offset;
+       struct page *page;

+
+ if (!mapping)
+ return;
+ offset = size & ~PAGE_MASK;
+ if (!offset)
+ return;

+       index = size >> PAGE_SHIFT;
+       page = find_lock_page(mapping, index);
+       if (page) {
+               unsigned int check = 0;
+               unsigned char *kaddr = kmap_atomic(page, KM_USER0);
+               do {
+                       check += kaddr[offset++];
+               } while (offset < PAGE_SIZE);
+               kunmap_atomic(kaddr,KM_USER0);
+               unlock_page(page);
+               page_cache_release(page);
+               if (check)

+ printk("%s: BADNESS: truncate check %u\n",
current->comm, check);
+ }
+}
+
/**
* vmtruncate - unmap mappings "freed" by truncate() syscall
* @inode: inode of the file used
@@ -1865,6 +1892,7 @@ do_expand:

               goto out_sig;
       if (offset > inode->i_sb->s_maxbytes)
               goto out_big;
+       check_last_page(mapping, inode->i_size);
       i_size_write(inode, offset);

out_truncate:
diff -Naupr linux-2.6.19.orig/mm/page-writeback.c linux-2.6.19/mm/page-writeback.c
--- linux-2.6.19.orig/mm/page-writeback.c 2006-11-29
14:57:37.000000000 -0700
+++ linux-2.6.19/mm/page-writeback.c 2006-12-21 01:26:53.000000000 -0700
@@ -843,39 +843,6 @@ int set_page_dirty_lock(struct page *pag EXPORT_SYMBOL(set_page_dirty_lock);

/*
- * Clear a page's dirty flag, while caring for dirty memory accounting.
- * Returns true if the page was previously dirty.
- */
-int test_clear_page_dirty(struct page *page)
-{
- struct address_space *mapping = page_mapping(page);
- unsigned long flags;
-
- if (mapping) {
- write_lock_irqsave(&mapping->tree_lock, flags);
- if (TestClearPageDirty(page)) {
- radix_tree_tag_clear(&mapping->page_tree,
- page_index(page),
- PAGECACHE_TAG_DIRTY);
- write_unlock_irqrestore(&mapping->tree_lock, flags);
- /*

-                        * We can continue to use `mapping' here because the
-                        * page is locked, which pins the address_space
-                        */

- if (mapping_cap_account_dirty(mapping)) {
- page_mkclean(page);
- dec_zone_page_state(page, NR_FILE_DIRTY);
- }
- return 1;

-               }
-               write_unlock_irqrestore(&mapping->tree_lock, flags);
-               return 0;

- }
- return TestClearPageDirty(page);
-}
-EXPORT_SYMBOL(test_clear_page_dirty);
-
-/*
* Clear a page's dirty flag, while caring for dirty memory accounting.
* Returns true if the page was previously dirty.
*

mm/rmap.c 2006-12-22 23:25:09.000000000 -0700 renamed from mm/rmap.c 2006-11-29 14:57:37.000000000 -0700 +0 -0
diff -Naupr linux-2.6.19.orig/mm/rmap.c linux-2.6.19/mm/rmap.c
@@ -432,7 +432,7 @@ static int page_mkclean_one(struct page
{
       struct mm_struct *mm = vma->vm_mm;
       unsigned long address;
-       pte_t *pte, entry;
+       pte_t *pte;
       spinlock_t *ptl;
       int ret = 0;

@@ -444,17 +444,18 @@ static int page_mkclean_one(struct page

       if (!pte)
               goto out;

- if (!pte_dirty(*pte) && !pte_write(*pte))
- goto unlock;
+ if (pte_dirty(*pte) || pte_write(*pte)) {
+ pte_t entry;

-       entry = ptep_get_and_clear(mm, address, pte);
-       entry = pte_mkclean(entry);
-       entry = pte_wrprotect(entry);
-       ptep_establish(vma, address, pte, entry);
-       lazy_mmu_prot_update(entry);
-       ret = 1;
+               flush_cache_page(vma, address, pte_pfn(*pte));
+               entry = ptep_clear_flush(vma, address, pte);
+               entry = pte_wrprotect(entry);
+               entry = pte_mkclean(entry);
+               set_pte_at(vma, address, pte, entry);
+               lazy_mmu_prot_update(entry);
+               ret = 1;

+ }

-unlock:

       pte_unmap_unlock(pte, ptl);
out:
       return ret;
@@ -489,6 +490,8 @@ int page_mkclean(struct page *page)
               if (mapping)
                       ret = page_mkclean_file(mapping, page);
       }

+ if (page_test_and_clear_dirty(page))
+ ret = 1;

       return ret;
}

@@ -587,8 +590,6 @@ void page_remove_rmap(struct page *page)
* Leaving it set also helps swapoff to reinstate ptes
* faster for those pages still in swapcache. */
- if (page_test_and_clear_dirty(page))
- set_page_dirty(page);
__dec_zone_page_state(page,
PageAnon(page) ? NR_ANON_PAGES :

NR_FILE_MAPPED);
       }
@@ -607,6 +608,7 @@ static int try_to_unmap_one(struct page
       pte_t pteval;
       spinlock_t *ptl;
       int ret = SWAP_AGAIN;

+ struct page *dirty_page = NULL;

       address = vma_address(page, vma);
       if (address == -EFAULT)

@@ -633,7 +635,7 @@ static int try_to_unmap_one(struct page

       /* Move the dirty bit to the physical page now the pte is gone. */
       if (pte_dirty(pteval))

- set_page_dirty(page);
+ dirty_page = page;

       /* Update high watermark before we lower rss */
       update_hiwater_rss(mm);

@@ -684,6 +686,8 @@ static int try_to_unmap_one(struct page

out_unmap:

       pte_unmap_unlock(pte, ptl);
+       if (dirty_page)
+               set_page_dirty(dirty_page);
out:
       return ret;
}
@@ -915,6 +919,9 @@ int try_to_unmap(struct page *page, int
       else
               ret = try_to_unmap_file(page, migration);

+ if (page_test_and_clear_dirty(page))
+ set_page_dirty(page);
+

       if (!page_mapped(page))
               ret = SWAP_SUCCESS;
       return ret;
mm/truncate.c 2006-12-23 13:21:42.000000000 -0700 renamed from mm/truncate.c 2006-11-29 14:57:37.000000000 -0700 +0 -0
diff -Naupr linux-2.6.19.orig/mm/truncate.c linux-2.6.19/mm/truncate.c
@@ -50,6 +50,21 @@ static inline void truncate_partial_page
               do_invalidatepage(page, partial);

}

+void cancel_dirty_page(struct page *page, unsigned int account_size) +{
+ /* If we're cancelling the page, it had better not be mapped
any more */+ if (page_mapped(page)) {
+ static unsigned int warncount;
+
+ WARN_ON(++warncount < 5);
+ }
+
+ if (TestClearPageDirty(page) && account_size &&
+ mapping_cap_account_dirty(page->mapping))
+ dec_zone_page_state(page, NR_FILE_DIRTY);
+}
+
+
/*
* 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
@@ -66,10 +81,11 @@ truncate_complete_page(struct address_sp

       if (page->mapping != mapping)
               return;

+ cancel_dirty_page(page, PAGE_CACHE_SIZE);
+

       if (PagePrivate(page))
               do_invalidatepage(page, 0);
-       clear_page_dirty(page);
       ClearPageUptodate(page);
       ClearPageMappedToDisk(page);
       remove_from_page_cache(page);
@@ -348,7 +364,6 @@ int invalidate_inode_pages2_range(struct
               for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
                       struct page *page = pvec.pages[i];
                       pgoff_t page_index;

- int was_dirty;

                       lock_page(page);
                       if (page->mapping != mapping) {
@@ -384,12 +399,8 @@ int invalidate_inode_pages2_range(struct
                                         PAGE_CACHE_SIZE, 0);
                               }
                       }

- was_dirty = test_clear_page_dirty(page);
- if (!invalidate_complete_page2(mapping, page)) {
- if (was_dirty)
- set_page_dirty(page);
+ if (!invalidate_complete_page2(mapping, page))

                               ret = -EIO;
-                       }
                       unlock_page(page);
               }
               pagevec_release(&pvec);
Signature · 1 line
Gordon Farquharson
#156 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 3 lines · Gordon Farquharson

Is there any way to provide any debugging information that may help solve the problem ?

I think we have people working on this. I know I'm trying to even come up with an idea of what is going on. I don't think we know yet.

Quoted 2 lines

Would it help to know the nature of the corruption e.g. an analysis of the corruption in the file ?

I actually think we know that, because Andrei already gave details. The corruption seems to be basically a few pages that get zeroes at the end rather than the expected contents. That's consistent with the page being written out once, but then _not_ getting written out again despite being dirtied some more.

But if you see ay other pattern, please holler, because that would be interesting.

Quoted 2 lines

BTW, I decided to try Linus's test program [1] on ARM (I don't think that anybody had tried it on ARM before).

You get the expected results, and in fact, I'd be very surprised if you didn't. It's something subtler than that going on.

I now _suspect_ that we're talking about something like

- we started a writeout. The IO is still pending, and the page was marked clean and is now in the "writeback" phase.
- a write happens to the page, and the page gets marked dirty again. Marking the page dirty also marks all the _buffers_ in the page dirty, but they were actually already dirty, because the IO hasn't completed yet.
- the IO from the _previous_ write completes, and marks the buffers clean again.

And no, thatr's not actually what is going on. The thing is, we actually clear the buffer dirty bits when we start the IO, not when we end it, but I think it is going to be this _kind_ of situation, where we missed something, and marked it clean too late, and thus cleared a dirty bit.

I don't think it's a page table issue any more, it just doesn't look likely with the ARM UP corruption. It's also not apparently even on a cacheline boundary, so it probably is really a dirty bit that got cleared wrogn due to some race with IO.

But right now we're all clueless. I personally suspect it's not even a new
bug: it's probably an old bug that simply didn't matter before.

Linus

#157 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 10 lines · On Sun, 24 Dec 2006 00:43:54 -0800 (PST) Linus Torvalds <[email protected]>…

I now _suspect_ that we're talking about something like

- we started a writeout. The IO is still pending, and the page was marked clean and is now in the "writeback" phase.
- a write happens to the page, and the page gets marked dirty again. Marking the page dirty also marks all the _buffers_ in the page dirty, but they were actually already dirty, because the IO hasn't completed yet.
- the IO from the _previous_ write completes, and marks the buffers clean again.

Some things for the testers to try, please:

- mount the fs with ext2 with the no-buffer-head option. That means either:

grub.conf: rootfstype=ext2 rootflags=nobh
/etc/fstab: ext2 nobh

- mount the fs with ext3 data=writeback, nobh

grub.conf: rootfstype=ext3 rootflags=nobh,data=writeback (I hope this works) /etc/fstab: ext2 data=writeback,nobh

if that still fails we can rule out buffer_head funnies.

#158 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 12 lines · Andrew Morton
Quoted 11 lines

I now _suspect_ that we're talking about something like

- we started a writeout. The IO is still pending, and the page was marked clean and is now in the "writeback" phase.
- a write happens to the page, and the page gets marked dirty again. Marking the page dirty also marks all the _buffers_ in the page dirty, but they were actually already dirty, because the IO hasn't completed yet.
- the IO from the _previous_ write completes, and marks the buffers clean again.

Quoted 3 lines

Some things for the testers to try, please:

- mount the fs with ext2 with the no-buffer-head option. That means either:

[ snip snip ]

This is definitely worth testing, but the exact schenario I outlined is probably not the thing that happens. It was really meant to be more of an exmple of the _kind_ of situation I think we might have.

That would explain why we didn't see this before: we simply didn't mark pages clean all that aggressively, and an app like rtorrent would normally have caused its flushes to happen _synchronously_ by using msync() (even if the IO itself was done asynchronously, all the dirty bit stuff would be synchronous wrt any rtorrent behaviour).

And the things that /did/ use to clean pages asynchronously (VM scanning) would always actually look at the "young" bit (aka "accessed") and not even touch the dirty bit if an application had accessed the page recently, so that basically avoided any likely races, because we'd touch the dirty bit ONLY if the page was "cold".

So this is why I'm saying that it might be an old bug, and it would be just the new pattern of handling dirty bits that triggers it.

But avoiding buffer heads and testing that part is worth doing. Just to remove one thing from the equation.

Linus

#159 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Andrew Morton
Quoted 14 lines · Andrew Morton
Quoted 10 lines · On Sun, 24 Dec 2006 00:43:54 -0800 (PST) Linus Torvalds <[email protected]>…

I now _suspect_ that we're talking about something like

- we started a writeout. The IO is still pending, and the page was marked clean and is now in the "writeback" phase.
- a write happens to the page, and the page gets marked dirty again. Marking the page dirty also marks all the _buffers_ in the page dirty, but they were actually already dirty, because the IO hasn't completed yet.
- the IO from the _previous_ write completes, and marks the buffers clean again.

Quoted 6 lines

Some things for the testers to try, please:

- mount the fs with ext2 with the no-buffer-head option. That means either:

grub.conf: rootfstype=ext2 rootflags=nobh
/etc/fstab: ext2 nobh

ierdnac ~ # mount
/dev/sda7 on / type ext2 (rw,noatime,nobh)

I have corruption.

Quoted 5 lines

- mount the fs with ext3 data=writeback, nobh

grub.conf: rootfstype=ext3 rootflags=nobh,data=writeback (I hope this works) /etc/fstab: ext2 data=writeback,nobh

ierdnac ~ # mount
/dev/sda7 on / type ext3 (rw,noatime,nobh)

ierdnac ~ # dmesg|grep EXT3
EXT3-fs: mounted filesystem with writeback data mode. EXT3 FS on sda7, internal journal

I don't have corruption. I tested twice.

Quoted 3 lines

if that still fails we can rule out buffer_head funnies.

#160 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 33 lines · Andrei Popa
Quoted 14 lines · Andrew Morton
Quoted 10 lines · On Sun, 24 Dec 2006 00:43:54 -0800 (PST) Linus Torvalds <[email protected]>…

I now _suspect_ that we're talking about something like

- we started a writeout. The IO is still pending, and the page was marked clean and is now in the "writeback" phase.
- a write happens to the page, and the page gets marked dirty again. Marking the page dirty also marks all the _buffers_ in the page dirty, but they were actually already dirty, because the IO hasn't completed yet.
- the IO from the _previous_ write completes, and marks the buffers clean again.

Quoted 6 lines

Some things for the testers to try, please:

- mount the fs with ext2 with the no-buffer-head option. That means either:

grub.conf: rootfstype=ext2 rootflags=nobh
/etc/fstab: ext2 nobh

ierdnac ~ # mount
/dev/sda7 on / type ext2 (rw,noatime,nobh)

I have corruption.

Quoted 5 lines

- mount the fs with ext3 data=writeback, nobh

grub.conf: rootfstype=ext3 rootflags=nobh,data=writeback (I hope this works) /etc/fstab: ext2 data=writeback,nobh

Quoted 9 lines

ierdnac ~ # mount
/dev/sda7 on / type ext3 (rw,noatime,nobh)

ierdnac ~ # dmesg|grep EXT3
EXT3-fs: mounted filesystem with writeback data mode. EXT3 FS on sda7, internal journal

I don't have corruption. I tested twice.

I also tested with ext3 ordered, nobh and I have file corruption...

Quoted 3 lines
Quoted 3 lines

if that still fails we can rule out buffer_head funnies.

#161 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 1 line · On Sun, 24 Dec 2006 14:26:01 +0200 Andrei Popa <[email protected]> wrote:

I also tested with ext3 ordered, nobh and I have file corruption...

ordered+nobh isn't a possible combination. The filesystem probably ignored nobh. nobh mode only makes sense with data=writeback.

#162 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Andrei Popa
Quoted 16 lines · On Sun, 24 Dec 2006 14:14:38 +0200 Andrei Popa <[email protected]> wrote:
Quoted 4 lines

- mount the fs with ext2 with the no-buffer-head option. That means either:

grub.conf: rootfstype=ext2 rootflags=nobh
/etc/fstab: ext2 nobh

ierdnac ~ # mount
/dev/sda7 on / type ext2 (rw,noatime,nobh)

I have corruption.

Quoted 5 lines

- mount the fs with ext3 data=writeback, nobh

grub.conf: rootfstype=ext3 rootflags=nobh,data=writeback (I hope this works) /etc/fstab: ext2 data=writeback,nobh

Quoted 8 lines

ierdnac ~ # mount
/dev/sda7 on / type ext3 (rw,noatime,nobh)

ierdnac ~ # dmesg|grep EXT3
EXT3-fs: mounted filesystem with writeback data mode. EXT3 FS on sda7, internal journal

I don't have corruption. I tested twice.

This is a surprising result. Can you pleas retest ext3 data=writeback,nobh?

#163 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 22 lines · Andrew Morton
Quoted 19 lines · On Sun, 24 Dec 2006 14:14:38 +0200 Andrei Popa <[email protected]> wrote:
Quoted 4 lines

- mount the fs with ext2 with the no-buffer-head option. That means either:

grub.conf: rootfstype=ext2 rootflags=nobh
/etc/fstab: ext2 nobh

ierdnac ~ # mount
/dev/sda7 on / type ext2 (rw,noatime,nobh)

I have corruption.

Quoted 5 lines

- mount the fs with ext3 data=writeback, nobh

grub.conf: rootfstype=ext3 rootflags=nobh,data=writeback (I hope this works) /etc/fstab: ext2 data=writeback,nobh

ierdnac ~ # mount
/dev/sda7 on / type ext3 (rw,noatime,nobh)

Quoted 7 lines
Quoted 5 lines

ierdnac ~ # dmesg|grep EXT3
EXT3-fs: mounted filesystem with writeback data mode. EXT3 FS on sda7, internal journal

I don't have corruption. I tested twice.

This is a surprising result. Can you pleas retest ext3 data=writeback,nobh?

Yes, no corruption. Also tested only with data=writeback and had no corruption.

#164 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 10 lines · Andrei Popa
Quoted 6 lines · Andrew Morton
Quoted 3 lines · Andrei Popa

/dev/sda7 on / type ext3 (rw,noatime,nobh)

I don't have corruption. I tested twice.

This is a surprising result. Can you pleas retest ext3 data=writeback,nobh?

Yes, no corruption. Also tested only with data=writeback and had no corruption.

Ok, so it would seem to be writeback related _somehow_. However, most of the differences (I _thought_) in ext3 actually show up only if you have *both* "nobh" and "data=writeback", and as far as I can tell, just a simple "data=writeback" should still use the bog-standard "block_write_full_page()".

Andrew?

Although as far as I can see, then ext2 should work as-is too (since it too also just uses "block_write_full_page()" without anything fancy).

Strange.

How about this particularly stupid diff? (please test with something that _would_ cause corruption normally).

It is _entirely_ untested, but what it tries to do is to simply serialize any writeback in progress with any process that tries to re-map a shared page into its address space and dirty it. I haven't tested it, and maybe it misses some case, but it looks likea good way to try to avoid races with marking pages dirty and the writeback phase ..

Linus
---

mm/memory.c +2 -0
index 563792f..64ed10b 100644
@@ -1544,6 +1544,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
 			if (!pte_same(*page_table, orig_pte))
 				goto unlock;
 		}
+		wait_on_page_writeback(old_page);
 		dirty_page = old_page;
 		get_page(dirty_page);
 		reuse = 1;
@@ -2215,6 +2216,7 @@ retry:
 				page_cache_release(new_page);
 				return VM_FAULT_SIGBUS;
 			}
+			wait_on_page_writeback(new_page);
 		}
 	}
 
#165 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 21 lines · On Sun, 24 Dec 2006 09:16:06 -0800 (PST) Linus Torvalds <[email protected]>…
Quoted 10 lines · Andrei Popa
Quoted 6 lines · Andrew Morton
Quoted 3 lines · Andrei Popa

/dev/sda7 on / type ext3 (rw,noatime,nobh)

I don't have corruption. I tested twice.

This is a surprising result. Can you pleas retest ext3 data=writeback,nobh?

Yes, no corruption. Also tested only with data=writeback and had no corruption.

Ok, so it would seem to be writeback related _somehow_. However, most of the differences (I _thought_) in ext3 actually show up only if you have *both* "nobh" and "data=writeback", and as far as I can tell, just a simple "data=writeback" should still use the bog-standard "block_write_full_page()".

Quoted 4 lines

Andrew?

Although as far as I can see, then ext2 should work as-is too (since it too also just uses "block_write_full_page()" without anything fancy).

ext2 uses the multipage-bio assembly code for writeback whereas ext3 doesn't. But ext3 doesn't use that code in data=ordered mode, of course.

Still, this:

fs/ext2/inode.c renamed from fs/ext2/inode.c~a +2 -2
@@ -693,7 +693,7 @@ const struct address_space_operations ex
 	.commit_write		= generic_commit_write,
 	.bmap			= ext2_bmap,
 	.direct_IO		= ext2_direct_IO,
-	.writepages		= ext2_writepages,
+//	.writepages		= ext2_writepages,
 	.migratepage		= buffer_migrate_page,
 };
 
@@ -711,7 +711,7 @@ const struct address_space_operations ex
 	.commit_write		= nobh_commit_write,
 	.bmap			= ext2_bmap,
 	.direct_IO		= ext2_direct_IO,
-	.writepages		= ext2_writepages,
+//	.writepages		= ext2_writepages,
 	.migratepage		= buffer_migrate_page,
 };
 

_

will switch it off for ext2.

Quoted 26 lines

Strange.

How about this particularly stupid diff? (please test with something that _would_ cause corruption normally).

It is _entirely_ untested, but what it tries to do is to simply serialize any writeback in progress with any process that tries to re-map a shared page into its address space and dirty it. I haven't tested it, and maybe it misses some case, but it looks likea good way to try to avoid races with marking pages dirty and the writeback phase ..

Linus
---

mm/memory.c +1 -0
index 563792f..64ed10b 100644
@@ -1544,6 +1544,7 @@ static int do_wp_page(struct mm_struct *mm, struct vm_area_struct *vma,
 			if (!pte_same(*page_table, orig_pte))
 				goto unlock;
 		}
+		wait_on_page_writeback(old_page);
 		dirty_page = old_page;
 		get_page(dirty_page);
 		reuse = 1;

mm/memory.c retry: · line 2221

Quoted 6 lines
mm/memory.c +1 -0
@@ -2215,6 +2216,7 @@ retry:
 				page_cache_release(new_page);
 				return VM_FAULT_SIGBUS;
 			}
+			wait_on_page_writeback(new_page);
 		}
 	}

yup. Also, we could perhaps lock the target page during pagefaults..

#166 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 3 lines · Linus Torvalds

How about this particularly stupid diff? (please test with something that _would_ cause corruption normally).

Actually, here's an even more stupid diff, which actually to some degree seems to capture the real problem better.

Peter, tell me I'm crazy, but with the new rules, the following condition is a bug:

- shared mapping
- writable
- not already marked dirty in the PTE

because that combination means that the hardware can mark the PTE dirty without us even realizing (and thus not marking the "struct page *" dirty).

(The above is actually a valid situation for IO mappings, but not for "real" mappings. And IO mappings should never take page faults, I think).

So, with that in mind, I wrote this stupid patch (for 32-bit x86, since I used my Mac Mini for testing ratehr than my main machine - but the x86-64 version should be pretty much identcal)..

And you know what, Peter? It triggers for me. I get

	WARNING at mm/memory.c:2274 do_no_page()
	 [<c0103d4a>] show_trace_log_lvl+0x1a/0x2f
	 [<c010436c>] show_trace+0x12/0x14
	 [<c01043f0>] dump_stack+0x16/0x18
	 [<c0159790>] __handle_mm_fault+0x38d/0x919
	 [<c011c8c4>] do_page_fault+0x1ff/0x507
	 [<c03fabcc>] error_code+0x7c/0x84

which seems to say that do_no_page() can be used to insert shared and non-dirty, but still writable, pages.

But maybe my patch is just bogus, and I didn't think it through.

Peter, I realize it's Christmas Eve, but let's face it, Santa appreciates good boys and girls, and we all want tons of loot. So please be good, and waste some time looking at this and tell me why I'm either wrong, or there's a real smoking gun here.. ;)

Linus

---

include/asm-i386/pgtable.h +7 -1
index e6a4723..1389bb7 100644
@@ -494,7 +494,13 @@ do {									\
  * The i386 doesn't have any external MMU info: the kernel page
  * tables contain all the necessary information.
  */
-#define update_mmu_cache(vma,address,pte) do { } while (0)
+#define bad_shared_pte(pte) (pte_write(pte) && !pte_dirty(pte))
+#define update_mmu_cache(vma,address,pte) do {		\
+	static int __cnt;				\
+	WARN_ON(((vma)->vm_flags & VM_SHARED)		\
+		 && bad_shared_pte(pte)			\
+		 && ++__cnt < 5);			\
+} while (0)
 #endif /* !__ASSEMBLY__ */
 
 #ifdef CONFIG_FLATMEM
#167 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 7 lines · Linus Torvalds

Peter, tell me I'm crazy, but with the new rules, the following condition is a bug:

- shared mapping
- writable
- not already marked dirty in the PTE

Ok, so how about this diff.

I'm actually feeling good about this one. It really looks like "do_no_page()" was simply buggy, and that this explains everything.

Please please please test. Throw all the other patches away (with the possible exception of the "update_mmu_cache()" sanity checker, which is still interesting in case some _other_ place does this too).

Don't do the "wait_on_page_writeback()" thing, because it changes timings and might hide thngs for the wrong reasons. Just apply this on top of a known failing kernel, and test.

Linus

---

mm/memory.c +5 -3
index 563792f..cf429c4 100644
@@ -2247,21 +2249,23 @@ retry:
 	if (pte_none(*page_table)) {
 		flush_icache_page(vma, new_page);
 		entry = mk_pte(new_page, vma->vm_page_prot);
-		if (write_access)
-			entry = maybe_mkwrite(pte_mkdirty(entry), vma);
-		set_pte_at(mm, address, page_table, entry);
 		if (anon) {
 			inc_mm_counter(mm, anon_rss);
 			lru_cache_add_active(new_page);
 			page_add_new_anon_rmap(new_page, vma, address);
+			if (write_access)
+				entry = maybe_mkwrite(pte_mkdirty(entry), vma);
 		} else {
 			inc_mm_counter(mm, file_rss);
 			page_add_file_rmap(new_page);
+			entry = pte_wrprotect(entry);
 			if (write_access) {
 				dirty_page = new_page;
 				get_page(dirty_page);
+				entry = maybe_mkwrite(pte_mkdirty(entry), vma);
 			}
 		}
+		set_pte_at(mm, address, page_table, entry);
 	} else {
 		/* One of our sibling threads was faster, back out. */
 		page_cache_release(new_page);
#168 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 4 lines · Linus Torvalds

Ok, so how about this diff.

I'm actually feeling good about this one. It really looks like "do_no_page()" was simply buggy, and that this explains everything.

I tested with just this patch and 2.6.19 and no change. Sorry Linus, no early Christmas present :-(

Gordon

Signature · 1 line
Gordon Farquharson
#169 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 5 lines · Linus Torvalds
Quoted 2 lines · Linus Torvalds

Peter, tell me I'm crazy, but with the new rules, the following condition is a bug:

Quoted 8 lines
Quoted 3 lines

- shared mapping
- writable
- not already marked dirty in the PTE

Ok, so how about this diff.

I'm actually feeling good about this one. It really looks like "do_no_page()" was simply buggy, and that this explains everything.

Still trying to catch up here, so I'm not going to reply to any old stuff and just start at the tip of the thread... Other than to say that I really like cancel_page_dirty ;)

I think your patch is quite right so that's a good catch. But I'm not too surprised that it does not help the problem, because I don't think we have started shedding any old pte_dirty tests at unmap/reclaim-time, have we? So the dirty bit isn't going to get lost, as such.

I was hoping that you've almost narrowed it down to the filesystem writeback code, with the last few mails?

Nick

Quoted 39 lines

Please please please test. Throw all the other patches away (with the possible exception of the "update_mmu_cache()" sanity checker, which is still interesting in case some _other_ place does this too).

Don't do the "wait_on_page_writeback()" thing, because it changes timings and might hide thngs for the wrong reasons. Just apply this on top of a known failing kernel, and test.

Linus

---

mm/memory.c +5 -3
index 563792f..cf429c4 100644
@@ -2247,21 +2249,23 @@ retry:
 	if (pte_none(*page_table)) {
 		flush_icache_page(vma, new_page);
 		entry = mk_pte(new_page, vma->vm_page_prot);
-		if (write_access)
-			entry = maybe_mkwrite(pte_mkdirty(entry), vma);
-		set_pte_at(mm, address, page_table, entry);
 		if (anon) {
 			inc_mm_counter(mm, anon_rss);
 			lru_cache_add_active(new_page);
 			page_add_new_anon_rmap(new_page, vma, address);
+			if (write_access)
+				entry = maybe_mkwrite(pte_mkdirty(entry), vma);
 		} else {
 			inc_mm_counter(mm, file_rss);
 			page_add_file_rmap(new_page);
+			entry = pte_wrprotect(entry);
 			if (write_access) {
 				dirty_page = new_page;
 				get_page(dirty_page);
+				entry = maybe_mkwrite(pte_mkdirty(entry), vma);
 			}
 		}
+		set_pte_at(mm, address, page_table, entry);
 	} else {
 		/* One of our sibling threads was faster, back out. */
 		page_cache_release(new_page);
Signature · 3 lines
SUSE Labs, Novell Inc.

Send instant messages to your online friends http://au.messenger.yahoo.com 
#170 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

Quoted 10 lines · Nick Piggin
Quoted 5 lines · Linus Torvalds

Ok, so how about this diff.

I'm actually feeling good about this one. It really looks like "do_no_page()" was simply buggy, and that this explains everything.

Still trying to catch up here, so I'm not going to reply to any old stuff and just start at the tip of the thread... Other than to say that I really like cancel_page_dirty ;)

Yeah, I think that part is a bit clearer about what's going on now.

Quoted 1 line

I think your patch is quite right so that's a good catch.

Actually, since people told me it didn't matter, I went back and looked at _why_ - the thing is, "vma->vm_page_prot" should always be read-only anyway, except for mappings that don't do dirty accounting at all, so I think my patch only found cases that are unimportant (ie pages that get faulted on on filesystems like ramfs that doesn't do any dirty page accounting because they're all dirty anyway).

Quoted 4 lines

But I'm not too surprised that it does not help the problem, because I don't think we have started shedding any old pte_dirty tests at unmap/reclaim-time, have we? So the dirty bit isn't going to get lost, as such.

True. We should no longer _need_ those dirty bit reclaims at unmap/reclaim, but we still do them, so you're right, even if we were buggy in this area, it should only really matter for the dirty page counting, not for any lost data.

Quoted 2 lines

I was hoping that you've almost narrowed it down to the filesystem writeback code, with the last few mails?

I think so, yes.

However, I've checked, and "rtorrent" really does seem to be fairly well-behaved wrt any filesystem activity. It does

- no threading. It's 100% single-threaded, and doesn't even appear to use signals.

- exactly _one_ "ftruncate()", and it does it at the beginning, for the full final size.

IOW, it's not anything subtle with truncate and dirty page cancel.

- It never uses mprotect on the shared mappings, but it _does_ do:
"mincore()" - but the return values don't much matter (it's used
as a heuristic on which parts to hash, apparently)

I double- and triple-checked this one, because I did make changes to "mincore()", but those didn't go into the affected kernels anyway (ie they are not in plain 2.6.19, nor in 2.6.18.3 either)

"madvise(MADV_WILLNEED)"
"msync(MS_ASYNC)" (or MS_SYNC if you use a command line flag) "munmap()" of course

- it never seems to mix mmap() and write() - it does _only_ mmap.

- it seems to mmap/munmap the shared files in nice 64-page chunks, all 64-page aligned in the file (ie it does NOT create one big mapping, it has some kind of LRU of thse 64-page chunks). The only exception being the last chunk, which it maps byte-accurate to the size.

- I haven't checked whether it only ever has the same chunk mapped once at a time.

Anyway, the _one_ half-way interesting thing is the fact that it doesn't allocate any backing store at all for the file, and as such the page writeback needs to create all the underlying buffers on the filesystem. I really don't see why that would be a problem either, but I could imagine that if we have some writeback bug where we can end up writing back the _same_ page concurrently, we'd actually end up racing in the kernel, and allocating two different backing stores, and then maybe the other one would effectively "get lost" (and the earlier writeback would win the race, explaining why we'd end up with zeroes at the end of a block).

Or something.

However, all the codepaths _seem_ to test for PG_writeback, and not even try to start another writeback while the first one is still active.

What would also actually be interesting is whether somebody can reproduce this on Reiserfs, for example. I _think_ all the reports I've seen are on ext2 or ext3, and if this is somehow writeback-related, it could be some bug that is just shared between the two by virtue of them still having a lot of stuff in common.

Linus

#171 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

On 12/27/06, Linus Torvalds <[email protected]> wrote:
<snip>

Quoted 8 lines

- It never uses mprotect on the shared mappings, but it _does_ do:
"mincore()" - but the return values don't much matter (it's used
as a heuristic on which parts to hash, apparently)

I double- and triple-checked this one, because I did make changes to "mincore()", but those didn't go into the affected kernels anyway (ie they are not in plain 2.6.19, nor in 2.6.18.3 either)

Correct, mincore is only used to check if it should delay the hash checking.

Quoted 10 lines

"madvise(MADV_WILLNEED)"
"msync(MS_ASYNC)" (or MS_SYNC if you use a command line flag) "munmap()" of course

- it never seems to mix mmap() and write() - it does _only_ mmap.

- it seems to mmap/munmap the shared files in nice 64-page chunks, all 64-page aligned in the file (ie it does NOT create one big mapping, it has some kind of LRU of thse 64-page chunks). The only exception being the last chunk, which it maps byte-accurate to the size.

The length of the chunks is only page aligned on single file torrents, not so on multi-file torrents. I've attached a patch for rtorrent that will extend the length to the page boundary.

Quoted 2 lines

- I haven't checked whether it only ever has the same chunk mapped once at a time.

This should be the case, but two mapped chunks may share a page, sometimes with different r/w permissions.

Jari Sundell

src/data/socket_file.cc +12 -5
Index: libtorrent/src/data/socket_file.cc
===================================================================
@@ -162,20 +162,27 @@
 MemoryChunk
 SocketFile::create_chunk(uint64_t offset, uint32_t length, int prot, int flags) const {
   if (!is_open())
-    throw internal_error("SocketFile::get_chunk() called on a closed file");
+    throw internal_error("SocketFile::get_chunk() called on a closed file.");
 
   if (((prot & MemoryChunk::prot_read) && !is_readable()) ||
       ((prot & MemoryChunk::prot_write) && !is_writable()))
-    throw storage_error("SocketFile::get_chunk() permission denied");
+    throw storage_error("SocketFile::get_chunk() permission denied.");
 
+  uint64_t fileSize = size();
+
   // For some reason mapping beyond the extent of the file does not
   // cause mmap to complain, so we need to check manually here.
-  if (offset < 0 || length == 0 || offset > size() || offset + length > size())
+  if (offset < 0 || length == 0 || offset > fileSize || offset + length > fileSize)
     return MemoryChunk();
 
-  uint64_t align = offset % MemoryChunk::page_size();
+  uint64_t align     = offset % MemoryChunk::page_size();
+  uint64_t mapLength = std::min(((length + align + MemoryChunk::page_size() - 1) / MemoryChunk::page_size()) * MemoryChunk::page_size(),
+                                fileSize - (offset - align));
 
-  char* ptr = (char*)mmap(NULL, length + align, prot, flags, m_fd, offset - align);
+  if (offset - align + mapLength != fileSize && (offset - align + mapLength) % MemoryChunk::page_size() != 0)
+    throw internal_error("SocketFile::create_chunk(...) Length not page aligned.");
+
+  char* ptr = (char*)mmap(NULL, mapLength, prot, flags, m_fd, offset - align);
   
   if (ptr == MAP_FAILED)
     return MemoryChunk();
#172 lore raw

Re: [PATCH] mm: fix page_mkclean_one (was: 2.6.19 file content corruption on ext3)

In reply to Linus Torvalds
Quoted 7 lines · Linus Torvalds

What would also actually be interesting is whether somebody can reproduce this on Reiserfs, for example. I _think_ all the reports I've seen are on ext2 or ext3, and if this is somehow writeback-related, it could be some bug that is just shared between the two by virtue of them still having a lot of stuff in common.

Linus

I do get this error on reiserfs ( old one, didn't try on reiser4 ). Stock 2.6.19 plus reiser4 patch. Previously reported by me only in the debian bts.

flo attenberger

---
Linux master 2.6.19 #1 PREEMPT Thu Dec 21 10:55:34 CET 2006 x86_64 GNU/Linux

#
# Automatically generated make config: don't edit # Linux kernel version: 2.6.19
# Thu Dec 21 10:45:05 2006
#
CONFIG_X86_64=y
CONFIG_64BIT=y
CONFIG_X86=y
CONFIG_ZONE_DMA32=y
CONFIG_LOCKDEP_SUPPORT=y CONFIG_STACKTRACE_SUPPORT=y CONFIG_SEMAPHORE_SLEEPERS=y
CONFIG_MMU=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y CONFIG_GENERIC_HWEIGHT=y CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_X86_CMPXCHG=y
CONFIG_EARLY_PRINTK=y
CONFIG_GENERIC_ISA_DMA=y
CONFIG_GENERIC_IOMAP=y CONFIG_ARCH_MAY_HAVE_PC_FDC=y CONFIG_ARCH_POPULATES_NODE_MAP=y
CONFIG_DMI=y
CONFIG_AUDIT_ARCH=y CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"

#
# Code maturity level options
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_LOCK_KERNEL=y
CONFIG_INIT_ENV_ARG_LIMIT=32

#
# General setup
#
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
# CONFIG_IPC_NS is not set
CONFIG_POSIX_MQUEUE=y
CONFIG_BSD_PROCESS_ACCT=y
# CONFIG_BSD_PROCESS_ACCT_V3 is not set
# CONFIG_TASKSTATS is not set
# CONFIG_UTS_NS is not set
# CONFIG_AUDIT is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
# CONFIG_RELAY is not set
CONFIG_INITRAMFS_SOURCE=""
# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
CONFIG_SYSCTL=y
# CONFIG_EMBEDDED is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
# CONFIG_KALLSYMS_EXTRA_PASS is not set
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SHMEM=y
CONFIG_SLAB=y
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_RT_MUTEXES=y
# CONFIG_TINY_SHMEM is not set
CONFIG_BASE_SMALL=0
# CONFIG_SLOB is not set

#
# Loadable module support
#
CONFIG_MODULES=y
CONFIG_MODULE_UNLOAD=y
CONFIG_MODULE_FORCE_UNLOAD=y
CONFIG_MODVERSIONS=y
# CONFIG_MODULE_SRCVERSION_ALL is not set
CONFIG_KMOD=y

#
# Block layer
#
CONFIG_BLOCK=y
# CONFIG_LBD is not set
# CONFIG_BLK_DEV_IO_TRACE is not set
# CONFIG_LSF is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
CONFIG_IOSCHED_AS=m
CONFIG_IOSCHED_DEADLINE=m
CONFIG_IOSCHED_CFQ=y
# CONFIG_DEFAULT_AS is not set
# CONFIG_DEFAULT_DEADLINE is not set
CONFIG_DEFAULT_CFQ=y
# CONFIG_DEFAULT_NOOP is not set
CONFIG_DEFAULT_IOSCHED="cfq"

#
# Processor type and features
#
CONFIG_X86_PC=y
# CONFIG_X86_VSMP is not set
CONFIG_MK8=y
# CONFIG_MPSC is not set
# CONFIG_GENERIC_CPU is not set
CONFIG_X86_L1_CACHE_BYTES=64
CONFIG_X86_L1_CACHE_SHIFT=6
CONFIG_X86_INTERNODE_CACHE_BYTES=64
CONFIG_X86_TSC=y
CONFIG_X86_GOOD_APIC=y
CONFIG_MICROCODE=m
CONFIG_MICROCODE_OLD_INTERFACE=y
CONFIG_X86_MSR=m
CONFIG_X86_CPUID=m
CONFIG_X86_IO_APIC=y
CONFIG_X86_LOCAL_APIC=y
CONFIG_MTRR=y
# CONFIG_SMP is not set
# CONFIG_PREEMPT_NONE is not set
# CONFIG_PREEMPT_VOLUNTARY is not set
CONFIG_PREEMPT=y
CONFIG_PREEMPT_BKL=y
CONFIG_ARCH_SPARSEMEM_ENABLE=y
CONFIG_ARCH_FLATMEM_ENABLE=y
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
# CONFIG_DISCONTIGMEM_MANUAL is not set
# CONFIG_SPARSEMEM_MANUAL is not set
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
# CONFIG_SPARSEMEM_STATIC is not set
CONFIG_SPLIT_PTLOCK_CPUS=4
CONFIG_RESOURCES_64BIT=y
CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
CONFIG_HPET_TIMER=y
CONFIG_IOMMU=y
# CONFIG_CALGARY_IOMMU is not set
CONFIG_SWIOTLB=y
CONFIG_X86_MCE=y
# CONFIG_X86_MCE_INTEL is not set
CONFIG_X86_MCE_AMD=y
CONFIG_KEXEC=y
# CONFIG_CRASH_DUMP is not set
CONFIG_PHYSICAL_START=0x200000
CONFIG_SECCOMP=y
# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_HZ_100 is not set
# CONFIG_HZ_250 is not set
CONFIG_HZ_1000=y
CONFIG_HZ=1000
CONFIG_REORDER=y
CONFIG_K8_NB=y
CONFIG_GENERIC_HARDIRQS=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_ISA_DMA_API=y

#
# Power management options
#
CONFIG_PM=y
CONFIG_PM_LEGACY=y
# CONFIG_PM_DEBUG is not set
CONFIG_PM_SYSFS_DEPRECATED=y
# CONFIG_SOFTWARE_SUSPEND is not set

#
# ACPI (Advanced Configuration and Power Interface) Support #
CONFIG_ACPI=y
CONFIG_ACPI_SLEEP=y
CONFIG_ACPI_SLEEP_PROC_FS=y
# CONFIG_ACPI_SLEEP_PROC_SLEEP is not set CONFIG_ACPI_AC=m
# CONFIG_ACPI_BATTERY is not set CONFIG_ACPI_BUTTON=m
CONFIG_ACPI_VIDEO=m
CONFIG_ACPI_HOTKEY=m
CONFIG_ACPI_FAN=m
# CONFIG_ACPI_DOCK is not set CONFIG_ACPI_PROCESSOR=m
CONFIG_ACPI_THERMAL=m
# CONFIG_ACPI_ASUS is not set
# CONFIG_ACPI_IBM is not set
# CONFIG_ACPI_TOSHIBA is not set CONFIG_ACPI_BLACKLIST_YEAR=0
# CONFIG_ACPI_DEBUG is not set
CONFIG_ACPI_EC=y
CONFIG_ACPI_POWER=y
CONFIG_ACPI_SYSTEM=y
CONFIG_X86_PM_TIMER=y
# CONFIG_ACPI_CONTAINER is not set
# CONFIG_ACPI_SBS is not set

#
# CPU Frequency scaling
#
CONFIG_CPU_FREQ=y
CONFIG_CPU_FREQ_TABLE=m
# CONFIG_CPU_FREQ_DEBUG is not set CONFIG_CPU_FREQ_STAT=m CONFIG_CPU_FREQ_STAT_DETAILS=y
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_PERFORMANCE=m CONFIG_CPU_FREQ_GOV_POWERSAVE=m CONFIG_CPU_FREQ_GOV_USERSPACE=y CONFIG_CPU_FREQ_GOV_ONDEMAND=m CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m

#
# CPUFreq processor drivers
#
CONFIG_X86_POWERNOW_K8=m
CONFIG_X86_POWERNOW_K8_ACPI=y
# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
CONFIG_X86_ACPI_CPUFREQ=m

#

# shared options
#
# CONFIG_X86_ACPI_CPUFREQ_PROC_INTF is not set
# CONFIG_X86_SPEEDSTEP_LIB is not set

#
# Bus options (PCI etc.)
#
CONFIG_PCI=y
CONFIG_PCI_DIRECT=y
CONFIG_PCI_MMCONFIG=y
# CONFIG_PCIEPORTBUS is not set
# CONFIG_PCI_MSI is not set
# CONFIG_PCI_DEBUG is not set
# CONFIG_HT_IRQ is not set

#
# PCCARD (PCMCIA/CardBus) support
#
# CONFIG_PCCARD is not set

#
# PCI Hotplug Support
#
CONFIG_HOTPLUG_PCI=m
CONFIG_HOTPLUG_PCI_FAKE=m
# CONFIG_HOTPLUG_PCI_ACPI is not set
# CONFIG_HOTPLUG_PCI_CPCI is not set
# CONFIG_HOTPLUG_PCI_SHPC is not set

#
# Executable file formats / Emulations
#
CONFIG_BINFMT_ELF=y
CONFIG_BINFMT_MISC=m
CONFIG_IA32_EMULATION=y
# CONFIG_IA32_AOUT is not set
CONFIG_COMPAT=y
CONFIG_SYSVIPC_COMPAT=y

#
# Networking
#
CONFIG_NET=y

#
# Networking options
#
# CONFIG_NETDEBUG is not set
CONFIG_PACKET=m
CONFIG_PACKET_MMAP=y
CONFIG_UNIX=y
CONFIG_XFRM=y
CONFIG_XFRM_USER=m
# CONFIG_XFRM_SUB_POLICY is not set
CONFIG_NET_KEY=m
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
CONFIG_IP_ADVANCED_ROUTER=y
CONFIG_ASK_IP_FIB_HASH=y
# CONFIG_IP_FIB_TRIE is not set
CONFIG_IP_FIB_HASH=y
CONFIG_IP_MULTIPLE_TABLES=y
CONFIG_IP_ROUTE_FWMARK=y
CONFIG_IP_ROUTE_MULTIPATH=y
# CONFIG_IP_ROUTE_MULTIPATH_CACHED is not set
CONFIG_IP_ROUTE_VERBOSE=y
# CONFIG_IP_PNP is not set
CONFIG_NET_IPIP=m
CONFIG_NET_IPGRE=m
# CONFIG_NET_IPGRE_BROADCAST is not set
CONFIG_IP_MROUTE=y
CONFIG_IP_PIMSM_V1=y
CONFIG_IP_PIMSM_V2=y
CONFIG_ARPD=y
CONFIG_SYN_COOKIES=y
CONFIG_INET_AH=m
CONFIG_INET_ESP=m
CONFIG_INET_IPCOMP=m
CONFIG_INET_XFRM_TUNNEL=m
CONFIG_INET_TUNNEL=m
# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
# CONFIG_INET_XFRM_MODE_TUNNEL is not set
# CONFIG_INET_XFRM_MODE_BEET is not set
CONFIG_INET_DIAG=m
CONFIG_INET_TCP_DIAG=m
CONFIG_TCP_CONG_ADVANCED=y
CONFIG_TCP_CONG_BIC=y
CONFIG_TCP_CONG_CUBIC=m
CONFIG_TCP_CONG_WESTWOOD=m
CONFIG_TCP_CONG_HTCP=m
# CONFIG_TCP_CONG_HSTCP is not set
# CONFIG_TCP_CONG_HYBLA is not set
# CONFIG_TCP_CONG_VEGAS is not set
# CONFIG_TCP_CONG_SCALABLE is not set
# CONFIG_TCP_CONG_LP is not set
# CONFIG_TCP_CONG_VENO is not set
CONFIG_DEFAULT_BIC=y
# CONFIG_DEFAULT_CUBIC is not set
# CONFIG_DEFAULT_HTCP is not set
# CONFIG_DEFAULT_VEGAS is not set
# CONFIG_DEFAULT_WESTWOOD is not set
# CONFIG_DEFAULT_RENO is not set
CONFIG_DEFAULT_TCP_CONG="bic"

#
# IP: Virtual Server Configuration
#
# CONFIG_IP_VS is not set
CONFIG_IPV6=m
CONFIG_IPV6_PRIVACY=y
# CONFIG_IPV6_ROUTER_PREF is not set CONFIG_INET6_AH=m
CONFIG_INET6_ESP=m
CONFIG_INET6_IPCOMP=m
# CONFIG_IPV6_MIP6 is not set CONFIG_INET6_XFRM_TUNNEL=m CONFIG_INET6_TUNNEL=m
# CONFIG_INET6_XFRM_MODE_TRANSPORT is not set # CONFIG_INET6_XFRM_MODE_TUNNEL is not set
# CONFIG_INET6_XFRM_MODE_BEET is not set
# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set CONFIG_IPV6_SIT=m
# CONFIG_IPV6_TUNNEL is not set
# CONFIG_IPV6_MULTIPLE_TABLES is not set
# CONFIG_NETLABEL is not set
# CONFIG_NETWORK_SECMARK is not set CONFIG_NETFILTER=y
# CONFIG_NETFILTER_DEBUG is not set

#
# Core Netfilter Configuration
#
CONFIG_NETFILTER_NETLINK=m CONFIG_NETFILTER_NETLINK_QUEUE=m CONFIG_NETFILTER_NETLINK_LOG=m CONFIG_NETFILTER_XTABLES=m CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
# CONFIG_NETFILTER_XT_TARGET_DSCP is not SCSI low-level drivers #
# CONFIG_ISCSI_TCP is not set
# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
# CONFIG_SCSI_3W_9XXX is not set
# CONFIG_SCSI_ACARD is not set
# CONFIG_SCSI_AACRAID is not set
# CONFIG_SCSI_AIC7XXX is not set
# CONFIG_SCSI_AIC7XXX_OLD is not set
# CONFIG_SCSI_AIC79XX is not set
# CONFIG_SCSI_AIC94XX is not set
# CONFIG_SCSI_ARCMSR is not set
# CONFIG_MEGARAID_NEWGEN is not set
# CONFIG_MEGARAID_LEGACY is not set
# CONFIG_MEGARAID_SAS is not set
# CONFIG_SCSI_HPTIOP is not set
# CONFIG_SCSI_BUSLOGIC is not set
# CONFIG_SCSI_DMX3191D is not set
# CONFIG_SCSI_EATA is not set
# CONFIG_SCSI_FUTURE_DOMAIN is not set
# CONFIG_SCSI_GDTH is not set
# CONFIG_SCSI_IPS is not set
# CONFIG_SCSI_INITIO is not set
# CONFIG_SCSI_INIA100 is not set
# CONFIG_SCSI_PPA is not set
# CONFIG_SCSI_IMM is not set
# CONFIG_SCSI_STEX is not set
# CONFIG_SCSI_SYM53C8XX_2 is not set
# CONFIG_SCSI_IPR is not set
# CONFIG_SCSI_QLOGIC_1280 is not set
# CONFIG_SCSI_QLA_FC is not set
# CONFIG_SCSI_QLA_ISCSI is not set
# CONFIG_SCSI_LPFC is not set
# CONFIG_SCSI_DC395x is not set
# CONFIG_SCSI_DC390T is not set
# CONFIG_SCSI_DEBUG is not set

#
# Serial ATA (prod) and Parallel ATA (experimental) drivers #
CONFIG_ATA=y
# CONFIG_SATA_AHCI is not set
# CONFIG_SATA_SVW is not set
# CONFIG_ATA_PIIX is not set
# CONFIG_SATA_MV is not set
# CONFIG_SATA_NV is not set
# CONFIG_PDC_ADMA is not set
# CONFIG_SATA_QSTOR is not set CONFIG_SATA_PROMISE=m
# CONFIG_SATA_SX4 is not set
# CONFIG_SATA_SIL is not set
# CONFIG_SATA_SIL24 is not set
# CONFIG_SATA_SIS is not set
# CONFIG_SATA_ULI is not set
CONFIG_SATA_VIA=y
# CONFIG_SATA_VITESSE is not set
# CONFIG_PATA_ALI is not set
# CONFIG_PATA_AMD is not set
# CONFIG_PATA_ARTOP is not set
# CONFIG_PATA_ATIIXP is not set
# CONFIG_PATA_CMD64X is not set
# CONFIG_PATA_CS5520 is not set
# CONFIG_PATA_CS5530 is not set
# CONFIG_PATA_CYPRESS is not set
# CONFIG_PATA_EFAR is not set
# CONFIG_ATA_GENERIC is not set
# CONFIG_PATA_HPT366 is not set
# CONFIG_PATA_HPT37X is not set
# CONFIG_PATA_HPT3X2N is not set
# C# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set

#
# ISDN subsystem
#
CONFIG_ISDN=m

#
# Old ISDN4Linux
#
CONFIG_ISDN_I4L=m
CONFIG_ISDN_PPP=y
CONFIG_ISDN_PPP_VJ=y
CONFIG_ISDN_MPP=y
# CONFIG_IPPP_FILTER is not set
CONFIG_ISDN_PPP_BSDCOMP=m
CONFIG_ISDN_AUDIO=y
CONFIG_ISDN_TTY_FAX=y

#
# ISDN feature submodules
#
# CONFIG_ISDN_DRV_LOOP is not set
CONFIG_ISDN_DIVERSION=m

#
# ISDN4Linux hardware drivers
#

#
# Passive cards
#
CONFIG_ISDN_DRV_HISAX=m

#
# D-channel protocol features
#
CONFIG_HISAX_EURO=y
CONFIG_DE_AOC=y
# CONFIG_HISAX_NO_SENDCOMPLETE is not set
# CONFIG_HISAX_NO_LLC is not set
# CONFIG_HISAX_NO_KEYPAD is not set
# CONFIG_HISAX_1TR6 is not set
# CONFIG_HISAX_NI1 is not set
CONFIG_HISAX_MAX_CARDS=8

#
# HiSax supported cards
#
# CONFIG_HISAX_16_3 is not set
# CONFIG_HISAX_TELESPCI is not set
# CONFIG_HISAX_S0BOX is not set
CONFIG_HISAX_FRITZPCI=y
# CONFIG_HISAX_AVM_A1_PCMCIA is not set
# CONFIG_HISAX_ELSA is not set
# CONFIG_HISAX_DIEHLDIVA is not set
# CONFIG_HISAX_SEDLBAUER is not set
# CONFIG_HISAX_NETJET is not set
# CONFIG_HISAX_NETJET_U is not set
# CONFIG_HISAX_NICCY is not set
# CONFIG_HISAX_BKM_A4T is not set
# CONFIG_HISAX_SCT_QUADRO is not set
# CONFIG_HISAX_GAZEL is not set
# CONFIG_HISAX_HFC_PCI is not set
# CONFIG_HISAX_W6692 is not set
# CONFIG_HISAX_HFC_SX is not set
# CONFIG_HISAX_DEBUG is not set

#
# HiSax PCMCIA card service modules
#

#
# HiSax sub driver modules
#
# CONFIG_HISAX_ST5481 is not set
# CONFIG_HISAX_HFCUSB is not set
# CONFIG_HISAX_HFC4S8S is not set
CONFIG_HISAX_FRITZ_PCIPNP=m

#
# Active cards
#
# CONFIG_HYSDN is not set

#
# Siemens Gigaset
#
# CONFIG_ISDN_DRV_GIGASET is not set

#
# CAPI subsystem
#
CONFIG_ISDN_CAPI=m
# CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON is not set
CONFIG_ISDN_CAPI_MIDDLEWARE=y
CONFIG_ISDN_CAPI_CAPI20=m
CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
CONFIG_ISDN_CAPI_CAPIFS=m
# CONFIG_ISDN_CAPI_CAPIDRV is not set

#
# CAPI hardware drivers
#

#
# Active AVM cards
#
# CONFIG_CAPI_AVM is not set

#
# Active Eicon DIVA Server cards
#
# CONFIG_CAPI_EICON is not set

#
# Telephony Support
#
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set

#
# Usepport
#
# CONFIG_SPI is not set
# CONFIG_SPI_MASTER is not set

#
# Dallas's 1-wire bus
#
CONFIG_W1=m

#
# 1-wire Bus Masters
#
# CONFIG_W1_MASTER_MATROX is not set
# CONFIG_W1_MASTER_DS2490 is not set
# CONFIG_W1_MASTER_DS2482 is not set

#
# 1-wire Slaves
#
CONFIG_W1_SLAVE_THERM=m
CONFIG_W1_SLAVE_SMEM=m
CONFIG_W1_SLAVE_DS2433=m
# CONFIG_W1_SLAVE_DS2433_CRC is not set

#
# Hardware Monitoring support
#
CONFIG_HWMON=m
CONFIG_HWMON_VID=m
# CONFIG_SENSORS_ABITUGURU is not set
CONFIG_SENSORS_ADM1021=m
CONFIG_SENSORS_ADM1025=m
CONFIG_SENSORS_ADM1026=m
CONFIG_SENSORS_ADM1031=m
CONFIG_SENSORS_ADM9240=m
CONFIG_SENSORS_K8TEMP=m
CONFIG_SENSORS_ASB100=m
# CONFIG_SENSORS_ATXP1 is not set
CONFIG_SENSORS_DS1621=m
# CONFIG_SENSORS_F71805F is not set
CONFIG_SENSORS_FSCHER=m
CONFIG_SENSORS_FSCPOS=m
CONFIG_SENSORS_GL518SM=m
CONFIG_SENSORS_GL520SM=m
CONFIG_SENSORS_IT87=m
CONFIG_SENSORS_LM63=m
CONFIG_SENSORS_LM75=m
CONFIG_SENSORS_LM77=m
CONFIG_SENSORS_LM78=m
CONFIG_SENSORS_LM80=m
CONFIG_SENSORS_LM83=m
CONFIG_SENSORS_LM85=m
CONFIG_SENSORS_LM87=m
CONFIG_SENSORS_LM90=m
CONFIG_SENSORS_LM92=m
CONFIG_SENSORS_MAX1619=m
CONFIG_SENSORS_PC87360=m
CONFIG_SENSORS_SIS5595=m
CONFIG_SENSORS_SMSC47M1=m
# CONFIG_SENSORS_SMSC47M192 is not set
CONFIG_SENSORS_SMSC47B397=m
CONFIG_SENSORS_VIA686A=m
CONFIG_SENSORS_VT1211=m
CONFIG_SENSORS_VT8231=m
CONFIG_SENSORS_W83781D=m
# CONFIG_SENSORS_W83791D is not set
CONFIG_SENSORS_W83792D=m
CONFIG_SENSORS_W83L785TS=m
CONFIG_SENSORS_W83627HF=m
CONFIG_SENSORS_W83627EHF=m
# CONFIG_SENSORS_HDAPS is not set
# CONFIG_HWMON_DEBUG_CHIP is not set

#
# Multimedia devices
#
CONFIG_VIDEO_DEV=m
CONFIG_VIDEO_V4L1=y
CONFIG_VIDEO_V4L1_COMPAT=y
CONFIG_VIDEO_V4L2=y

#
# Video Capture Adapters
#

#
# Video Capture Adapters
#
# CONFIG_VIDEO_ADV_DEBUG is not set
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_VIDEO_TVAUDIO=m
CONFIG_VIDEO_TDA7432=m
CONFIG_VIDEO_TDA9875=m
CONFIG_VIDEO_MSP3400=m
# CONFIG_VIDEO_VIVI is not set
CONFIG_VIDEO_BT848=m
# CONFIG_VIDEO_BT848_DVB is not set
CONFIG_VIDEO_SAA6588=m
# CONFIG_VIDEO_BWQCAM is not set
# CONFIG_VIDEO_CQCAM is not set
# CONFIG_VIDEO_W9966 is not set
# CONFIG_VIDEO_CPIA is not set
# CONFIG_VIDEO_CPIA2 is not set
CONFIG_VIDEO_SAA5246A=m
CONFIG_VIDEO__UART=m
CONFIG_SND_AC97_CODEC=m
CONFIG_SND_AC97_BUS=m
CONFIG_SND_DUMMY=m
CONFIG_SND_VIRMIDI=m
# CONFIG_SND_MTPAV is not set
# CONFIG_SND_MTS64 is not set
# CONFIG_SND_SERIAL_U16550 is not set
CONFIG_SND_MPU401=m

#
# PCI devices
#
# CONFIG_SND_AD1889 is not set
# CONFIG_SND_ALS300 is not set
# CONFIG_SND_ALS4000 is not set
# CONFIG_SND_ALI5451 is not set
# CONFIG_SND_ATIIXP is not set
# CONFIG_SND_ATIIXP_MODEM is not set
# CONFIG_SND_AU8810 is not set
# CONFIG_SND_AU8820 is not set
# CONFIG_SND_AU8830 is not set
# CONFIG_SND_AZT3328 is not set
CONFIG_SND_BT87X=m
# CONFIG_SND_BT87X_OVERCLOCK is not set
# CONFIG_SND_CA0106 is not set
# CONFIG_SND_CMIPCI is not set
# CONFIG_SND_CS4281 is not set
# CONFIG_SND_CS46XX is not set
# CONFIG_SND_DARLA20 is not set
# CONFIG_SND_GINA20 is not set
# CONFIG_SND_LAYLA20 is not set
# CONFIG_SND_DARLA24 is not set
# CONFIG_SND_GINA24 is not set
# CONFIG_SND_LAYLA24 is not set
# CONFIG_SND_MONA is not set
# CONFIG_SND_MIA is not set
# CONFIG_SND_ECHO3G is not set
# CONFIG_SND_INDIGO is not set
# CONFIG_SND_INDIGOIO is not set
# CONFIG_SND_INDIGODJ is not set
# CONFIG_SND_EMU10K1 is not set
# CONFIG_SND_EMU10K1X is not set
# CONFIG_SND_ENS1370 is not set
CONFIG_SND_ENS1371=m
# CONFIG_SND_ES1938 is not set
# CONFIG_SND_ES1968 is not set
# CONFIG_SND_FM801 is not set
# CONFIG_SND_HDA_INTEL is not set
# CONFIG_SND_HDSP is not set
# CONFIG_SND_HDSPM is not set
# CONFIG_SND_ICE1712 is not set
# CONFIG_SND_ICE1724 is not set
# CONFIG_SND_INTEL8X0 is not set
# CONFIG_SND_INTEL8X0M is not set
# CONFIG_SND_KORG1212 is not set
# CONFIG_SND_MAESTRO3 is not set
# CONFIG_SND_MIXART is not set
# CONFIG_SND_NM256 is not set
# CONFIG_SND_PCXHR is not set
# CONFIG_SND_RIPTIDE is not set
# CONFIG_SND_RME32 is not set
# CONFIG_SND_RME96 is not set
# CONFIG_SND_RME9652 is not set
# CONFIG_SND_SONICVIBES is not set
# CONFIG_SND_TRIDENT is not set
CONFIG_SND_VIA82XX=m
# CONFIG_SND_VIA82XX_MODEM is not set
# CONFIG_SND_VX222 is not set
# CONFIG_SND_YMFPCI is not set
CONFIG_SND_AC97_POWER_SAVE=y

#
# USB devices
#
CONFIG_SND_USB_AUDIO=m
# CONFIG_SND_USB_USX2Y is not set

#
# Open Sound System
#
# CONFIG_SOUND_PRIME is not set

#
# USB support
#
CONFIG_USB_ARCH_HAS_HCD=y
CONFIG_USB_ARCH_HAS_OHCI=y
CONFIG_USB_ARCH_HAS_EHCI=y
CONFIG_USB=m
# CONFIG_USB_DEBUG is not set

#
# Miscellaneous USB options
#
CONFIG_USB_DEVICEFS=y
# CONFIG_USB_BANDWIDTH is not set
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_OTG is not set

#
# USB Host Controller Drivers
#
CONFIG_USB_EHCI_HCD=m
CONFIG_USB_EHCI_SPLIT_ISO=y
CONFIG_USB_EHCI_ROOT_HUB_TT=y
# CONFIG_USB_EHCI_TT_NEWSCHED is not set
# CONFIG_USB_ISP116X_HCD is not set
CONFIG_USB_OHCI_HCD=m
# CONFIG_USB_OHCI_BIG_ENDIAN is not set
CONFIG_USB_OHCI_LITTLE_ENDIAN=y
CONFIG_USB_UHCI_HCD=m
# CONFIG_USB_SL811_HCD is not set

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
CONFIG_USB_PRINTER=m

#
# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' #

#

# may also be needed; see USB_STORAGE Help for more information
#
CONFIG_USB_STORAGE=m
# CONFIG_USB_STORAGE_DEBUG is not set
# CONFIG_USB_STORAGE_DATAFAB is not set
# CONFIG_USB_STORAGE_FREECOM is not set
# CONFIG_USB_STORAGE_DPCM is not set
# CONFIG_USB_STORAGE_USBAT is not set
# CONFIG_USB_STORAGE_SDDR09 is not set
# CONFIG_USB_STORAGE_SDDR55 is not set
# CONFIG_USB_STORAGE_JUMPSHOT is not set
# CONFIG_USB_STORAGE_ALAUDA is not set
# CONFIG_USB_STORAGE_KARMA is not set
# CONFIG_USB_LIBUSUAL is not set

#
# USB Input Devices
#
CONFIG_USB_HID=m
CONFIG_USB_HIDINPUT=y
# CONFIG_USB_HIDINPUT_POWERBOOK is not set
# CONFIG_HID_FF is not set
CONFIG_USB_HIDDEV=y

#
# USB HID Boot Protocol drivers
#
# CONFIG_USB_KBD is not set
# CONFIG_USB_MOUSE is not set
# CONFIG_USB_AIPTEK is not set
# CONFIG_USB_WACOM is not set
# CONFIG_USB_ACECAD is not set
# CONFIG_USB_KBTAB is not set
# CONFIG_USB_POWERMATE is not set
# CONFIG_USB_TOUCHSCREEN is not set
# CONFIG_USB_YEALINK is not set
# CONFIG_USB_XPAD is not set
# CONFIG_USB_ATI_REMOTE is not set
# CONFIG_USB_ATI_REMOTE2 is not set
# CONFIG_USB_KEYSPAN_REMOTE is not set
# CONFIG_USB_APPLETOUCH is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set
# CONFIG_USB_MICROTEK is not set

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
# CONFIG_USB_USBNET_MII is not set
# CONFIG_USB_USBNET is not set
# CONFIG_USB_MON is not set

#
# USB port drivers
#
# CONFIG_USB_USS720 is not set

#
# USB Serial Converter support
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
# CONFIG_USB_AUERSWALD is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYPRESS_CY7C63 is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_PHIDGET is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_FTDI_ELAN is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_SISUSBVGA is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_TEST is not set

#
# USB DSL modem support
#

#
# USB Gadget Support
#
# CONFIG_USB_GADGET is not set

#
# MMC/SD Card support
#
# CONFIG_MMC is not set

#
# LED devices
#
# CONFIG_NEW_LEDS is not set

#
# LED drivers
#

#
# LED Triggers
#

#
# InfiniBand support
#
# CONFIG_INFINIBAND is not set

#
# EDAC - error detection and reporting (RAS) (EXPERIMENTAL) #
# CONFIG_EDAC is not set

#
# Real Time Clock
#
CONFIG_RTC_LIB=m
CONFIG_RTC_CLASS=m

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=m
CONFIG_RTC_INTF_PROC=m
CONFIG_RTC_INTF_DEV=m
# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set

#
# RTC drivers
#
CONFIG_RTC_DRV_X1205=m
CONFIG_RTC_DRV_DS1307=m
CONFIG_RTC_DRV_DS1553=m
CONFIG_RTC_DRV_ISL1208=m
CONFIG_RTC_DRV_DS1672=m
CONFIG_RTC_DRV_DS1742=m
CONFIG_RTC_DRV_PCF8563=m
CONFIG_RTC_DRV_PCF8583=m
CONFIG_RTC_DRV_RS5C372=m
CONFIG_RTC_DRV_M48T86=m
CONFIG_RTC_DRV_TEST=m
CONFIG_RTC_DRV_V3020=m

#
# DMA Engine support
#
# CONFIG_DMA_ENGINE is not set

#
# DMA Clients
#

#
# DMA Devices
#

#
# Firmware Drivers
#
# CONFIG_EDD is not set
# CONFIG_DELL_RBU is not set
# CONFIG_DCDBAS is not set

#
# File systems
#
CONFIG_EXT2_FS=y
# CONFIG_EXT2_FS_XATTR is not set
# CONFIG_EXT2_FS_XIP is not set
CONFIG_EXT3_FS=m
# CONFIG_EXT3_FS_XATTR is not set
# CONFIG_EXT4DEV_FS is not set
CONFIG_JBD=m
# CONFIG_JBD_DEBUG is not set
CONFIG_REISER4_FS=y
# CONFIG_REISER4_DEBUG is not set
CONFIG_REISERFS_FS=y
# CONFIG_REISERFS_CHECK is not set
# CONFIG_REISERFS_PROC_INFO is not set
# CONFIG_REISERFS_FS_XATTR is not set
# CONFIG_JFS_FS is not set
CONFIG_FS_POSIX_ACL=y
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_OCFS2_FS is not set
CONFIG_MINIX_FS=m
CONFIG_ROMFS_FS=m
CONFIG_INOTIFY=y
CONFIG_INOTIFY_USER=y
# CONFIG_QUOTA is not set
CONFIG_DNOTIFY=y
CONFIG_AUTOFS_FS=m
CONFIG_AUTOFS4_FS=m
CONFIG_FUSE_FS=m

#
# CD-ROM/DVD Filesystems
#
CONFIG_ISO9660_FS=m
CONFIG_JOLIET=y
CONFIG_ZISOFS=y
CONFIG_ZISOFS_FS=m
CONFIG_UDF_FS=m
CONFIG_UDF_NLS=y

#
# DOS/FAT/NT Filesystems
#
CONFIG_FAT_FS=m
CONFIG_MSDOS_FS=m
CONFIG_VFAT_FS=m
CONFIG_FAT_DEFAULT_CODEPAGE=850
CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-15"
CONFIG_NTFS_FS=m
# CONFIG_NTFS_DEBUG is not set
CONFIG_NTFS_RW=y

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_KCORE=y
CONFIG_PROC_SYSCTL=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_HUGETLBFS is not set
# CONFIG_HUGETLB_PAGE is not set
CONFIG_RAMFS=y
# CONFIG_CONFIGFS_FS is not set

#
# Miscellaneous filesystems
#
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
CONFIG_CRAMFS=m
# CONFIG_VXFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set

#
# Network File Systems
#
CONFIG_NFS_FS=m
CONFIG_NFS_V3=y
# CONFIG_NFS_V3_ACL is not set
CONFIG_NFS_V4=y
# CONFIG_NFS_DIRECTIO is not set
CONFIG_NFSD=m
CONFIG_NFSD_V3=y
# CONFIG_ONFIG_CRYPTO_HASH=y
CONFIG_CRYPTO_MANAGER=y
CONFIG_CRYPTO_HMAC=y
CONFIG_CRYPTO_NULL=m
CONFIG_CRYPTO_MD4=m
CONFIG_CRYPTO_MD5=y
CONFIG_CRYPTO_SHA1=m
CONFIG_CRYPTO_SHA256=m
CONFIG_CRYPTO_SHA512=m
CONFIG_CRYPTO_WP512=m
CONFIG_CRYPTO_TGR192=m
CONFIG_CRYPTO_ECB=m
CONFIG_CRYPTO_CBC=m
CONFIG_CRYPTO_DES=m
CONFIG_CRYPTO_BLOWFISH=m
CONFIG_CRYPTO_TWOFISH=m
CONFIG_CRYPTO_TWOFISH_COMMON=m
CONFIG_CRYPTO_TWOFISH_X86_64=m
CONFIG_CRYPTO_SERPENT=m
CONFIG_CRYPTO_AES=m
CONFIG_CRYPTO_AES_X86_64=m
CONFIG_CRYPTO_CAST5=m
CONFIG_CRYPTO_CAST6=m
CONFIG_CRYPTO_TEA=m
CONFIG_CRYPTO_ARC4=m
CONFIG_CRYPTO_KHAZAD=m
CONFIG_CRYPTO_ANUBIS=m
CONFIG_CRYPTO_DEFLATE=m
CONFIG_CRYPTO_MICHAEL_MIC=m
CONFIG_CRYPTO_CRC32C=m
CONFIG_CRYPTO_TEST=m

#
# Hardware crypto devices
#

#
# Library routines
#
CONFIG_CRC_CCITT=m
CONFIG_CRC16=m
CONFIG_CRC32=m
CONFIG_LIBCRC32C=m
CONFIG_ZLIB_INFLATE=y
CONFIG_ZLIB_DEFLATE=y
CONFIG_TEXTSEARCH=y
CONFIG_TEXTSEARCH_KMP=m
CONFIG_TEXTSEARCH_BM=m
CONFIG_TEXTSEARCH_FSM=m
CONFIG_PLIST=y