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

📄 xfs_log_recover.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
 * The next region to add is the start of a new region.  It could be * a whole region or it could be the first part of a new region.  Because * of this, the assumption here is that the type and size fields of all * format structures fit into the first 32 bits of the structure. * * This works because all regions must be 32 bit aligned.  Therefore, we * either have both fields or we have neither field.  In the case we have * neither field, the data part of the region is zero length.  We only have * a log_op_header and can throw away the header since a new one will appear * later.  If we have at least 4 bytes, then we can determine how many regions * will appear in the current log item. */STATIC intxlog_recover_add_to_trans(	xlog_recover_t		*trans,	xfs_caddr_t		dp,	int			len){	xfs_inode_log_format_t	*in_f;			/* any will do */	xlog_recover_item_t	*item;	xfs_caddr_t		ptr;	if (!len)		return 0;	item = trans->r_itemq;	if (item == NULL) {		ASSERT(*(uint *)dp == XFS_TRANS_HEADER_MAGIC);		if (len == sizeof(xfs_trans_header_t))			xlog_recover_add_item(&trans->r_itemq);		memcpy(&trans->r_theader, dp, len); /* d, s, l */		return 0;	}	ptr = kmem_alloc(len, KM_SLEEP);	memcpy(ptr, dp, len);	in_f = (xfs_inode_log_format_t *)ptr;	if (item->ri_prev->ri_total != 0 &&	     item->ri_prev->ri_total == item->ri_prev->ri_cnt) {		xlog_recover_add_item(&trans->r_itemq);	}	item = trans->r_itemq;	item = item->ri_prev;	if (item->ri_total == 0) {		/* first region to be added */		item->ri_total	= in_f->ilf_size;		ASSERT(item->ri_total <= XLOG_MAX_REGIONS_IN_ITEM);		item->ri_buf = kmem_zalloc((item->ri_total *					    sizeof(xfs_log_iovec_t)), KM_SLEEP);	}	ASSERT(item->ri_total > item->ri_cnt);	/* Description region is ri_buf[0] */	item->ri_buf[item->ri_cnt].i_addr = ptr;	item->ri_buf[item->ri_cnt].i_len  = len;	item->ri_cnt++;	return 0;}STATIC voidxlog_recover_new_tid(	xlog_recover_t		**q,	xlog_tid_t		tid,	xfs_lsn_t		lsn){	xlog_recover_t		*trans;	trans = kmem_zalloc(sizeof(xlog_recover_t), KM_SLEEP);	trans->r_log_tid   = tid;	trans->r_lsn	   = lsn;	xlog_recover_put_hashq(q, trans);}STATIC intxlog_recover_unlink_tid(	xlog_recover_t		**q,	xlog_recover_t		*trans){	xlog_recover_t		*tp;	int			found = 0;	ASSERT(trans != NULL);	if (trans == *q) {		*q = (*q)->r_next;	} else {		tp = *q;		while (tp) {			if (tp->r_next == trans) {				found = 1;				break;			}			tp = tp->r_next;		}		if (!found) {			xlog_warn(			     "XFS: xlog_recover_unlink_tid: trans not found");			ASSERT(0);			return XFS_ERROR(EIO);		}		tp->r_next = tp->r_next->r_next;	}	return 0;}STATIC voidxlog_recover_insert_item_backq(	xlog_recover_item_t	**q,	xlog_recover_item_t	*item){	if (*q == NULL) {		item->ri_prev = item->ri_next = item;		*q = item;	} else {		item->ri_next		= *q;		item->ri_prev		= (*q)->ri_prev;		(*q)->ri_prev		= item;		item->ri_prev->ri_next	= item;	}}STATIC voidxlog_recover_insert_item_frontq(	xlog_recover_item_t	**q,	xlog_recover_item_t	*item){	xlog_recover_insert_item_backq(q, item);	*q = item;}STATIC intxlog_recover_reorder_trans(	xlog_recover_t		*trans){	xlog_recover_item_t	*first_item, *itemq, *itemq_next;	xfs_buf_log_format_t	*buf_f;	ushort			flags = 0;	first_item = itemq = trans->r_itemq;	trans->r_itemq = NULL;	do {		itemq_next = itemq->ri_next;		buf_f = (xfs_buf_log_format_t *)itemq->ri_buf[0].i_addr;		switch (ITEM_TYPE(itemq)) {		case XFS_LI_BUF:			flags = buf_f->blf_flags;			if (!(flags & XFS_BLI_CANCEL)) {				xlog_recover_insert_item_frontq(&trans->r_itemq,								itemq);				break;			}		case XFS_LI_INODE:		case XFS_LI_DQUOT:		case XFS_LI_QUOTAOFF:		case XFS_LI_EFD:		case XFS_LI_EFI:			xlog_recover_insert_item_backq(&trans->r_itemq, itemq);			break;		default:			xlog_warn(	"XFS: xlog_recover_reorder_trans: unrecognized type of log operation");			ASSERT(0);			return XFS_ERROR(EIO);		}		itemq = itemq_next;	} while (first_item != itemq);	return 0;}/* * Build up the table of buf cancel records so that we don't replay * cancelled data in the second pass.  For buffer records that are * not cancel records, there is nothing to do here so we just return. * * If we get a cancel record which is already in the table, this indicates * that the buffer was cancelled multiple times.  In order to ensure * that during pass 2 we keep the record in the table until we reach its * last occurrence in the log, we keep a reference count in the cancel * record in the table to tell us how many times we expect to see this * record during the second pass. */STATIC voidxlog_recover_do_buffer_pass1(	xlog_t			*log,	xfs_buf_log_format_t	*buf_f){	xfs_buf_cancel_t	*bcp;	xfs_buf_cancel_t	*nextp;	xfs_buf_cancel_t	*prevp;	xfs_buf_cancel_t	**bucket;	xfs_daddr_t		blkno = 0;	uint			len = 0;	ushort			flags = 0;	switch (buf_f->blf_type) {	case XFS_LI_BUF:		blkno = buf_f->blf_blkno;		len = buf_f->blf_len;		flags = buf_f->blf_flags;		break;	}	/*	 * If this isn't a cancel buffer item, then just return.	 */	if (!(flags & XFS_BLI_CANCEL))		return;	/*	 * Insert an xfs_buf_cancel record into the hash table of	 * them.  If there is already an identical record, bump	 * its reference count.	 */	bucket = &log->l_buf_cancel_table[(__uint64_t)blkno %					  XLOG_BC_TABLE_SIZE];	/*	 * If the hash bucket is empty then just insert a new record into	 * the bucket.	 */	if (*bucket == NULL) {		bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t),						     KM_SLEEP);		bcp->bc_blkno = blkno;		bcp->bc_len = len;		bcp->bc_refcount = 1;		bcp->bc_next = NULL;		*bucket = bcp;		return;	}	/*	 * The hash bucket is not empty, so search for duplicates of our	 * record.  If we find one them just bump its refcount.  If not	 * then add us at the end of the list.	 */	prevp = NULL;	nextp = *bucket;	while (nextp != NULL) {		if (nextp->bc_blkno == blkno && nextp->bc_len == len) {			nextp->bc_refcount++;			return;		}		prevp = nextp;		nextp = nextp->bc_next;	}	ASSERT(prevp != NULL);	bcp = (xfs_buf_cancel_t *)kmem_alloc(sizeof(xfs_buf_cancel_t),					     KM_SLEEP);	bcp->bc_blkno = blkno;	bcp->bc_len = len;	bcp->bc_refcount = 1;	bcp->bc_next = NULL;	prevp->bc_next = bcp;}/* * Check to see whether the buffer being recovered has a corresponding * entry in the buffer cancel record table.  If it does then return 1 * so that it will be cancelled, otherwise return 0.  If the buffer is * actually a buffer cancel item (XFS_BLI_CANCEL is set), then decrement * the refcount on the entry in the table and remove it from the table * if this is the last reference. * * We remove the cancel record from the table when we encounter its * last occurrence in the log so that if the same buffer is re-used * again after its last cancellation we actually replay the changes * made at that point. */STATIC intxlog_check_buffer_cancelled(	xlog_t			*log,	xfs_daddr_t		blkno,	uint			len,	ushort			flags){	xfs_buf_cancel_t	*bcp;	xfs_buf_cancel_t	*prevp;	xfs_buf_cancel_t	**bucket;	if (log->l_buf_cancel_table == NULL) {		/*		 * There is nothing in the table built in pass one,		 * so this buffer must not be cancelled.		 */		ASSERT(!(flags & XFS_BLI_CANCEL));		return 0;	}	bucket = &log->l_buf_cancel_table[(__uint64_t)blkno %					  XLOG_BC_TABLE_SIZE];	bcp = *bucket;	if (bcp == NULL) {		/*		 * There is no corresponding entry in the table built		 * in pass one, so this buffer has not been cancelled.		 */		ASSERT(!(flags & XFS_BLI_CANCEL));		return 0;	}	/*	 * Search for an entry in the buffer cancel table that	 * matches our buffer.	 */	prevp = NULL;	while (bcp != NULL) {		if (bcp->bc_blkno == blkno && bcp->bc_len == len) {			/*			 * We've go a match, so return 1 so that the			 * recovery of this buffer is cancelled.			 * If this buffer is actually a buffer cancel			 * log item, then decrement the refcount on the			 * one in the table and remove it if this is the			 * last reference.			 */			if (flags & XFS_BLI_CANCEL) {				bcp->bc_refcount--;				if (bcp->bc_refcount == 0) {					if (prevp == NULL) {						*bucket = bcp->bc_next;					} else {						prevp->bc_next = bcp->bc_next;					}					kmem_free(bcp,						  sizeof(xfs_buf_cancel_t));				}			}			return 1;		}		prevp = bcp;		bcp = bcp->bc_next;	}	/*	 * We didn't find a corresponding entry in the table, so	 * return 0 so that the buffer is NOT cancelled.	 */	ASSERT(!(flags & XFS_BLI_CANCEL));	return 0;}STATIC intxlog_recover_do_buffer_pass2(	xlog_t			*log,	xfs_buf_log_format_t	*buf_f){	xfs_daddr_t		blkno = 0;	ushort			flags = 0;	uint			len = 0;	switch (buf_f->blf_type) {	case XFS_LI_BUF:		blkno = buf_f->blf_blkno;		flags = buf_f->blf_flags;		len = buf_f->blf_len;		break;	}	return xlog_check_buffer_cancelled(log, blkno, len, flags);}/* * Perform recovery for a buffer full of inodes.  In these buffers, * the only data which should be recovered is that which corresponds * to the di_next_unlinked pointers in the on disk inode structures. * The rest of the data for the inodes is always logged through the * inodes themselves rather than the inode buffer and is recovered * in xlog_recover_do_inode_trans(). * * The only time when buffers full of inodes are fully recovered is * when the buffer is full of newly allocated inodes.  In this case * the buffer will not be marked as an inode buffer and so will be * sent to xlog_recover_do_reg_buffer() below during recovery. */STATIC intxlog_recover_do_inode_buffer(	xfs_mount_t		*mp,	xlog_recover_item_t	*item,	xfs_buf_t		*bp,	xfs_buf_log_format_t	*buf_f){	int			i;	int			item_index;	int			bit;	int			nbits;	int			reg_buf_offset;	int			reg_buf_bytes;	int			next_unlinked_offset;	int			inodes_per_buf;	xfs_agino_t		*logged_nextp;	xfs_agino_t		*buffer_nextp;	unsigned int		*data_map = NULL;	unsigned int		map_size = 0;	switch (buf_f->blf_type) {	case XFS_LI_BUF:		data_map = buf_f->blf_data_map;		map_size = buf_f->blf_map_size;		break;	}	/*	 * Set the variables corresponding to the current region to	 * 0 so that we'll initialize them on the first pass through	 * the loop.	 */	reg_buf_offset = 0;	reg_buf_bytes = 0;	bit = 0;	nbits = 0;	item_index = 0;	inodes_per_buf = XFS_BUF_COUNT(bp) >> mp->m_sb.sb_inodelog;	for (i = 0; i < inodes_per_buf; i++) {		next_unlinked_offset = (i * mp->m_sb.sb_inodesize) +			offsetof(xfs_dinode_t, di_next_unlinked);		while (next_unlinked_offset >=		       (reg_buf_offset + reg_buf_bytes)) {			/*			 * The next di_next_unlinked field is beyond			 * the current logged region.  Find the next			 * logged region that contains or is beyond			 * the current di_next_unlinked field.			 */			bit += nbits;			bit = xfs_next_bit(data_map, map_size, bit);			/*			 * If there are no more logged regions in the			 * buffer, then we're done.			 */			if (bit == -1) {				return 0;			}			nbits = xfs_contig_bits(data_map, map_size,							 bit);			ASSERT(nbits > 0);			reg_buf_offset = bit << XFS_BLI_SHIFT;			reg_buf_bytes = nbits << XFS_BLI_SHIFT;			item_index++;		}		/*		 * If the current logged region starts after the current		 * di_next_unlinked field, then move on to the next		 * di_next_unlinked field.		 */		if (next_unlinked_offset < reg_buf_offset) {			continue;		}		ASSERT(item->ri_buf[item_index].i_addr != NULL);		ASSERT((item->ri_buf[item_index].i_len % XFS_BLI_CHUNK) == 0);		ASSERT((reg_buf_offset + reg_buf_bytes) <= XFS_BUF_COUNT(bp));		/*		 * The current logged region contains a copy of the		 * current di_next_unlinked field.  Extract its value		 * and copy it to the buffer copy.		 */		logged_nextp = (xfs_agino_t *)			       ((char *)(item->ri_buf[item_index].i_addr) +				(next_unlinked_offset - reg_buf_offset));		if (unlikely(*logged_nextp == 0)) {			xfs_fs_cmn_err(CE_ALERT, mp,

⌨️ 快捷键说明

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