diff options
author | Dave Chinner <dchinner@redhat.com> | 2013-08-26 18:10:53 -0400 |
---|---|---|
committer | Ben Myers <bpm@sgi.com> | 2013-08-29 11:37:06 -0400 |
commit | 84a5b7300c724f4000f689c410aeae3242b4f034 (patch) | |
tree | 9982c376b488c62d75ff832e27ca1da69e55a86b /fs/xfs/xfs_log_recover.c | |
parent | 0d0ab120d1fe90fcc73a2bfff3945bea636b3025 (diff) |
xfs: don't account buffer cancellation during log recovery readahead
When doing readhaead in log recovery, we check to see if buffers are
cancelled before doing readahead. If we find a cancelled buffer,
however, we always decrement the reference count we have on it, and
that means that readahead is causing a double decrement of the
cancelled buffer reference count.
This results in log recovery *replaying cancelled buffers* as the
actual recovery pass does not find the cancelled buffer entry in the
commit phase of the second pass across a transaction. On debug
kernels, this results in an ASSERT failure like so:
XFS: Assertion failed: !(flags & XFS_BLF_CANCEL), file: fs/xfs/xfs_log_recover.c, line: 1815
xfstests generic/311 reproduces this ASSERT failure with 100%
reproducability.
Fix it by making readahead only peek at the buffer cancelled state
rather than the full accounting that xlog_check_buffer_cancelled()
does.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Ben Myers <bpm@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
Diffstat (limited to 'fs/xfs/xfs_log_recover.c')
-rw-r--r-- | fs/xfs/xfs_log_recover.c | 61 |
1 files changed, 35 insertions, 26 deletions
diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 64e530e67053..90b756f7117c 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c | |||
@@ -1769,19 +1769,11 @@ xlog_recover_buffer_pass1( | |||
1769 | 1769 | ||
1770 | /* | 1770 | /* |
1771 | * Check to see whether the buffer being recovered has a corresponding | 1771 | * Check to see whether the buffer being recovered has a corresponding |
1772 | * entry in the buffer cancel record table. If it does then return 1 | 1772 | * entry in the buffer cancel record table. If it is, return the cancel |
1773 | * so that it will be cancelled, otherwise return 0. If the buffer is | 1773 | * buffer structure to the caller. |
1774 | * actually a buffer cancel item (XFS_BLF_CANCEL is set), then decrement | ||
1775 | * the refcount on the entry in the table and remove it from the table | ||
1776 | * if this is the last reference. | ||
1777 | * | ||
1778 | * We remove the cancel record from the table when we encounter its | ||
1779 | * last occurrence in the log so that if the same buffer is re-used | ||
1780 | * again after its last cancellation we actually replay the changes | ||
1781 | * made at that point. | ||
1782 | */ | 1774 | */ |
1783 | STATIC int | 1775 | STATIC struct xfs_buf_cancel * |
1784 | xlog_check_buffer_cancelled( | 1776 | xlog_peek_buffer_cancelled( |
1785 | struct xlog *log, | 1777 | struct xlog *log, |
1786 | xfs_daddr_t blkno, | 1778 | xfs_daddr_t blkno, |
1787 | uint len, | 1779 | uint len, |
@@ -1790,22 +1782,16 @@ xlog_check_buffer_cancelled( | |||
1790 | struct list_head *bucket; | 1782 | struct list_head *bucket; |
1791 | struct xfs_buf_cancel *bcp; | 1783 | struct xfs_buf_cancel *bcp; |
1792 | 1784 | ||
1793 | if (log->l_buf_cancel_table == NULL) { | 1785 | if (!log->l_buf_cancel_table) { |
1794 | /* | 1786 | /* empty table means no cancelled buffers in the log */ |
1795 | * There is nothing in the table built in pass one, | ||
1796 | * so this buffer must not be cancelled. | ||
1797 | */ | ||
1798 | ASSERT(!(flags & XFS_BLF_CANCEL)); | 1787 | ASSERT(!(flags & XFS_BLF_CANCEL)); |
1799 | return 0; | 1788 | return NULL; |
1800 | } | 1789 | } |
1801 | 1790 | ||
1802 | /* | ||
1803 | * Search for an entry in the cancel table that matches our buffer. | ||
1804 | */ | ||
1805 | bucket = XLOG_BUF_CANCEL_BUCKET(log, blkno); | 1791 | bucket = XLOG_BUF_CANCEL_BUCKET(log, blkno); |
1806 | list_for_each_entry(bcp, bucket, bc_list) { | 1792 | list_for_each_entry(bcp, bucket, bc_list) { |
1807 | if (bcp->bc_blkno == blkno && bcp->bc_len == len) | 1793 | if (bcp->bc_blkno == blkno && bcp->bc_len == len) |
1808 | goto found; | 1794 | return bcp; |
1809 | } | 1795 | } |
1810 | 1796 | ||
1811 | /* | 1797 | /* |
@@ -1813,9 +1799,32 @@ xlog_check_buffer_cancelled( | |||
1813 | * that the buffer is NOT cancelled. | 1799 | * that the buffer is NOT cancelled. |
1814 | */ | 1800 | */ |
1815 | ASSERT(!(flags & XFS_BLF_CANCEL)); | 1801 | ASSERT(!(flags & XFS_BLF_CANCEL)); |
1816 | return 0; | 1802 | return NULL; |
1803 | } | ||
1804 | |||
1805 | /* | ||
1806 | * If the buffer is being cancelled then return 1 so that it will be cancelled, | ||
1807 | * otherwise return 0. If the buffer is actually a buffer cancel item | ||
1808 | * (XFS_BLF_CANCEL is set), then decrement the refcount on the entry in the | ||
1809 | * table and remove it from the table if this is the last reference. | ||
1810 | * | ||
1811 | * We remove the cancel record from the table when we encounter its last | ||
1812 | * occurrence in the log so that if the same buffer is re-used again after its | ||
1813 | * last cancellation we actually replay the changes made at that point. | ||
1814 | */ | ||
1815 | STATIC int | ||
1816 | xlog_check_buffer_cancelled( | ||
1817 | struct xlog *log, | ||
1818 | xfs_daddr_t blkno, | ||
1819 | uint len, | ||
1820 | ushort flags) | ||
1821 | { | ||
1822 | struct xfs_buf_cancel *bcp; | ||
1823 | |||
1824 | bcp = xlog_peek_buffer_cancelled(log, blkno, len, flags); | ||
1825 | if (!bcp) | ||
1826 | return 0; | ||
1817 | 1827 | ||
1818 | found: | ||
1819 | /* | 1828 | /* |
1820 | * We've go a match, so return 1 so that the recovery of this buffer | 1829 | * We've go a match, so return 1 so that the recovery of this buffer |
1821 | * is cancelled. If this buffer is actually a buffer cancel log | 1830 | * is cancelled. If this buffer is actually a buffer cancel log |
@@ -3127,7 +3136,7 @@ xlog_recover_buffer_ra_pass2( | |||
3127 | struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr; | 3136 | struct xfs_buf_log_format *buf_f = item->ri_buf[0].i_addr; |
3128 | struct xfs_mount *mp = log->l_mp; | 3137 | struct xfs_mount *mp = log->l_mp; |
3129 | 3138 | ||
3130 | if (xlog_check_buffer_cancelled(log, buf_f->blf_blkno, | 3139 | if (xlog_peek_buffer_cancelled(log, buf_f->blf_blkno, |
3131 | buf_f->blf_len, buf_f->blf_flags)) { | 3140 | buf_f->blf_len, buf_f->blf_flags)) { |
3132 | return; | 3141 | return; |
3133 | } | 3142 | } |
@@ -3156,7 +3165,7 @@ xlog_recover_inode_ra_pass2( | |||
3156 | return; | 3165 | return; |
3157 | } | 3166 | } |
3158 | 3167 | ||
3159 | if (xlog_check_buffer_cancelled(log, ilfp->ilf_blkno, ilfp->ilf_len, 0)) | 3168 | if (xlog_peek_buffer_cancelled(log, ilfp->ilf_blkno, ilfp->ilf_len, 0)) |
3160 | return; | 3169 | return; |
3161 | 3170 | ||
3162 | xfs_buf_readahead(mp->m_ddev_targp, ilfp->ilf_blkno, | 3171 | xfs_buf_readahead(mp->m_ddev_targp, ilfp->ilf_blkno, |