📄 xfs_log_recover.c
字号:
* 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 + -