⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 xfs_log_recover.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
		error = -1;		goto out;	}	/*	 * We have the final block of the good log (the first block	 * of the log record _before_ the head. So we check the uuid.	 */	if ((error = xlog_header_check_mount(log->l_mp, head)))		goto out;	/*	 * We may have found a log record header before we expected one.	 * last_blk will be the 1st block # with a given cycle #.  We may end	 * up reading an entire log record.  In this case, we don't want to	 * reset last_blk.  Only when last_blk points in the middle of a log	 * record do we update last_blk.	 */	if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {		uint	h_size = INT_GET(head->h_size, ARCH_CONVERT);		xhdrs = h_size / XLOG_HEADER_CYCLE_SIZE;		if (h_size % XLOG_HEADER_CYCLE_SIZE)			xhdrs++;	} else {		xhdrs = 1;	}	if (*last_blk - i + extra_bblks			!= BTOBB(INT_GET(head->h_len, ARCH_CONVERT)) + xhdrs)		*last_blk = i;out:	xlog_put_bp(bp);	return error;}/* * Head is defined to be the point of the log where the next log write * write could go.  This means that incomplete LR writes at the end are * eliminated when calculating the head.  We aren't guaranteed that previous * LR have complete transactions.  We only know that a cycle number of * current cycle number -1 won't be present in the log if we start writing * from our current block number. * * last_blk contains the block number of the first block with a given * cycle number. * * Return: zero if normal, non-zero if error. */STATIC intxlog_find_head(	xlog_t 		*log,	xfs_daddr_t	*return_head_blk){	xfs_buf_t	*bp;	xfs_caddr_t	offset;	xfs_daddr_t	new_blk, first_blk, start_blk, last_blk, head_blk;	int		num_scan_bblks;	uint		first_half_cycle, last_half_cycle;	uint		stop_on_cycle;	int		error, log_bbnum = log->l_logBBsize;	/* Is the end of the log device zeroed? */	if ((error = xlog_find_zeroed(log, &first_blk)) == -1) {		*return_head_blk = first_blk;		/* Is the whole lot zeroed? */		if (!first_blk) {			/* Linux XFS shouldn't generate totally zeroed logs -			 * mkfs etc write a dummy unmount record to a fresh			 * log so we can store the uuid in there			 */			xlog_warn("XFS: totally zeroed log");		}		return 0;	} else if (error) {		xlog_warn("XFS: empty log check failed");		return error;	}	first_blk = 0;			/* get cycle # of 1st block */	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_half_cycle = GET_CYCLE(offset, ARCH_CONVERT);	last_blk = head_blk = log_bbnum - 1;	/* get cycle # of last block */	if ((error = xlog_bread(log, last_blk, 1, bp)))		goto bp_err;	offset = xlog_align(log, last_blk, 1, bp);	last_half_cycle = GET_CYCLE(offset, ARCH_CONVERT);	ASSERT(last_half_cycle != 0);	/*	 * If the 1st half cycle number is equal to the last half cycle number,	 * then the entire log is stamped with the same cycle number.  In this	 * case, head_blk can't be set to zero (which makes sense).  The below	 * math doesn't work out properly with head_blk equal to zero.  Instead,	 * we set it to log_bbnum which is an invalid block number, but this	 * value makes the math correct.  If head_blk doesn't changed through	 * all the tests below, *head_blk is set to zero at the very end rather	 * than log_bbnum.  In a sense, log_bbnum and zero are the same block	 * in a circular file.	 */	if (first_half_cycle == last_half_cycle) {		/*		 * In this case we believe that the entire log should have		 * cycle number last_half_cycle.  We need to scan backwards		 * from the end verifying that there are no holes still		 * containing last_half_cycle - 1.  If we find such a hole,		 * then the start of that hole will be the new head.  The		 * simple case looks like		 *        x | x ... | x - 1 | x		 * Another case that fits this picture would be		 *        x | x + 1 | x ... | x		 * In this case the head really is somewhere at the end of the		 * log, as one of the latest writes at the beginning was		 * incomplete.		 * One more case is		 *        x | x + 1 | x ... | x - 1 | x		 * This is really the combination of the above two cases, and		 * the head has to end up at the start of the x-1 hole at the		 * end of the log.		 *		 * In the 256k log case, we will read from the beginning to the		 * end of the log and search for cycle numbers equal to x-1.		 * We don't worry about the x+1 blocks that we encounter,		 * because we know that they cannot be the head since the log		 * started with x.		 */		head_blk = log_bbnum;		stop_on_cycle = last_half_cycle - 1;	} else {		/*		 * In this case we want to find the first block with cycle		 * number matching last_half_cycle.  We expect the log to be		 * some variation on		 *        x + 1 ... | x ...		 * The first block with cycle number x (last_half_cycle) will		 * be where the new head belongs.  First we do a binary search		 * for the first occurrence of last_half_cycle.  The binary		 * search may not be totally accurate, so then we scan back		 * from there looking for occurrences of last_half_cycle before		 * us.  If that backwards scan wraps around the beginning of		 * the log, then we look for occurrences of last_half_cycle - 1		 * at the end of the log.  The cases we're looking for look		 * like		 *        x + 1 ... | x | x + 1 | x ...		 *                               ^ binary search stopped here		 * or		 *        x + 1 ... | x ... | x - 1 | x		 *        <---------> less than scan distance		 */		stop_on_cycle = last_half_cycle;		if ((error = xlog_find_cycle_start(log, bp, first_blk,						&head_blk, last_half_cycle)))			goto bp_err;	}	/*	 * Now validate the answer.  Scan back some number of maximum possible	 * blocks and make sure each one has the expected cycle number.  The	 * maximum is determined by the total possible amount of buffering	 * in the in-core log.  The following number can be made tighter if	 * we actually look at the block size of the filesystem.	 */	num_scan_bblks = XLOG_TOTAL_REC_SHIFT(log);	if (head_blk >= num_scan_bblks) {		/*		 * We are guaranteed that the entire check can be performed		 * in one buffer.		 */		start_blk = head_blk - num_scan_bblks;		if ((error = xlog_find_verify_cycle(log,						start_blk, num_scan_bblks,						stop_on_cycle, &new_blk)))			goto bp_err;		if (new_blk != -1)			head_blk = new_blk;	} else {		/* need to read 2 parts of log */		/*		 * We are going to scan backwards in the log in two parts.		 * First we scan the physical end of the log.  In this part		 * of the log, we are looking for blocks with cycle number		 * last_half_cycle - 1.		 * If we find one, then we know that the log starts there, as		 * we've found a hole that didn't get written in going around		 * the end of the physical log.  The simple case for this is		 *        x + 1 ... | x ... | x - 1 | x		 *        <---------> less than scan distance		 * If all of the blocks at the end of the log have cycle number		 * last_half_cycle, then we check the blocks at the start of		 * the log looking for occurrences of last_half_cycle.  If we		 * find one, then our current estimate for the location of the		 * first occurrence of last_half_cycle is wrong and we move		 * back to the hole we've found.  This case looks like		 *        x + 1 ... | x | x + 1 | x ...		 *                               ^ binary search stopped here		 * Another case we need to handle that only occurs in 256k		 * logs is		 *        x + 1 ... | x ... | x+1 | x ...		 *                   ^ binary search stops here		 * In a 256k log, the scan at the end of the log will see the		 * x + 1 blocks.  We need to skip past those since that is		 * certainly not the head of the log.  By searching for		 * last_half_cycle-1 we accomplish that.		 */		start_blk = log_bbnum - num_scan_bblks + head_blk;		ASSERT(head_blk <= INT_MAX &&			(xfs_daddr_t) num_scan_bblks - head_blk >= 0);		if ((error = xlog_find_verify_cycle(log, start_blk,					num_scan_bblks - (int)head_blk,					(stop_on_cycle - 1), &new_blk)))			goto bp_err;		if (new_blk != -1) {			head_blk = new_blk;			goto bad_blk;		}		/*		 * Scan beginning of log now.  The last part of the physical		 * log is good.  This scan needs to verify that it doesn't find		 * the last_half_cycle.		 */		start_blk = 0;		ASSERT(head_blk <= INT_MAX);		if ((error = xlog_find_verify_cycle(log,					start_blk, (int)head_blk,					stop_on_cycle, &new_blk)))			goto bp_err;		if (new_blk != -1)			head_blk = new_blk;	} bad_blk:	/*	 * Now we need to make sure head_blk is not pointing to a block in	 * the middle of a log record.	 */	num_scan_bblks = XLOG_REC_SHIFT(log);	if (head_blk >= num_scan_bblks) {		start_blk = head_blk - num_scan_bblks; /* don't read head_blk */		/* start ptr at last block ptr before head_blk */		if ((error = xlog_find_verify_log_record(log, start_blk,							&head_blk, 0)) == -1) {			error = XFS_ERROR(EIO);			goto bp_err;		} else if (error)			goto bp_err;	} else {		start_blk = 0;		ASSERT(head_blk <= INT_MAX);		if ((error = xlog_find_verify_log_record(log, start_blk,							&head_blk, 0)) == -1) {			/* We hit the beginning of the log during our search */			start_blk = log_bbnum - num_scan_bblks + head_blk;			new_blk = log_bbnum;			ASSERT(start_blk <= INT_MAX &&				(xfs_daddr_t) log_bbnum-start_blk >= 0);			ASSERT(head_blk <= INT_MAX);			if ((error = xlog_find_verify_log_record(log,							start_blk, &new_blk,							(int)head_blk)) == -1) {				error = XFS_ERROR(EIO);				goto bp_err;			} else if (error)				goto bp_err;			if (new_blk != log_bbnum)				head_blk = new_blk;		} else if (error)			goto bp_err;	}	xlog_put_bp(bp);	if (head_blk == log_bbnum)		*return_head_blk = 0;	else		*return_head_blk = head_blk;	/*	 * When returning here, we have a good block number.  Bad block	 * means that during a previous crash, we didn't have a clean break	 * from cycle number N to cycle number N-1.  In this case, we need	 * to find the first block with cycle number N-1.	 */	return 0; bp_err:	xlog_put_bp(bp);	if (error)	    xlog_warn("XFS: failed to find log head");	return error;}/* * Find the sync block number or the tail of the log. * * This will be the block number of the last record to have its * associated buffers synced to disk.  Every log record header has * a sync lsn embedded in it.  LSNs hold block numbers, so it is easy * to get a sync block number.  The only concern is to figure out which * log record header to believe. * * The following algorithm uses the log record header with the largest * lsn.  The entire log record does not need to be valid.  We only care * that the header is valid. * * We could speed up search by using current head_blk buffer, but it is not * available. */intxlog_find_tail(	xlog_t			*log,	xfs_daddr_t		*head_blk,	xfs_daddr_t		*tail_blk){	xlog_rec_header_t	*rhead;	xlog_op_header_t	*op_head;	xfs_caddr_t		offset = NULL;	xfs_buf_t		*bp;	int			error, i, found;	xfs_daddr_t		umount_data_blk;	xfs_daddr_t		after_umount_blk;	xfs_lsn_t		tail_lsn;	int			hblks;	found = 0;	/*	 * Find previous log record	 */	if ((error = xlog_find_head(log, head_blk)))		return error;	bp = xlog_get_bp(log, 1);	if (!bp)		return ENOMEM;	if (*head_blk == 0) {				/* special case */		if ((error = xlog_bread(log, 0, 1, bp)))			goto bread_err;		offset = xlog_align(log, 0, 1, bp);		if (GET_CYCLE(offset, ARCH_CONVERT) == 0) {			*tail_blk = 0;			/* leave all other log inited values alone */			goto exit;		}	}	/*	 * Search backwards looking for log record header block	 */	ASSERT(*head_blk < INT_MAX);	for (i = (int)(*head_blk) - 1; i >= 0; i--) {		if ((error = xlog_bread(log, i, 1, bp)))			goto bread_err;		offset = xlog_align(log, i, 1, bp);		if (XLOG_HEADER_MAGIC_NUM ==		    INT_GET(*(uint *)offset, ARCH_CONVERT)) {			found = 1;			break;		}	}	/*	 * If we haven't found the log record header block, start looking	 * again from the end of the physical log.  XXXmiken: There should be	 * a check here to make sure we didn't search more than N blocks in	 * the previous code.	 */	if (!found) {		for (i = log->l_logBBsize - 1; i >= (int)(*head_blk); i--) {			if ((error = xlog_bread(log, i, 1, bp)))				goto bread_err;			offset = xlog_align(log, i, 1, bp);			if (XLOG_HEADER_MAGIC_NUM ==			    INT_GET(*(uint*)offset, ARCH_CONVERT)) {				found = 2;				break;			}		}	}	if (!found) {		xlog_warn("XFS: xlog_find_tail: couldn't find sync record");		ASSERT(0);		return XFS_ERROR(EIO);	}	/* find blk_no of tail of log */	rhead = (xlog_rec_header_t *)offset;	*tail_blk = BLOCK_LSN(INT_GET(rhead->h_tail_lsn, ARCH_CONVERT));	/*	 * Reset log values according to the state of the log when we	 * crashed.  In the case where head_blk == 0, we bump curr_cycle	 * one because the next write starts a new cycle rather than	 * continuing the cycle of the last good log record.  At this	 * point we have guaranteed that all partial log records have been	 * accounted for.  Therefore, we know that the last good log record	 * written was complete and ended exactly on the end boundary	 * of the physical log.	 */	log->l_prev_block = i;	log->l_curr_block = (int)*head_blk;	log->l_curr_cycle = INT_GET(rhead->h_cycle, ARCH_CONVERT);	if (found == 2)		log->l_curr_cycle++;	log->l_tail_lsn = INT_GET(rhead->h_tail_lsn, ARCH_CONVERT);	log->l_last_sync_lsn = INT_GET(rhead->h_lsn, ARCH_CONVERT);	log->l_grant_reserve_cycle = log->l_curr_cycle;	log->l_grant_reserve_bytes = BBTOB(log->l_curr_block);	log->l_grant_write_cycle = log->l_curr_cycle;	log->l_grant_write_bytes = BBTOB(log->l_curr_block);	/*	 * Look for unmount record.  If we find it, then we know there	 * was a clean unmount.  Since 'i' could be the last block in	 * the physical log, we convert to a log block before comparing	 * to the head_blk.	 *	 * Save the current tail lsn to use to pass to	 * xlog_clear_stale_blocks() below.  We won't want to clear the	 * unmount record if there is one, so we pass the lsn of the	 * unmount record rather than the block after it.	 */	if (XFS_SB_VERSION_HASLOGV2(&log->l_mp->m_sb)) {		int	h_size = INT_GET(rhead->h_size, ARCH_CONVERT);		int	h_version = INT_GET(rhead->h_version, ARCH_CONVERT);		if ((h_version & XLOG_VERSION_2) &&		    (h_size > XLOG_HEADER_CYCLE_SIZE)) {			hblks = h_size / XLOG_HEADER_CYCLE_SIZE;			if (h_size % XLOG_HEADER_CYCLE_SIZE)				hblks++;		} else {			hblks = 1;		}	} else {		hblks = 1;	}	after_umount_blk = (i + hblks + (int)		BTOBB(INT_GET(rhead->h_len, ARCH_CONVERT))) % log->l_logBBsize;	tail_lsn = log->l_tail_lsn;	if (*head_blk == after_umount_blk &&	    INT_GET(rhead->h_num_logops, ARCH_CONVERT) == 1) {		umount_data_blk = (i + hblks) % log->l_logBBsize;		if ((error = xlog_bread(log, umount_data_blk, 1, bp))) {			goto bread_err;		}		offset = xlog_align(log, umount_data_blk, 1, bp);		op_head = (xlog_op_header_t *)offset;		if (op_head->oh_flags & XLOG_UNMOUNT_TRANS) {			/*			 * Set tail and last sync so that newly written			 * log records will point recovery to after the			 * current unmount record.			 */			ASSIGN_ANY_LSN_HOST(log->l_tail_lsn, log->l_curr_cycle,					after_umount_blk);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -