From: Nick Piggin <npiggin@suse.de>
To: Andrew Morton <akpm@osdl.org>
Cc: Linus Torvalds <torvalds@osdl.org>,
Peter Zijlstra <a.p.zijlstra@chello.nl>,
Linux Memory Management List <linux-mm@kvack.org>,
Greg KH <gregkh@suse.de>
Subject: Re: [patch] mm: bug in set_page_dirty_buffers
Date: Tue, 10 Oct 2006 08:52:17 +0200 [thread overview]
Message-ID: <20061010065217.GC25500@wotan.suse.de> (raw)
In-Reply-To: <20061010063900.GB25500@wotan.suse.de>
On Tue, Oct 10, 2006 at 08:39:00AM +0200, Nick Piggin wrote:
> As far as set_page_dirty races goes, I am having a bit of a look at that,
> but it would still require filesystems people to have a look.
I'm thinking something along the lines of this (untested) patch.
Some internal filesystem path might still be racy against
set_page_dirty_buffers, but that might equally be a path that
doesn't hold the page lock.
--
Index: linux-2.6/fs/buffer.c
===================================================================
--- linux-2.6.orig/fs/buffer.c
+++ linux-2.6/fs/buffer.c
@@ -701,10 +701,11 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode);
*/
int __set_page_dirty_buffers(struct page *page)
{
+ int ret;
struct address_space * const mapping = page_mapping(page);
if (unlikely(!mapping))
- return !TestSetPageDirty(page);
+ return 0;
spin_lock(&mapping->private_lock);
if (page_has_buffers(page)) {
@@ -712,26 +713,26 @@ int __set_page_dirty_buffers(struct page
struct buffer_head *bh = head;
do {
- set_buffer_dirty(bh);
+ if (!buffer_invalid(bh))
+ set_buffer_dirty(bh);
bh = bh->b_this_page;
} while (bh != head);
}
spin_unlock(&mapping->private_lock);
- if (!TestSetPageDirty(page)) {
- write_lock_irq(&mapping->tree_lock);
- if (page->mapping) { /* Race with truncate? */
- if (mapping_cap_account_dirty(mapping))
- __inc_zone_page_state(page, NR_FILE_DIRTY);
- radix_tree_tag_set(&mapping->page_tree,
- page_index(page),
- PAGECACHE_TAG_DIRTY);
- }
- write_unlock_irq(&mapping->tree_lock);
- __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
- return 1;
+ ret = 0;
+ write_lock_irq(&mapping->tree_lock);
+ if (page->mapping) { /* Race with truncate? */
+ if (mapping_cap_account_dirty(mapping))
+ __inc_zone_page_state(page, NR_FILE_DIRTY);
+ radix_tree_tag_set(&mapping->page_tree,
+ page_index(page), PAGECACHE_TAG_DIRTY);
+ ret = !TestSetPageDirty(page);
}
- return 0;
+ write_unlock_irq(&mapping->tree_lock);
+ if (ret)
+ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+ return ret;
}
EXPORT_SYMBOL(__set_page_dirty_buffers);
@@ -1407,7 +1408,6 @@ EXPORT_SYMBOL(set_bh_page);
static void discard_buffer(struct buffer_head * bh)
{
lock_buffer(bh);
- clear_buffer_dirty(bh);
bh->b_bdev = NULL;
clear_buffer_mapped(bh);
clear_buffer_req(bh);
@@ -1433,15 +1433,15 @@ static void discard_buffer(struct buffer
*/
void block_invalidatepage(struct page *page, unsigned long offset)
{
- struct address_space *mapping;
+ struct address_space *mapping = page->mapping;
struct buffer_head *head, *bh, *next;
- unsigned int curr_off = 0;
+ unsigned int curr_off;
BUG_ON(!PageLocked(page));
- spin_lock(&mapping->private_lock);
if (!page_has_buffers(page))
goto out;
+ curr_off = 0;
head = page_buffers(page);
bh = head;
do {
@@ -1456,6 +1456,23 @@ void block_invalidatepage(struct page *p
curr_off = next_off;
bh = next;
} while (bh != head);
+
+ /* strip the dirty bits and protect against concurrent set_page_dirty */
+ spin_lock(&mapping->private_lock);
+ curr_off = 0;
+ head = page_buffers(page);
+ bh = head;
+ do {
+ unsigned int next_off = curr_off + bh->b_size;
+ next = bh->b_this_page;
+
+ if (offset <= curr_off) {
+ clear_buffer_dirty(bh);
+ set_buffer_invalid(bh);
+ }
+ curr_off = next_off;
+ bh = next;
+ } while (bh != head);
spin_unlock(&mapping->private_lock);
/*
Index: linux-2.6/include/linux/buffer_head.h
===================================================================
--- linux-2.6.orig/include/linux/buffer_head.h
+++ linux-2.6/include/linux/buffer_head.h
@@ -18,6 +18,7 @@
enum bh_state_bits {
BH_Uptodate, /* Contains valid data */
+ BH_Invalid, /* Has been truncated/invalidated */
BH_Dirty, /* Is dirty */
BH_Lock, /* Is locked */
BH_Req, /* Has been submitted for I/O */
@@ -109,6 +110,7 @@ static inline int test_clear_buffer_##na
* do something in addition to setting a b_state bit.
*/
BUFFER_FNS(Uptodate, uptodate)
+BUFFER_FNS(Invalid, invalid)
BUFFER_FNS(Dirty, dirty)
TAS_BUFFER_FNS(Dirty, dirty)
BUFFER_FNS(Lock, locked)
Index: linux-2.6/mm/page-writeback.c
===================================================================
--- linux-2.6.orig/mm/page-writeback.c
+++ linux-2.6/mm/page-writeback.c
@@ -757,30 +757,36 @@ EXPORT_SYMBOL(write_one_page);
*/
int __set_page_dirty_nobuffers(struct page *page)
{
- if (!TestSetPageDirty(page)) {
- struct address_space *mapping = page_mapping(page);
+ struct address_space *mapping;
+
+ if (PageDirty(page))
+ return 0;
+
+ mapping = page_mapping(page);
+ if (mapping) { /* Race with truncate? */
+ int ret;
struct address_space *mapping2;
- if (mapping) {
- write_lock_irq(&mapping->tree_lock);
- mapping2 = page_mapping(page);
- if (mapping2) { /* Race with truncate? */
- BUG_ON(mapping2 != mapping);
- if (mapping_cap_account_dirty(mapping))
- __inc_zone_page_state(page,
- NR_FILE_DIRTY);
- radix_tree_tag_set(&mapping->page_tree,
+ ret = 0;
+ write_lock_irq(&mapping->tree_lock);
+ mapping2 = page_mapping(page);
+ if (mapping2 && !TestSetPageDirty(page)) {
+ BUG_ON(mapping2 != mapping);
+ if (mapping_cap_account_dirty(mapping))
+ __inc_zone_page_state(page, NR_FILE_DIRTY);
+ radix_tree_tag_set(&mapping->page_tree,
page_index(page), PAGECACHE_TAG_DIRTY);
- }
- write_unlock_irq(&mapping->tree_lock);
- if (mapping->host) {
- /* !PageAnon && !swapper_space */
- __mark_inode_dirty(mapping->host,
- I_DIRTY_PAGES);
- }
+ ret = 1;
}
- return 1;
+ write_unlock_irq(&mapping->tree_lock);
+ if (ret) {
+ /* !PageAnon && !swapper_space */
+ __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
+ }
+ return ret;
}
+
+ /* Don't bother dirtying truncated pages */
return 0;
}
EXPORT_SYMBOL(__set_page_dirty_nobuffers);
Index: linux-2.6/mm/vmscan.c
===================================================================
--- linux-2.6.orig/mm/vmscan.c
+++ linux-2.6/mm/vmscan.c
@@ -341,6 +341,14 @@ static pageout_t pageout(struct page *pa
return PAGE_CLEAN;
}
}
+ /*
+ * Truncate/invalidate clears dirty, and it shouldn't get dirty
+ * again (unless SetPageDirty is used instead of set_page_dirty,
+ * so this will have some false positives)
+ */
+ if (unlikely(PageDirty(page)))
+ printk("%s: dirty orphaned page\n", __FUNCTION__);
+
return PAGE_KEEP;
}
if (mapping->a_ops->writepage == NULL)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
next prev parent reply other threads:[~2006-10-10 6:52 UTC|newest]
Thread overview: 36+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-10-10 2:36 [patch] mm: bug in set_page_dirty_buffers Nick Piggin
2006-10-10 3:06 ` Linus Torvalds
2006-10-10 3:19 ` Nick Piggin
2006-10-10 3:20 ` Andrew Morton
2006-10-10 3:34 ` Nick Piggin
2006-10-10 3:50 ` Andrew Morton
2006-10-10 3:58 ` Nick Piggin
2006-10-10 4:14 ` Andrew Morton
2006-10-10 4:21 ` Nick Piggin
2006-10-10 4:38 ` Andrew Morton
2006-10-10 4:47 ` Nick Piggin
2006-10-10 5:01 ` Andrew Morton
2006-10-10 5:22 ` Nick Piggin
2006-10-10 5:29 ` Andrew Morton
2006-10-10 5:48 ` Nick Piggin
2006-10-10 6:08 ` Andrew Morton
2006-10-10 6:19 ` Nick Piggin
2006-10-10 6:27 ` Andrew Morton
2006-10-10 6:39 ` Nick Piggin
2006-10-10 6:52 ` Nick Piggin [this message]
2006-10-10 7:06 ` Andrew Morton
2006-10-10 7:21 ` Nick Piggin
2006-10-10 8:07 ` Andrew Morton
2006-10-10 8:18 ` Nick Piggin
2006-10-10 8:41 ` Andrew Morton
2006-10-10 8:49 ` Nick Piggin
2006-10-10 9:07 ` Andrew Morton
2006-10-10 9:23 ` Nick Piggin
2006-10-10 6:48 ` Peter Zijlstra
2006-10-10 6:59 ` Nick Piggin
2006-10-10 7:11 ` Peter Zijlstra
2006-10-10 7:30 ` Nick Piggin
2006-10-10 4:11 ` Nick Piggin
2006-10-10 3:37 ` Andrew Morton
2006-10-10 3:42 ` Nick Piggin
2006-10-10 7:42 ` patch mm-bug-in-set_page_dirty_buffers.patch queued to -stable tree gregkh
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20061010065217.GC25500@wotan.suse.de \
--to=npiggin@suse.de \
--cc=a.p.zijlstra@chello.nl \
--cc=akpm@osdl.org \
--cc=gregkh@suse.de \
--cc=linux-mm@kvack.org \
--cc=torvalds@osdl.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.