📄 xfs_log_recover.c
字号:
ASSIGN_ANY_LSN_HOST(log->l_last_sync_lsn, log->l_curr_cycle, after_umount_blk); *tail_blk = after_umount_blk; /* * Note that the unmount was clean. If the unmount * was not clean, we need to know this to rebuild the * superblock counters from the perag headers if we * have a filesystem using non-persistent counters. */ log->l_mp->m_flags |= XFS_MOUNT_WAS_CLEAN; } } /* * Make sure that there are no blocks in front of the head * with the same cycle number as the head. This can happen * because we allow multiple outstanding log writes concurrently, * and the later writes might make it out before earlier ones. * * We use the lsn from before modifying it so that we'll never * overwrite the unmount record after a clean unmount. * * Do this only if we are going to recover the filesystem * * NOTE: This used to say "if (!readonly)" * However on Linux, we can & do recover a read-only filesystem. * We only skip recovery if NORECOVERY is specified on mount, * in which case we would not be here. * * But... if the -device- itself is readonly, just skip this. * We can't recover this device anyway, so it won't matter. */ if (!xfs_readonly_buftarg(log->l_mp->m_logdev_targp)) { error = xlog_clear_stale_blocks(log, tail_lsn); }bread_err:exit: xlog_put_bp(bp); if (error) xlog_warn("XFS: failed to locate log tail"); return error;}/* * Is the log zeroed at all? * * The last binary search should be changed to perform an X block read * once X becomes small enough. You can then search linearly through * the X blocks. This will cut down on the number of reads we need to do. * * If the log is partially zeroed, this routine will pass back the blkno * of the first block with cycle number 0. It won't have a complete LR * preceding it. * * Return: * 0 => the log is completely written to * -1 => use *blk_no as the first block of the log * >0 => error has occurred */intxlog_find_zeroed( xlog_t *log, xfs_daddr_t *blk_no){ xfs_buf_t *bp; xfs_caddr_t offset; uint first_cycle, last_cycle; xfs_daddr_t new_blk, last_blk, start_blk; xfs_daddr_t num_scan_bblks; int error, log_bbnum = log->l_logBBsize; *blk_no = 0; /* check totally zeroed log */ bp = xlog_get_bp(log, 1); if (!bp) return ENOMEM; if ((error = xlog_bread(log, 0, 1, bp))) goto bp_err; offset = xlog_align(log, 0, 1, bp); first_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (first_cycle == 0) { /* completely zeroed log */ *blk_no = 0; xlog_put_bp(bp); return -1; } /* check partially zeroed log */ if ((error = xlog_bread(log, log_bbnum-1, 1, bp))) goto bp_err; offset = xlog_align(log, log_bbnum-1, 1, bp); last_cycle = GET_CYCLE(offset, ARCH_CONVERT); if (last_cycle != 0) { /* log completely written to */ xlog_put_bp(bp); return 0; } else if (first_cycle != 1) { /* * If the cycle of the last block is zero, the cycle of * the first block must be 1. If it's not, maybe we're * not looking at a log... Bail out. */ xlog_warn("XFS: Log inconsistent or not a log (last==0, first!=1)"); return XFS_ERROR(EINVAL); } /* we have a partially zeroed log */ last_blk = log_bbnum-1; if ((error = xlog_find_cycle_start(log, bp, 0, &last_blk, 0))) goto bp_err; /* * Validate the answer. Because there is no way to guarantee that * the entire log is made up of log records which are the same size, * we scan over the defined maximum blocks. At this point, the maximum * is not chosen to mean anything special. XXXmiken */ num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log); ASSERT(num_scan_bblks <= INT_MAX); if (last_blk < num_scan_bblks) num_scan_bblks = last_blk; start_blk = last_blk - num_scan_bblks; /* * We search for any instances of cycle number 0 that occur before * our current estimate of the head. What we're trying to detect is * 1 ... | 0 | 1 | 0... * ^ binary search ends here */ if ((error = xlog_find_verify_cycle(log, start_blk, (int)num_scan_bblks, 0, &new_blk))) goto bp_err; if (new_blk != -1) last_blk = new_blk; /* * Potentially backup over partial log record write. We don't need * to search the end of the log because we know it is zero. */ if ((error = xlog_find_verify_log_record(log, start_blk, &last_blk, 0)) == -1) { error = XFS_ERROR(EIO); goto bp_err; } else if (error) goto bp_err; *blk_no = last_blk;bp_err: xlog_put_bp(bp); if (error) return error; return -1;}/* * These are simple subroutines used by xlog_clear_stale_blocks() below * to initialize a buffer full of empty log record headers and write * them into the log. */STATIC voidxlog_add_record( xlog_t *log, xfs_caddr_t buf, int cycle, int block, int tail_cycle, int tail_block){ xlog_rec_header_t *recp = (xlog_rec_header_t *)buf; memset(buf, 0, BBSIZE); INT_SET(recp->h_magicno, ARCH_CONVERT, XLOG_HEADER_MAGIC_NUM); INT_SET(recp->h_cycle, ARCH_CONVERT, cycle); INT_SET(recp->h_version, ARCH_CONVERT, XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb) ? 2 : 1); ASSIGN_ANY_LSN_DISK(recp->h_lsn, cycle, block); ASSIGN_ANY_LSN_DISK(recp->h_tail_lsn, tail_cycle, tail_block); INT_SET(recp->h_fmt, ARCH_CONVERT, XLOG_FMT); memcpy(&recp->h_fs_uuid, &log->l_mp->m_sb.sb_uuid, sizeof(uuid_t));}STATIC intxlog_write_log_records( xlog_t *log, int cycle, int start_block, int blocks, int tail_cycle, int tail_block){ xfs_caddr_t offset; xfs_buf_t *bp; int balign, ealign; int sectbb = XLOG_SECTOR_ROUNDUP_BBCOUNT(log, 1); int end_block = start_block + blocks; int bufblks; int error = 0; int i, j = 0; bufblks = 1 << ffs(blocks); while (!(bp = xlog_get_bp(log, bufblks))) { bufblks >>= 1; if (bufblks <= log->l_sectbb_log) return ENOMEM; } /* We may need to do a read at the start to fill in part of * the buffer in the starting sector not covered by the first * write below. */ balign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, start_block); if (balign != start_block) { if ((error = xlog_bread(log, start_block, 1, bp))) { xlog_put_bp(bp); return error; } j = start_block - balign; } for (i = start_block; i < end_block; i += bufblks) { int bcount, endcount; bcount = min(bufblks, end_block - start_block); endcount = bcount - j; /* We may need to do a read at the end to fill in part of * the buffer in the final sector not covered by the write. * If this is the same sector as the above read, skip it. */ ealign = XLOG_SECTOR_ROUNDDOWN_BLKNO(log, end_block); if (j == 0 && (start_block + endcount > ealign)) { offset = XFS_BUF_PTR(bp); balign = BBTOB(ealign - start_block); XFS_BUF_SET_PTR(bp, offset + balign, BBTOB(sectbb)); if ((error = xlog_bread(log, ealign, sectbb, bp))) break; XFS_BUF_SET_PTR(bp, offset, bufblks); } offset = xlog_align(log, start_block, endcount, bp); for (; j < endcount; j++) { xlog_add_record(log, offset, cycle, i+j, tail_cycle, tail_block); offset += BBSIZE; } error = xlog_bwrite(log, start_block, endcount, bp); if (error) break; start_block += endcount; j = 0; } xlog_put_bp(bp); return error;}/* * This routine is called to blow away any incomplete log writes out * in front of the log head. We do this so that we won't become confused * if we come up, write only a little bit more, and then crash again. * If we leave the partial log records out there, this situation could * cause us to think those partial writes are valid blocks since they * have the current cycle number. We get rid of them by overwriting them * with empty log records with the old cycle number rather than the * current one. * * The tail lsn is passed in rather than taken from * the log so that we will not write over the unmount record after a * clean unmount in a 512 block log. Doing so would leave the log without * any valid log records in it until a new one was written. If we crashed * during that time we would not be able to recover. */STATIC intxlog_clear_stale_blocks( xlog_t *log, xfs_lsn_t tail_lsn){ int tail_cycle, head_cycle; int tail_block, head_block; int tail_distance, max_distance; int distance; int error; tail_cycle = CYCLE_LSN(tail_lsn); tail_block = BLOCK_LSN(tail_lsn); head_cycle = log->l_curr_cycle; head_block = log->l_curr_block; /* * Figure out the distance between the new head of the log * and the tail. We want to write over any blocks beyond the * head that we may have written just before the crash, but * we don't want to overwrite the tail of the log. */ if (head_cycle == tail_cycle) { /* * The tail is behind the head in the physical log, * so the distance from the head to the tail is the * distance from the head to the end of the log plus * the distance from the beginning of the log to the * tail. */ if (unlikely(head_block < tail_block || head_block >= log->l_logBBsize)) { XFS_ERROR_REPORT("xlog_clear_stale_blocks(1)", XFS_ERRLEVEL_LOW, log->l_mp); return XFS_ERROR(EFSCORRUPTED); } tail_distance = tail_block + (log->l_logBBsize - head_block); } else { /* * The head is behind the tail in the physical log, * so the distance from the head to the tail is just * the tail block minus the head block. */ if (unlikely(head_block >= tail_block || head_cycle != (tail_cycle + 1))){ XFS_ERROR_REPORT("xlog_clear_stale_blocks(2)", XFS_ERRLEVEL_LOW, log->l_mp); return XFS_ERROR(EFSCORRUPTED); } tail_distance = tail_block - head_block; } /* * If the head is right up against the tail, we can't clear * anything. */ if (tail_distance <= 0) { ASSERT(tail_distance == 0); return 0; } max_distance = XLOG_TOTAL_REC_SHIFT(log); /* * Take the smaller of the maximum amount of outstanding I/O * we could have and the distance to the tail to clear out. * We take the smaller so that we don't overwrite the tail and * we don't waste all day writing from the head to the tail * for no reason. */ max_distance = MIN(max_distance, tail_distance); if ((head_block + max_distance) <= log->l_logBBsize) { /* * We can stomp all the blocks we need to without * wrapping around the end of the log. Just do it * in a single write. Use the cycle number of the * current cycle minus one so that the log will look like: * n ... | n - 1 ... */ error = xlog_write_log_records(log, (head_cycle - 1), head_block, max_distance, tail_cycle, tail_block); if (error) return error; } else { /* * We need to wrap around the end of the physical log in * order to clear all the blocks. Do it in two separate * I/Os. The first write should be from the head to the * end of the physical log, and it should use the current * cycle number minus one just like above. */ distance = log->l_logBBsize - head_block; error = xlog_write_log_records(log, (head_cycle - 1), head_block, distance, tail_cycle, tail_block); if (error) return error; /* * Now write the blocks at the start of the physical log. * This writes the remainder of the blocks we want to clear. * It uses the current cycle number since we're now on the * same cycle as the head so that we get: * n ... n ... | n - 1 ... * ^^^^^ blocks we're writing */ distance = max_distance - (log->l_logBBsize - head_block); error = xlog_write_log_records(log, head_cycle, 0, distance, tail_cycle, tail_block); if (error) return error; } return 0;}/****************************************************************************** * * Log recover routines * ****************************************************************************** */STATIC xlog_recover_t *xlog_recover_find_tid( xlog_recover_t *q, xlog_tid_t tid){ xlog_recover_t *p = q; while (p != NULL) { if (p->r_log_tid == tid) break; p = p->r_next; } return p;}STATIC voidxlog_recover_put_hashq( xlog_recover_t **q, xlog_recover_t *trans){ trans->r_next = *q; *q = trans;}STATIC voidxlog_recover_add_item( xlog_recover_item_t **itemq){ xlog_recover_item_t *item; item = kmem_zalloc(sizeof(xlog_recover_item_t), KM_SLEEP); xlog_recover_insert_item_backq(itemq, item);}STATIC intxlog_recover_add_to_cont_trans( xlog_recover_t *trans, xfs_caddr_t dp, int len){ xlog_recover_item_t *item; xfs_caddr_t ptr, old_ptr; int old_len; item = trans->r_itemq; if (item == NULL) { /* finish copying rest of trans header */ xlog_recover_add_item(&trans->r_itemq); ptr = (xfs_caddr_t) &trans->r_theader + sizeof(xfs_trans_header_t) - len; memcpy(ptr, dp, len); /* d, s, l */ return 0; } item = item->ri_prev; old_ptr = item->ri_buf[item->ri_cnt-1].i_addr; old_len = item->ri_buf[item->ri_cnt-1].i_len; ptr = kmem_realloc(old_ptr, len+old_len, old_len, 0u); memcpy(&ptr[old_len], dp, len); /* d, s, l */ item->ri_buf[item->ri_cnt-1].i_len += len; item->ri_buf[item->ri_cnt-1].i_addr = ptr; return 0;}/*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -