📄 xfs_log_recover.c
字号:
/* * Copyright (c) 2000-2006 Silicon Graphics, Inc. * All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation. * * This program is distributed in the hope that it would be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */#include "xfs.h"#include "xfs_fs.h"#include "xfs_types.h"#include "xfs_bit.h"#include "xfs_log.h"#include "xfs_inum.h"#include "xfs_trans.h"#include "xfs_sb.h"#include "xfs_ag.h"#include "xfs_dir2.h"#include "xfs_dmapi.h"#include "xfs_mount.h"#include "xfs_error.h"#include "xfs_bmap_btree.h"#include "xfs_alloc_btree.h"#include "xfs_ialloc_btree.h"#include "xfs_dir2_sf.h"#include "xfs_attr_sf.h"#include "xfs_dinode.h"#include "xfs_inode.h"#include "xfs_inode_item.h"#include "xfs_imap.h"#include "xfs_alloc.h"#include "xfs_ialloc.h"#include "xfs_log_priv.h"#include "xfs_buf_item.h"#include "xfs_log_recover.h"#include "xfs_extfree_item.h"#include "xfs_trans_priv.h"#include "xfs_quota.h"#include "xfs_rw.h"STATIC int xlog_find_zeroed(xlog_t *, xfs_daddr_t *);STATIC int xlog_clear_stale_blocks(xlog_t *, xfs_lsn_t);STATIC void xlog_recover_insert_item_backq(xlog_recover_item_t **q, xlog_recover_item_t *item);#if defined(DEBUG)STATIC void xlog_recover_check_summary(xlog_t *);STATIC void xlog_recover_check_ail(xfs_mount_t *, xfs_log_item_t *, int);#else#define xlog_recover_check_summary(log)#define xlog_recover_check_ail(mp, lip, gen)#endif/* * Sector aligned buffer routines for buffer create/read/write/access */#define XLOG_SECTOR_ROUNDUP_BBCOUNT(log, bbs) \ ( ((log)->l_sectbb_mask && (bbs & (log)->l_sectbb_mask)) ? \ ((bbs + (log)->l_sectbb_mask + 1) & ~(log)->l_sectbb_mask) : (bbs) )#define XLOG_SECTOR_ROUNDDOWN_BLKNO(log, bno) ((bno) & ~(log)->l_sectbb_mask)xfs_buf_t *xlog_get_bp( xlog_t *log, int num_bblks){ ASSERT(num_bblks > 0); if (log->l_sectbb_log) { if (num_bblks > 1) num_bblks += XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); num_bblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, num_bblks); } return xfs_buf_get_noaddr(BBTOB(num_bblks), log->l_mp->m_logdev_targp);}voidxlog_put_bp( xfs_buf_t *bp){ xfs_buf_free(bp);}/* * nbblks should be uint, but oh well. Just want to catch that 32-bit length. */intxlog_bread( xlog_t *log, xfs_daddr_t blk_no, int nbblks, xfs_buf_t *bp){ int error; if (log->l_sectbb_log) { blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); } ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); ASSERT(bp); XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no); XFS_BUF_READ(bp); XFS_BUF_BUSY(bp); XFS_BUF_SET_COUNT(bp, BBTOB(nbblks)); XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp); xfsbdstrat(log->l_mp, bp); if ((error = xfs_iowait(bp))) xfs_ioerror_alert("xlog_bread", log->l_mp, bp, XFS_BUF_ADDR(bp)); return error;}/* * Write out the buffer at the given block for the given number of blocks. * The buffer is kept locked across the write and is returned locked. * This can only be used for synchronous log writes. */STATIC intxlog_bwrite( xlog_t *log, xfs_daddr_t blk_no, int nbblks, xfs_buf_t *bp){ int error; if (log->l_sectbb_log) { blk_no = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, blk_no); nbblks = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, nbblks); } ASSERT(nbblks > 0); ASSERT(BBTOB(nbblks) <= XFS_BUF_SIZE(bp)); XFS_BUF_SET_ADDR(bp, log->l_logBBstart + blk_no); XFS_BUF_ZEROFLAGS(bp); XFS_BUF_BUSY(bp); XFS_BUF_HOLD(bp); XFS_BUF_PSEMA(bp, PRIBIO); XFS_BUF_SET_COUNT(bp, BBTOB(nbblks)); XFS_BUF_SET_TARGET(bp, log->l_mp->m_logdev_targp); if ((error = xfs_bwrite(log->l_mp, bp))) xfs_ioerror_alert("xlog_bwrite", log->l_mp, bp, XFS_BUF_ADDR(bp)); return error;}STATIC xfs_caddr_txlog_align( xlog_t *log, xfs_daddr_t blk_no, int nbblks, xfs_buf_t *bp){ xfs_caddr_t ptr; if (!log->l_sectbb_log) return XFS_BUF_PTR(bp); ptr = XFS_BUF_PTR(bp) + BBTOB((int)blk_no & log->l_sectbb_mask); ASSERT(XFS_BUF_SIZE(bp) >= BBTOB(nbblks + (blk_no & log->l_sectbb_mask))); return ptr;}#ifdef DEBUG/* * dump debug superblock and log record information */STATIC voidxlog_header_check_dump( xfs_mount_t *mp, xlog_rec_header_t *head){ int b; cmn_err(CE_DEBUG, "%s: SB : uuid = ", __FUNCTION__); for (b = 0; b < 16; b++) cmn_err(CE_DEBUG, "%02x", ((uchar_t *)&mp->m_sb.sb_uuid)[b]); cmn_err(CE_DEBUG, ", fmt = %d\n", XLOG_FMT); cmn_err(CE_DEBUG, " log : uuid = "); for (b = 0; b < 16; b++) cmn_err(CE_DEBUG, "%02x",((uchar_t *)&head->h_fs_uuid)[b]); cmn_err(CE_DEBUG, ", fmt = %d\n", INT_GET(head->h_fmt, ARCH_CONVERT));}#else#define xlog_header_check_dump(mp, head)#endif/* * check log record header for recovery */STATIC intxlog_header_check_recover( xfs_mount_t *mp, xlog_rec_header_t *head){ ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); /* * IRIX doesn't write the h_fmt field and leaves it zeroed * (XLOG_FMT_UNKNOWN). This stops us from trying to recover * a dirty log created in IRIX. */ if (unlikely(INT_GET(head->h_fmt, ARCH_CONVERT) != XLOG_FMT)) { xlog_warn( "XFS: dirty log written in incompatible format - can't recover"); xlog_header_check_dump(mp, head); XFS_ERROR_REPORT("xlog_header_check_recover(1)", XFS_ERRLEVEL_HIGH, mp); return XFS_ERROR(EFSCORRUPTED); } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { xlog_warn( "XFS: dirty log entry has mismatched uuid - can't recover"); xlog_header_check_dump(mp, head); XFS_ERROR_REPORT("xlog_header_check_recover(2)", XFS_ERRLEVEL_HIGH, mp); return XFS_ERROR(EFSCORRUPTED); } return 0;}/* * read the head block of the log and check the header */STATIC intxlog_header_check_mount( xfs_mount_t *mp, xlog_rec_header_t *head){ ASSERT(INT_GET(head->h_magicno, ARCH_CONVERT) == XLOG_HEADER_MAGIC_NUM); if (uuid_is_nil(&head->h_fs_uuid)) { /* * IRIX doesn't write the h_fs_uuid or h_fmt fields. If * h_fs_uuid is nil, we assume this log was last mounted * by IRIX and continue. */ xlog_warn("XFS: nil uuid in log - IRIX style log"); } else if (unlikely(!uuid_equal(&mp->m_sb.sb_uuid, &head->h_fs_uuid))) { xlog_warn("XFS: log has mismatched uuid - can't recover"); xlog_header_check_dump(mp, head); XFS_ERROR_REPORT("xlog_header_check_mount", XFS_ERRLEVEL_HIGH, mp); return XFS_ERROR(EFSCORRUPTED); } return 0;}STATIC voidxlog_recover_iodone( struct xfs_buf *bp){ xfs_mount_t *mp; ASSERT(XFS_BUF_FSPRIVATE(bp, void *)); if (XFS_BUF_GETERROR(bp)) { /* * We're not going to bother about retrying * this during recovery. One strike! */ mp = XFS_BUF_FSPRIVATE(bp, xfs_mount_t *); xfs_ioerror_alert("xlog_recover_iodone", mp, bp, XFS_BUF_ADDR(bp)); xfs_force_shutdown(mp, SHUTDOWN_META_IO_ERROR); } XFS_BUF_SET_FSPRIVATE(bp, NULL); XFS_BUF_CLR_IODONE_FUNC(bp); xfs_biodone(bp);}/* * This routine finds (to an approximation) the first block in the physical * log which contains the given cycle. It uses a binary search algorithm. * Note that the algorithm can not be perfect because the disk will not * necessarily be perfect. */intxlog_find_cycle_start( xlog_t *log, xfs_buf_t *bp, xfs_daddr_t first_blk, xfs_daddr_t *last_blk, uint cycle){ xfs_caddr_t offset; xfs_daddr_t mid_blk; uint mid_cycle; int error; mid_blk = BLK_AVG(first_blk, *last_blk); while (mid_blk != first_blk && mid_blk != *last_blk) { if ((error = xlog_bread(log, mid_blk, 1, bp))) return error; offset = xlog_align(log, mid_blk, 1, bp); mid_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (mid_cycle == cycle) { *last_blk = mid_blk; /* last_half_cycle == mid_cycle */ } else { first_blk = mid_blk; /* first_half_cycle == mid_cycle */ } mid_blk = BLK_AVG(first_blk, *last_blk); } ASSERT((mid_blk == first_blk && mid_blk+1 == *last_blk) || (mid_blk == *last_blk && mid_blk-1 == first_blk)); return 0;}/* * Check that the range of blocks does not contain the cycle number * given. The scan needs to occur from front to back and the ptr into the * region must be updated since a later routine will need to perform another * test. If the region is completely good, we end up returning the same * last block number. * * Set blkno to -1 if we encounter no errors. This is an invalid block number * since we don't ever expect logs to get this large. */STATIC intxlog_find_verify_cycle( xlog_t *log, xfs_daddr_t start_blk, int nbblks, uint stop_on_cycle_no, xfs_daddr_t *new_blk){ xfs_daddr_t i, j; uint cycle; xfs_buf_t *bp; xfs_daddr_t bufblks; xfs_caddr_t buf = NULL; int error = 0; bufblks = 1 << ffs(nbblks); while (!(bp = xlog_get_bp(log, bufblks))) { /* can't get enough memory to do everything in one big buffer */ bufblks >>= 1; if (bufblks <= log->l_sectbb_log) return ENOMEM; } for (i = start_blk; i < start_blk + nbblks; i += bufblks) { int bcount; bcount = min(bufblks, (start_blk + nbblks - i)); if ((error = xlog_bread(log, i, bcount, bp))) goto out; buf = xlog_align(log, i, bcount, bp); for (j = 0; j < bcount; j++) { cycle = GET_CYCLE(buf, ARCH_CONVERT); if (cycle == stop_on_cycle_no) { *new_blk = i+j; goto out; } buf += BBSIZE; } } *new_blk = -1;out: xlog_put_bp(bp); return error;}/* * Potentially backup over partial log record write. * * In the typical case, last_blk is the number of the block directly after * a good log record. Therefore, we subtract one to get the block number * of the last block in the given buffer. extra_bblks contains the number * of blocks we would have read on a previous read. This happens when the * last log record is split over the end of the physical log. * * extra_bblks is the number of blocks potentially verified on a previous * call to this routine. */STATIC intxlog_find_verify_log_record( xlog_t *log, xfs_daddr_t start_blk, xfs_daddr_t *last_blk, int extra_bblks){ xfs_daddr_t i; xfs_buf_t *bp; xfs_caddr_t offset = NULL; xlog_rec_header_t *head = NULL; int error = 0; int smallmem = 0; int num_blks = *last_blk - start_blk; int xhdrs; ASSERT(start_blk != 0 || *last_blk != start_blk); if (!(bp = xlog_get_bp(log, num_blks))) { if (!(bp = xlog_get_bp(log, 1))) return ENOMEM; smallmem = 1; } else { if ((error = xlog_bread(log, start_blk, num_blks, bp))) goto out; offset = xlog_align(log, start_blk, num_blks, bp); offset += ((num_blks - 1) << BBSHIFT); } for (i = (*last_blk) - 1; i >= 0; i--) { if (i < start_blk) { /* valid log record not found */ xlog_warn( "XFS: Log inconsistent (didn't find previous header)"); ASSERT(0); error = XFS_ERROR(EIO); goto out; } if (smallmem) { if ((error = xlog_bread(log, i, 1, bp))) goto out; offset = xlog_align(log, i, 1, bp); } head = (xlog_rec_header_t *)offset; if (XLOG_HEADER_MAGIC_NUM == INT_GET(head->h_magicno, ARCH_CONVERT)) break; if (!smallmem) offset -= BBSIZE; } /* * We hit the beginning of the physical log & still no header. Return * to caller. If caller can handle a return of -1, then this routine * will be called again for the end of the physical log. */ if (i == -1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -