diff --git a/fs/nilfs2/page.c b/fs/nilfs2/page.c index d2d6d5c761e8db60931562c02058e6d32d0ecac5..3b97d2f51ec91e093494352cb1a706429896457b 100644 --- a/fs/nilfs2/page.c +++ b/fs/nilfs2/page.c @@ -390,6 +390,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) { @@ -402,10 +407,6 @@ void nilfs_clear_dirty_page(struct page *page, bool silent) nilfs_warn(sb, "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 = @@ -413,8 +414,28 @@ void nilfs_clear_dirty_page(struct page *page, bool silent) BIT(BH_Async_Write) | BIT(BH_NILFS_Volatile) | BIT(BH_NILFS_Checked) | BIT(BH_NILFS_Redirected) | BIT(BH_Delay)); + 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) @@ -427,6 +448,9 @@ 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); }