diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index cc3308d1c61d6a859513d184cef6006fe154b1f7..78661186c360883e7e3d4399d2d9572f12bc0ca9 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -383,6 +383,11 @@ void nilfs_clear_dirty_pages(struct address_space *mapping, bool silent) * nilfs_clear_dirty_page - discard dirty page * @page: dirty page that will be discarded * @silent: suppress [true] or print [false] warning messages + * + * nilfs_clear_dirty_page() clears working states including dirty state for + * the page and its buffers. If the page has buffers, clear only if it is + * confirmed that none of the buffer heads are busy (none have valid + * references and none are locked). */ void nilfs_clear_dirty_page(struct page *page, bool silent) { @@ -396,18 +401,35 @@ void nilfs_clear_dirty_page(struct page *page, bool silent) "discard dirty page: offset=%lld, ino=%lu", page_offset(page), inode->i_ino); - ClearPageUptodate(page); - ClearPageMappedToDisk(page); - ClearPageChecked(page); - if (page_has_buffers(page)) { struct buffer_head *bh, *head; const unsigned long clear_bits = (BIT(BH_Uptodate) | BIT(BH_Dirty) | BIT(BH_Mapped) | BIT(BH_Async_Write) | BIT(BH_NILFS_Volatile) | BIT(BH_NILFS_Checked) | BIT(BH_NILFS_Redirected)); + bool busy, invalidated = false; - bh = head = page_buffers(page); + head = page_buffers(page); + +recheck_buffers: + busy = false; + bh = head; + do { + if (atomic_read(&bh->b_count) | buffer_locked(bh)) { + busy = true; + break; + } + } while (bh = bh->b_this_page, bh != head); + + if (busy) { + if (invalidated) + return; + invalidate_bh_lrus(); + invalidated = true; + goto recheck_buffers; + } + + bh = head; do { lock_buffer(bh); if (!silent) @@ -420,6 +442,10 @@ void nilfs_clear_dirty_page(struct page *page, bool silent) } while (bh = bh->b_this_page, bh != head); } + ClearPageUptodate(page); + ClearPageMappedToDisk(page); + ClearPageChecked(page); + __nilfs_clear_page_dirty(page); }