aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@woody.osdl.org>2006-12-20 16:46:42 -0500
committerLinus Torvalds <torvalds@woody.osdl.org>2006-12-21 12:19:57 -0500
commitfba2591bf4e418b6c3f9f8794c9dd8fe40ae7bd9 (patch)
treed6909973e402b3171ee409f660b33df2fad029ba
parent46d2277c796f9f4937bfa668c40b2e3f43e93dd0 (diff)
VM: Remove "clear_page_dirty()" and "test_clear_page_dirty()" functions
They were horribly easy to mis-use because of their tempting naming, and they also did way more than any users of them generally wanted them to do. A dirty page can become clean under two circumstances: (a) when we write it out. We have "clear_page_dirty_for_io()" for this, and that function remains unchanged. In the "for IO" case it is not sufficient to just clear the dirty bit, you also have to mark the page as being under writeback etc. (b) when we actually remove a page due to it becoming inaccessible to users, notably because it was truncate()'d away or the file (or metadata) no longer exists, and we thus want to cancel any outstanding dirty state. For the (b) case, we now introduce "cancel_dirty_page()", which only touches the page state itself, and verifies that the page is not mapped (since cancelling writes on a mapped page would be actively wrong as it is still accessible to users). Some filesystems need to be fixed up for this: CIFS, FUSE, JFS, ReiserFS, XFS all use the old confusing functions, and will be fixed separately in subsequent commits (with some of them just removing the offending logic, and others using clear_page_dirty_for_io()). This was confirmed by Martin Michlmayr to fix the apt database corruption on ARM. Cc: Martin Michlmayr <tbm@cyrius.com> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Hugh Dickins <hugh@veritas.com> Cc: Nick Piggin <nickpiggin@yahoo.com.au> Cc: Arjan van de Ven <arjan@infradead.org> Cc: Andrei Popa <andrei.popa@i-neo.ro> Cc: Andrew Morton <akpm@osdl.org> Cc: Dave Kleikamp <shaggy@linux.vnet.ibm.com> Cc: Gordon Farquharson <gordonfarquharson@gmail.com> Cc: Martin Schwidefsky <schwidefsky@de.ibm.com> Cc: Trond Myklebust <trond.myklebust@fys.uio.no> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
-rw-r--r--fs/hugetlbfs/inode.c2
-rw-r--r--include/linux/page-flags.h8
-rw-r--r--mm/page-writeback.c32
-rw-r--r--mm/truncate.c25
4 files changed, 20 insertions, 47 deletions
diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c
index ed2c22340ad7..4f4cd132b571 100644
--- a/fs/hugetlbfs/inode.c
+++ b/fs/hugetlbfs/inode.c
@@ -176,7 +176,7 @@ static int hugetlbfs_commit_write(struct file *file,
176 176
177static void truncate_huge_page(struct page *page) 177static void truncate_huge_page(struct page *page)
178{ 178{
179 clear_page_dirty(page); 179 cancel_dirty_page(page, /* No IO accounting for huge pages? */0);
180 ClearPageUptodate(page); 180 ClearPageUptodate(page);
181 remove_from_page_cache(page); 181 remove_from_page_cache(page);
182 put_page(page); 182 put_page(page);
diff --git a/include/linux/page-flags.h b/include/linux/page-flags.h
index 4830a3bedfb2..350878a2d848 100644
--- a/include/linux/page-flags.h
+++ b/include/linux/page-flags.h
@@ -253,15 +253,11 @@ static inline void SetPageUptodate(struct page *page)
253 253
254struct page; /* forward declaration */ 254struct page; /* forward declaration */
255 255
256int test_clear_page_dirty(struct page *page); 256extern void cancel_dirty_page(struct page *page, unsigned int account_size);
257
257int test_clear_page_writeback(struct page *page); 258int test_clear_page_writeback(struct page *page);
258int test_set_page_writeback(struct page *page); 259int test_set_page_writeback(struct page *page);
259 260
260static inline void clear_page_dirty(struct page *page)
261{
262 test_clear_page_dirty(page);
263}
264
265static inline void set_page_writeback(struct page *page) 261static inline void set_page_writeback(struct page *page)
266{ 262{
267 test_set_page_writeback(page); 263 test_set_page_writeback(page);
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
index 237107c1b084..b3a198c9248d 100644
--- a/mm/page-writeback.c
+++ b/mm/page-writeback.c
@@ -845,38 +845,6 @@ int set_page_dirty_lock(struct page *page)
845EXPORT_SYMBOL(set_page_dirty_lock); 845EXPORT_SYMBOL(set_page_dirty_lock);
846 846
847/* 847/*
848 * Clear a page's dirty flag, while caring for dirty memory accounting.
849 * Returns true if the page was previously dirty.
850 */
851int test_clear_page_dirty(struct page *page)
852{
853 struct address_space *mapping = page_mapping(page);
854 unsigned long flags;
855
856 if (!mapping)
857 return TestClearPageDirty(page);
858
859 write_lock_irqsave(&mapping->tree_lock, flags);
860 if (TestClearPageDirty(page)) {
861 radix_tree_tag_clear(&mapping->page_tree,
862 page_index(page), PAGECACHE_TAG_DIRTY);
863 write_unlock_irqrestore(&mapping->tree_lock, flags);
864 /*
865 * We can continue to use `mapping' here because the
866 * page is locked, which pins the address_space
867 */
868 if (mapping_cap_account_dirty(mapping)) {
869 page_mkclean(page);
870 dec_zone_page_state(page, NR_FILE_DIRTY);
871 }
872 return 1;
873 }
874 write_unlock_irqrestore(&mapping->tree_lock, flags);
875 return 0;
876}
877EXPORT_SYMBOL(test_clear_page_dirty);
878
879/*
880 * Clear a page's dirty flag, while caring for dirty memory accounting. 848 * Clear a page's dirty flag, while caring for dirty memory accounting.
881 * Returns true if the page was previously dirty. 849 * Returns true if the page was previously dirty.
882 * 850 *
diff --git a/mm/truncate.c b/mm/truncate.c
index 9bfb8e853860..bf9e2965d666 100644
--- a/mm/truncate.c
+++ b/mm/truncate.c
@@ -51,6 +51,20 @@ static inline void truncate_partial_page(struct page *page, unsigned partial)
51 do_invalidatepage(page, partial); 51 do_invalidatepage(page, partial);
52} 52}
53 53
54void cancel_dirty_page(struct page *page, unsigned int account_size)
55{
56 /* If we're cancelling the page, it had better not be mapped any more */
57 if (page_mapped(page)) {
58 static unsigned int warncount;
59
60 WARN_ON(++warncount < 5);
61 }
62
63 if (TestClearPageDirty(page) && account_size)
64 task_io_account_cancelled_write(account_size);
65}
66
67
54/* 68/*
55 * If truncate cannot remove the fs-private metadata from the page, the page 69 * If truncate cannot remove the fs-private metadata from the page, the page
56 * becomes anonymous. It will be left on the LRU and may even be mapped into 70 * 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)
70 if (PagePrivate(page)) 84 if (PagePrivate(page))
71 do_invalidatepage(page, 0); 85 do_invalidatepage(page, 0);
72 86
73 if (test_clear_page_dirty(page)) 87 cancel_dirty_page(page, PAGE_CACHE_SIZE);
74 task_io_account_cancelled_write(PAGE_CACHE_SIZE); 88
75 ClearPageUptodate(page); 89 ClearPageUptodate(page);
76 ClearPageMappedToDisk(page); 90 ClearPageMappedToDisk(page);
77 remove_from_page_cache(page); 91 remove_from_page_cache(page);
@@ -350,7 +364,6 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
350 for (i = 0; !ret && i < pagevec_count(&pvec); i++) { 364 for (i = 0; !ret && i < pagevec_count(&pvec); i++) {
351 struct page *page = pvec.pages[i]; 365 struct page *page = pvec.pages[i];
352 pgoff_t page_index; 366 pgoff_t page_index;
353 int was_dirty;
354 367
355 lock_page(page); 368 lock_page(page);
356 if (page->mapping != mapping) { 369 if (page->mapping != mapping) {
@@ -386,12 +399,8 @@ int invalidate_inode_pages2_range(struct address_space *mapping,
386 PAGE_CACHE_SIZE, 0); 399 PAGE_CACHE_SIZE, 0);
387 } 400 }
388 } 401 }
389 was_dirty = test_clear_page_dirty(page); 402 if (!invalidate_complete_page2(mapping, page))
390 if (!invalidate_complete_page2(mapping, page)) {
391 if (was_dirty)
392 set_page_dirty(page);
393 ret = -EIO; 403 ret = -EIO;
394 }
395 unlock_page(page); 404 unlock_page(page);
396 } 405 }
397 pagevec_release(&pvec); 406 pagevec_release(&pvec);