History

fs: introduce some page/buffer invariants

It is a bug to set a page dirty if it is not uptodate unless it has buffers. If the page has buffers, then the page may be dirty (some buffers dirty) but not uptodate (some buffers not uptodate). The exception to this rule is if the set_page_dirty caller is racing with truncate or invalidate. A buffer can not be set dirty if it is not uptodate. If either of these situations occurs, it indicates there could be some data loss problem. Some of these warnings could be a harmless one where the page or buffer is set uptodate immediately after it is dirtied, however we should fix those up, and enforce this ordering. Bring the order of operations for truncate into line with those of invalidate. This will prevent a page from being able to go !uptodate while we're holding the tree_lock, which is probably a good thing anyway.

Nick Piggin committed 19 years ago

Commit 787d2211 parent a1ed3dd3 files · +39 −18Browse files

Unified · Split

3 files
  1. fs/buffer.c +37 −17
  2. mm/page-writeback.c +1 −0
  3. mm/truncate.c +1 −1
fs/buffer.c+37−17
@@ -675,6 +675,39 @@ void mark_buffer_dirty_inode(struct buffer_head *bh, struct inode *inode) · 674 unmodified lines
675}675}
676EXPORT_SYMBOL(mark_buffer_dirty_inode);676EXPORT_SYMBOL(mark_buffer_dirty_inode);
677 677
678/*
679 * Mark the page dirty, and set it dirty in the radix tree, and mark the inode
680 * dirty.
681 *
682 * If warn is true, then emit a warning if the page is not uptodate and has
683 * not been truncated.
684 */
685static int __set_page_dirty(struct page *page,
686 struct address_space *mapping, int warn)
687{
688 if (unlikely(!mapping))
689 return !TestSetPageDirty(page);
690
691 if (TestSetPageDirty(page))
692 return 0;
693
694 write_lock_irq(&mapping->tree_lock);
695 if (page->mapping) { /* Race with truncate? */
696 WARN_ON_ONCE(warn && !PageUptodate(page));
697
698 if (mapping_cap_account_dirty(mapping)) {
699 __inc_zone_page_state(page, NR_FILE_DIRTY);
700 task_io_account_write(PAGE_CACHE_SIZE);
701 }
702 radix_tree_tag_set(&mapping->page_tree,
703 page_index(page), PAGECACHE_TAG_DIRTY);
704 }
705 write_unlock_irq(&mapping->tree_lock);
706 __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
707
708 return 1;
709}
710
678/*711/*
679 * Add a page to the dirty page list.712 * Add a page to the dirty page list.
680 *713 *
@@ -702,7 +735,7 @@ EXPORT_SYMBOL(mark_buffer_dirty_inode); · 21 unmodified lines
702 */735 */
703int __set_page_dirty_buffers(struct page *page)736int __set_page_dirty_buffers(struct page *page)
704{737{
705 struct address_space * const mapping = page_mapping(page);738 struct address_space *mapping = page_mapping(page);
706 739
707 if (unlikely(!mapping))740 if (unlikely(!mapping))
708 return !TestSetPageDirty(page);741 return !TestSetPageDirty(page);
@@ -719,21 +752,7 @@ int __set_page_dirty_buffers(struct page *page) · 10 unmodified lines
719 }752 }
720 spin_unlock(&mapping->private_lock);753 spin_unlock(&mapping->private_lock);
721 754
722 if (TestSetPageDirty(page))755 return __set_page_dirty(page, mapping, 1);
723 return 0;
724
725 write_lock_irq(&mapping->tree_lock);
726 if (page->mapping) { /* Race with truncate? */
727 if (mapping_cap_account_dirty(mapping)) {
728 __inc_zone_page_state(page, NR_FILE_DIRTY);
729 task_io_account_write(PAGE_CACHE_SIZE);
730 }
731 radix_tree_tag_set(&mapping->page_tree,
732 page_index(page), PAGECACHE_TAG_DIRTY);
733 }
734 write_unlock_irq(&mapping->tree_lock);
735 __mark_inode_dirty(mapping->host, I_DIRTY_PAGES);
736 return 1;
737}756}
738EXPORT_SYMBOL(__set_page_dirty_buffers);757EXPORT_SYMBOL(__set_page_dirty_buffers);
739 758
@@ -1132,8 +1151,9 @@ __getblk_slow(struct block_device *bdev, sector_t block, int size) · 392 unmodified lines
1132 */1151 */
1133void fastcall mark_buffer_dirty(struct buffer_head *bh)1152void fastcall mark_buffer_dirty(struct buffer_head *bh)
1134{1153{
1154 WARN_ON_ONCE(!buffer_uptodate(bh));
1135 if (!buffer_dirty(bh) && !test_set_buffer_dirty(bh))1155 if (!buffer_dirty(bh) && !test_set_buffer_dirty(bh))
1136 __set_page_dirty_nobuffers(bh->b_page);1156 __set_page_dirty(bh->b_page, page_mapping(bh->b_page), 0);
1137}1157}
1138 1158
1139/*1159/*
mm/page-writeback.c+1−0
@@ -824,6 +824,7 @@ int __set_page_dirty_nobuffers(struct page *page) · 823 unmodified lines
824 mapping2 = page_mapping(page);824 mapping2 = page_mapping(page);
825 if (mapping2) { /* Race with truncate? */825 if (mapping2) { /* Race with truncate? */
826 BUG_ON(mapping2 != mapping);826 BUG_ON(mapping2 != mapping);
827 WARN_ON_ONCE(!PagePrivate(page) && !PageUptodate(page));
827 if (mapping_cap_account_dirty(mapping)) {828 if (mapping_cap_account_dirty(mapping)) {
828 __inc_zone_page_state(page, NR_FILE_DIRTY);829 __inc_zone_page_state(page, NR_FILE_DIRTY);
829 task_io_account_write(PAGE_CACHE_SIZE);830 task_io_account_write(PAGE_CACHE_SIZE);
mm/truncate.c+1−1
@@ -100,9 +100,9 @@ truncate_complete_page(struct address_space *mapping, struct page *page) · 99 unmodified lines
100 if (PagePrivate(page))100 if (PagePrivate(page))
101 do_invalidatepage(page, 0);101 do_invalidatepage(page, 0);
102 102
103 remove_from_page_cache(page);
103 ClearPageUptodate(page);104 ClearPageUptodate(page);
104 ClearPageMappedToDisk(page);105 ClearPageMappedToDisk(page);
105 remove_from_page_cache(page);
106 page_cache_release(page); /* pagecache ref */106 page_cache_release(page); /* pagecache ref */
107}107}
108 108