📄 xfs_log.c
字号:
int partial_copy; /* did we split a region? */ int partial_copy_len;/* # bytes copied if split region */ int need_copy; /* # bytes need to memcpy this region */ int copy_len; /* # bytes actually memcpy'ing */ int copy_off; /* # bytes from entry start */ int contwr; /* continued write of in-core log? */ int error; int record_cnt = 0, data_cnt = 0; partial_copy_len = partial_copy = 0; /* Calculate potential maximum space. Each region gets its own * xlog_op_header_t and may need to be double word aligned. */ len = 0; if (ticket->t_flags & XLOG_TIC_INITED) { /* acct for start rec of xact */ len += sizeof(xlog_op_header_t); ticket->t_res_num_ophdrs++; } for (index = 0; index < nentries; index++) { len += sizeof(xlog_op_header_t); /* each region gets >= 1 */ ticket->t_res_num_ophdrs++; len += reg[index].i_len; xlog_tic_add_region(ticket, reg[index].i_len, reg[index].i_type); } contwr = *start_lsn = 0; if (ticket->t_curr_res < len) { xlog_print_tic_res(mp, ticket);#ifdef DEBUG xlog_panic( "xfs_log_write: reservation ran out. Need to up reservation");#else /* Customer configurable panic */ xfs_cmn_err(XFS_PTAG_LOGRES, CE_ALERT, mp, "xfs_log_write: reservation ran out. Need to up reservation"); /* If we did not panic, shutdown the filesystem */ xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);#endif } else ticket->t_curr_res -= len; for (index = 0; index < nentries; ) { if ((error = xlog_state_get_iclog_space(log, len, &iclog, ticket, &contwr, &log_offset))) return error; ASSERT(log_offset <= iclog->ic_size - 1); ptr = (__psint_t) ((char *)iclog->ic_datap+log_offset); /* start_lsn is the first lsn written to. That's all we need. */ if (! *start_lsn) *start_lsn = INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT); /* This loop writes out as many regions as can fit in the amount * of space which was allocated by xlog_state_get_iclog_space(). */ while (index < nentries) { ASSERT(reg[index].i_len % sizeof(__int32_t) == 0); ASSERT((__psint_t)ptr % sizeof(__int32_t) == 0); start_rec_copy = 0; /* If first write for transaction, insert start record. * We can't be trying to commit if we are inited. We can't * have any "partial_copy" if we are inited. */ if (ticket->t_flags & XLOG_TIC_INITED) { logop_head = (xlog_op_header_t *)ptr; INT_SET(logop_head->oh_tid, ARCH_CONVERT, ticket->t_tid); logop_head->oh_clientid = ticket->t_clientid; logop_head->oh_len = 0; logop_head->oh_flags = XLOG_START_TRANS; logop_head->oh_res2 = 0; ticket->t_flags &= ~XLOG_TIC_INITED; /* clear bit */ record_cnt++; start_rec_copy = sizeof(xlog_op_header_t); xlog_write_adv_cnt(ptr, len, log_offset, start_rec_copy); } /* Copy log operation header directly into data section */ logop_head = (xlog_op_header_t *)ptr; INT_SET(logop_head->oh_tid, ARCH_CONVERT, ticket->t_tid); logop_head->oh_clientid = ticket->t_clientid; logop_head->oh_res2 = 0; /* header copied directly */ xlog_write_adv_cnt(ptr, len, log_offset, sizeof(xlog_op_header_t)); /* are we copying a commit or unmount record? */ logop_head->oh_flags = flags; /* * We've seen logs corrupted with bad transaction client * ids. This makes sure that XFS doesn't generate them on. * Turn this into an EIO and shut down the filesystem. */ switch (logop_head->oh_clientid) { case XFS_TRANSACTION: case XFS_VOLUME: case XFS_LOG: break; default: xfs_fs_cmn_err(CE_WARN, mp, "Bad XFS transaction clientid 0x%x in ticket 0x%p", logop_head->oh_clientid, tic); return XFS_ERROR(EIO); } /* Partial write last time? => (partial_copy != 0) * need_copy is the amount we'd like to copy if everything could * fit in the current memcpy. */ need_copy = reg[index].i_len - partial_copy_len; copy_off = partial_copy_len; if (need_copy <= iclog->ic_size - log_offset) { /*complete write */ INT_SET(logop_head->oh_len, ARCH_CONVERT, copy_len = need_copy); if (partial_copy) logop_head->oh_flags|= (XLOG_END_TRANS|XLOG_WAS_CONT_TRANS); partial_copy_len = partial_copy = 0; } else { /* partial write */ copy_len = iclog->ic_size - log_offset; INT_SET(logop_head->oh_len, ARCH_CONVERT, copy_len); logop_head->oh_flags |= XLOG_CONTINUE_TRANS; if (partial_copy) logop_head->oh_flags |= XLOG_WAS_CONT_TRANS; partial_copy_len += copy_len; partial_copy++; len += sizeof(xlog_op_header_t); /* from splitting of region */ /* account for new log op header */ ticket->t_curr_res -= sizeof(xlog_op_header_t); ticket->t_res_num_ophdrs++; } xlog_verify_dest_ptr(log, ptr); /* copy region */ ASSERT(copy_len >= 0); memcpy((xfs_caddr_t)ptr, reg[index].i_addr + copy_off, copy_len); xlog_write_adv_cnt(ptr, len, log_offset, copy_len); /* make copy_len total bytes copied, including headers */ copy_len += start_rec_copy + sizeof(xlog_op_header_t); record_cnt++; data_cnt += contwr ? copy_len : 0; if (partial_copy) { /* copied partial region */ /* already marked WANT_SYNC by xlog_state_get_iclog_space */ xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); record_cnt = data_cnt = 0; if ((error = xlog_state_release_iclog(log, iclog))) return error; break; /* don't increment index */ } else { /* copied entire region */ index++; partial_copy_len = partial_copy = 0; if (iclog->ic_size - log_offset <= sizeof(xlog_op_header_t)) { xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); record_cnt = data_cnt = 0; xlog_state_want_sync(log, iclog); if (commit_iclog) { ASSERT(flags & XLOG_COMMIT_TRANS); *commit_iclog = iclog; } else if ((error = xlog_state_release_iclog(log, iclog))) return error; if (index == nentries) return 0; /* we are done */ else break; } } /* if (partial_copy) */ } /* while (index < nentries) */ } /* for (index = 0; index < nentries; ) */ ASSERT(len == 0); xlog_state_finish_copy(log, iclog, record_cnt, data_cnt); if (commit_iclog) { ASSERT(flags & XLOG_COMMIT_TRANS); *commit_iclog = iclog; return 0; } return xlog_state_release_iclog(log, iclog);} /* xlog_write *//***************************************************************************** * * State Machine functions * ***************************************************************************** *//* Clean iclogs starting from the head. This ordering must be * maintained, so an iclog doesn't become ACTIVE beyond one that * is SYNCING. This is also required to maintain the notion that we use * a counting semaphore to hold off would be writers to the log when every * iclog is trying to sync to disk. * * State Change: DIRTY -> ACTIVE */STATIC voidxlog_state_clean_log(xlog_t *log){ xlog_in_core_t *iclog; int changed = 0; iclog = log->l_iclog; do { if (iclog->ic_state == XLOG_STATE_DIRTY) { iclog->ic_state = XLOG_STATE_ACTIVE; iclog->ic_offset = 0; iclog->ic_callback = NULL; /* don't need to free */ /* * If the number of ops in this iclog indicate it just * contains the dummy transaction, we can * change state into IDLE (the second time around). * Otherwise we should change the state into * NEED a dummy. * We don't need to cover the dummy. */ if (!changed && (INT_GET(iclog->ic_header.h_num_logops, ARCH_CONVERT) == XLOG_COVER_OPS)) { changed = 1; } else { /* * We have two dirty iclogs so start over * This could also be num of ops indicates * this is not the dummy going out. */ changed = 2; } iclog->ic_header.h_num_logops = 0; memset(iclog->ic_header.h_cycle_data, 0, sizeof(iclog->ic_header.h_cycle_data)); iclog->ic_header.h_lsn = 0; } else if (iclog->ic_state == XLOG_STATE_ACTIVE) /* do nothing */; else break; /* stop cleaning */ iclog = iclog->ic_next; } while (iclog != log->l_iclog); /* log is locked when we are called */ /* * Change state for the dummy log recording. * We usually go to NEED. But we go to NEED2 if the changed indicates * we are done writing the dummy record. * If we are done with the second dummy recored (DONE2), then * we go to IDLE. */ if (changed) { switch (log->l_covered_state) { case XLOG_STATE_COVER_IDLE: case XLOG_STATE_COVER_NEED: case XLOG_STATE_COVER_NEED2: log->l_covered_state = XLOG_STATE_COVER_NEED; break; case XLOG_STATE_COVER_DONE: if (changed == 1) log->l_covered_state = XLOG_STATE_COVER_NEED2; else log->l_covered_state = XLOG_STATE_COVER_NEED; break; case XLOG_STATE_COVER_DONE2: if (changed == 1) log->l_covered_state = XLOG_STATE_COVER_IDLE; else log->l_covered_state = XLOG_STATE_COVER_NEED; break; default: ASSERT(0); } }} /* xlog_state_clean_log */STATIC xfs_lsn_txlog_get_lowest_lsn( xlog_t *log){ xlog_in_core_t *lsn_log; xfs_lsn_t lowest_lsn, lsn; lsn_log = log->l_iclog; lowest_lsn = 0; do { if (!(lsn_log->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_DIRTY))) { lsn = INT_GET(lsn_log->ic_header.h_lsn, ARCH_CONVERT); if ((lsn && !lowest_lsn) || (XFS_LSN_CMP(lsn, lowest_lsn) < 0)) { lowest_lsn = lsn; } } lsn_log = lsn_log->ic_next; } while (lsn_log != log->l_iclog); return lowest_lsn;}STATIC voidxlog_state_do_callback( xlog_t *log, int aborted, xlog_in_core_t *ciclog){ xlog_in_core_t *iclog; xlog_in_core_t *first_iclog; /* used to know when we've * processed all iclogs once */ xfs_log_callback_t *cb, *cb_next; int flushcnt = 0; xfs_lsn_t lowest_lsn; int ioerrors; /* counter: iclogs with errors */ int loopdidcallbacks; /* flag: inner loop did callbacks*/ int funcdidcallbacks; /* flag: function did callbacks */ int repeats; /* for issuing console warnings if * looping too many times */ SPLDECL(s); s = LOG_LOCK(log); first_iclog = iclog = log->l_iclog; ioerrors = 0; funcdidcallbacks = 0; repeats = 0; do { /* * Scan all iclogs starting with the one pointed to by the * log. Reset this starting point each time the log is * unlocked (during callbacks). * * Keep looping through iclogs until one full pass is made * without running any callbacks. */ first_iclog = log->l_iclog; iclog = log->l_iclog; loopdidcallbacks = 0; repeats++; do { /* skip all iclogs in the ACTIVE & DIRTY states */ if (iclog->ic_state & (XLOG_STATE_ACTIVE|XLOG_STATE_DIRTY)) { iclog = iclog->ic_next; continue; } /* * Between marking a filesystem SHUTDOWN and stopping * the log, we do flush all iclogs to disk (if there * wasn't a log I/O error). So, we do want things to * go smoothly in case of just a SHUTDOWN w/o a * LOG_IO_ERROR. */ if (!(iclog->ic_state & XLOG_STATE_IOERROR)) { /* * Can only perform callbacks in order. Since * this iclog is not in the DONE_SYNC/ * DO_CALLBACK state, we skip the rest and * just try to clean up. If we set our iclog * to DO_CALLBACK, we will not process it when * we retry since a previous iclog is in the * CALLBACK and the state cannot change since * we are holding the LOG_LOCK. */ if (!(iclog->ic_state & (XLOG_STATE_DONE_SYNC | XLOG_STATE_DO_CALLBACK))) { if (ciclog && (ciclog->ic_state == XLOG_STATE_DONE_SYNC)) { ciclog->ic_state = XLOG_STATE_DO_CALLBACK; } break; } /* * We now have an iclog that is in either the * DO_CALLBACK or DONE_SYNC states. The other * states (WANT_SYNC, SYNCING, or CALLBACK were * caught by the above if and are going to * clean (i.e. we aren't doing their callbacks) * see the above if. */ /* * We will do one more check here to see if we * have chased our tail around. */ lowest_lsn = xlog_get_lowest_lsn(log); if (lowest_lsn && ( XFS_LSN_CMP( lowest_lsn, INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT) )<0)) { iclog = iclog->ic_next; continue; /* Leave this iclog for * another thread */ } iclog->ic_state = XLOG_STATE_CALLBACK; LOG_UNLOCK(log, s); /* l_last_sync_lsn field protected by * GRANT_LOCK. Don't worry about iclog's lsn. * No one else can be here except us. */ s = GRANT_LOCK(log); ASSERT(XFS_LSN_CMP( log->l_last_sync_lsn, INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT) )<=0); log->l_last_sync_lsn = INT_GET(iclog->ic_header.h_lsn, ARCH_CONVERT); GRANT_UNLOCK(log, s); /* * Keep processing entries in the callback list * until we come around and it is empty. We * need to atomically see that the list is * empty and change the state to DIRTY so that * we don't miss any more callbacks being added. */ s = LOG_LOCK(log); } else { ioerrors++; } cb = iclog->ic_callback; while (cb) { iclog->ic_callback_tail = &(iclog->ic_callback); iclog->ic_callback = NULL; LOG_UNLOCK(log, s); /* perform callbacks in the order given */ for (; cb; cb = cb_next) { cb_next = cb->cb_next; cb->cb_func(cb->cb_arg, aborte
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -