aboutsummaryrefslogtreecommitdiffstats
path: root/fs/xfs/xfs_log_recover.c
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2013-08-26 18:10:53 -0400
committerBen Myers <bpm@sgi.com>2013-08-29 11:37:06 -0400
commit84a5b7300c724f4000f689c410aeae3242b4f034 (patch)
tree9982c376b488c62d75ff832e27ca1da69e55a86b /fs/xfs/xfs_log_recover.c
parent0d0ab120d1fe90fcc73a2bfff3945bea636b3025 (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.c61
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 */
1783STATIC int 1775STATIC struct xfs_buf_cancel *
1784xlog_check_buffer_cancelled( 1776xlog_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 */
1815STATIC int
1816xlog_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
1818found:
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,