📄 xfs_trans.c
字号:
/* * We need to pass the iclog buffer which was used for the * transaction commit record into this function, and attach * the callback to it. The callback must be attached before * the items are unlocked to avoid racing with other threads * waiting for an item to unlock. */ shutdown = xfs_log_notify(mp, commit_iclog, &(tp->t_logcb)); /* * Mark this thread as no longer being in a transaction */ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); /* * Once all the items of the transaction have been copied * to the in core log and the callback is attached, the * items can be unlocked. * * This will free descriptors pointing to items which were * not logged since there is nothing more to do with them. * For items which were logged, we will keep pointers to them * so they can be unpinned after the transaction commits to disk. * This will also stamp each modified meta-data item with * the commit lsn of this transaction for dependency tracking * purposes. */ xfs_trans_unlock_items(tp, commit_lsn); /* * If we detected a log error earlier, finish committing * the transaction now (unpin log items, etc). * * Order is critical here, to avoid using the transaction * pointer after its been freed (by xfs_trans_committed * either here now, or as a callback). We cannot do this * step inside xfs_log_notify as was done earlier because * of this issue. */ if (shutdown) xfs_trans_committed(tp, XFS_LI_ABORTED); /* * Now that the xfs_trans_committed callback has been attached, * and the items are released we can finally allow the iclog to * go to disk. */ error = xfs_log_release_iclog(mp, commit_iclog); /* * If the transaction needs to be synchronous, then force the * log out now and wait for it. */ if (sync) { if (!error) { error = _xfs_log_force(mp, commit_lsn, XFS_LOG_FORCE | XFS_LOG_SYNC, log_flushed); } XFS_STATS_INC(xs_trans_sync); } else { XFS_STATS_INC(xs_trans_async); } return (error);}/* * Total up the number of log iovecs needed to commit this * transaction. The transaction itself needs one for the * transaction header. Ask each dirty item in turn how many * it needs to get the total. */STATIC uintxfs_trans_count_vecs( xfs_trans_t *tp){ int nvecs; xfs_log_item_desc_t *lidp; nvecs = 1; lidp = xfs_trans_first_item(tp); ASSERT(lidp != NULL); /* In the non-debug case we need to start bailing out if we * didn't find a log_item here, return zero and let trans_commit * deal with it. */ if (lidp == NULL) return 0; while (lidp != NULL) { /* * Skip items which aren't dirty in this transaction. */ if (!(lidp->lid_flags & XFS_LID_DIRTY)) { lidp = xfs_trans_next_item(tp, lidp); continue; } lidp->lid_size = IOP_SIZE(lidp->lid_item); nvecs += lidp->lid_size; lidp = xfs_trans_next_item(tp, lidp); } return nvecs;}/* * Called from the trans_commit code when we notice that * the filesystem is in the middle of a forced shutdown. */STATIC voidxfs_trans_uncommit( xfs_trans_t *tp, uint flags){ xfs_log_item_desc_t *lidp; for (lidp = xfs_trans_first_item(tp); lidp != NULL; lidp = xfs_trans_next_item(tp, lidp)) { /* * Unpin all but those that aren't dirty. */ if (lidp->lid_flags & XFS_LID_DIRTY) IOP_UNPIN_REMOVE(lidp->lid_item, tp); } xfs_trans_unreserve_and_mod_sb(tp); XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(tp->t_mountp, tp); xfs_trans_free_items(tp, flags); xfs_trans_free_busy(tp); xfs_trans_free(tp);}/* * Fill in the vector with pointers to data to be logged * by this transaction. The transaction header takes * the first vector, and then each dirty item takes the * number of vectors it indicated it needed in xfs_trans_count_vecs(). * * As each item fills in the entries it needs, also pin the item * so that it cannot be flushed out until the log write completes. */STATIC voidxfs_trans_fill_vecs( xfs_trans_t *tp, xfs_log_iovec_t *log_vector){ xfs_log_item_desc_t *lidp; xfs_log_iovec_t *vecp; uint nitems; /* * Skip over the entry for the transaction header, we'll * fill that in at the end. */ vecp = log_vector + 1; /* pointer arithmetic */ nitems = 0; lidp = xfs_trans_first_item(tp); ASSERT(lidp != NULL); while (lidp != NULL) { /* * Skip items which aren't dirty in this transaction. */ if (!(lidp->lid_flags & XFS_LID_DIRTY)) { lidp = xfs_trans_next_item(tp, lidp); continue; } /* * The item may be marked dirty but not log anything. * This can be used to get called when a transaction * is committed. */ if (lidp->lid_size) { nitems++; } IOP_FORMAT(lidp->lid_item, vecp); vecp += lidp->lid_size; /* pointer arithmetic */ IOP_PIN(lidp->lid_item); lidp = xfs_trans_next_item(tp, lidp); } /* * Now that we've counted the number of items in this * transaction, fill in the transaction header. */ tp->t_header.th_magic = XFS_TRANS_HEADER_MAGIC; tp->t_header.th_type = tp->t_type; tp->t_header.th_num_items = nitems; log_vector->i_addr = (xfs_caddr_t)&tp->t_header; log_vector->i_len = sizeof(xfs_trans_header_t); XLOG_VEC_SET_TYPE(log_vector, XLOG_REG_TYPE_TRANSHDR);}/* * Unlock all of the transaction's items and free the transaction. * The transaction must not have modified any of its items, because * there is no way to restore them to their previous state. * * If the transaction has made a log reservation, make sure to release * it as well. */voidxfs_trans_cancel( xfs_trans_t *tp, int flags){ int log_flags;#ifdef DEBUG xfs_log_item_chunk_t *licp; xfs_log_item_desc_t *lidp; xfs_log_item_t *lip; int i;#endif xfs_mount_t *mp = tp->t_mountp; /* * See if the caller is being too lazy to figure out if * the transaction really needs an abort. */ if ((flags & XFS_TRANS_ABORT) && !(tp->t_flags & XFS_TRANS_DIRTY)) flags &= ~XFS_TRANS_ABORT; /* * See if the caller is relying on us to shut down the * filesystem. This happens in paths where we detect * corruption and decide to give up. */ if ((tp->t_flags & XFS_TRANS_DIRTY) && !XFS_FORCED_SHUTDOWN(mp)) { XFS_ERROR_REPORT("xfs_trans_cancel", XFS_ERRLEVEL_LOW, mp); xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); }#ifdef DEBUG if (!(flags & XFS_TRANS_ABORT)) { licp = &(tp->t_items); while (licp != NULL) { lidp = licp->lic_descs; for (i = 0; i < licp->lic_unused; i++, lidp++) { if (XFS_LIC_ISFREE(licp, i)) { continue; } lip = lidp->lid_item; if (!XFS_FORCED_SHUTDOWN(mp)) ASSERT(!(lip->li_type == XFS_LI_EFD)); } licp = licp->lic_next; } }#endif xfs_trans_unreserve_and_mod_sb(tp); XFS_TRANS_UNRESERVE_AND_MOD_DQUOTS(mp, tp); if (tp->t_ticket) { if (flags & XFS_TRANS_RELEASE_LOG_RES) { ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); log_flags = XFS_LOG_REL_PERM_RESERV; } else { log_flags = 0; } xfs_log_done(mp, tp->t_ticket, NULL, log_flags); } /* mark this thread as no longer being in a transaction */ current_restore_flags_nested(&tp->t_pflags, PF_FSTRANS); xfs_trans_free_items(tp, flags); xfs_trans_free_busy(tp); xfs_trans_free(tp);}/* * Free the transaction structure. If there is more clean up * to do when the structure is freed, add it here. */STATIC voidxfs_trans_free( xfs_trans_t *tp){ atomic_dec(&tp->t_mountp->m_active_trans); XFS_TRANS_FREE_DQINFO(tp->t_mountp, tp); kmem_zone_free(xfs_trans_zone, tp);}/* * THIS SHOULD BE REWRITTEN TO USE xfs_trans_next_item(). * * This is typically called by the LM when a transaction has been fully * committed to disk. It needs to unpin the items which have * been logged by the transaction and update their positions * in the AIL if necessary. * This also gets called when the transactions didn't get written out * because of an I/O error. Abortflag & XFS_LI_ABORTED is set then. * * Call xfs_trans_chunk_committed() to process the items in * each chunk. */STATIC voidxfs_trans_committed( xfs_trans_t *tp, int abortflag){ xfs_log_item_chunk_t *licp; xfs_log_item_chunk_t *next_licp; xfs_log_busy_chunk_t *lbcp; xfs_log_busy_slot_t *lbsp; int i; /* * Call the transaction's completion callback if there * is one. */ if (tp->t_callback != NULL) { tp->t_callback(tp, tp->t_callarg); } /* * Special case the chunk embedded in the transaction. */ licp = &(tp->t_items); if (!(XFS_LIC_ARE_ALL_FREE(licp))) { xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag); } /* * Process the items in each chunk in turn. */ licp = licp->lic_next; while (licp != NULL) { ASSERT(!XFS_LIC_ARE_ALL_FREE(licp)); xfs_trans_chunk_committed(licp, tp->t_lsn, abortflag); next_licp = licp->lic_next; kmem_free(licp, sizeof(xfs_log_item_chunk_t)); licp = next_licp; } /* * Clear all the per-AG busy list items listed in this transaction */ lbcp = &tp->t_busy; while (lbcp != NULL) { for (i = 0, lbsp = lbcp->lbc_busy; i < lbcp->lbc_unused; i++, lbsp++) { if (!XFS_LBC_ISFREE(lbcp, i)) { xfs_alloc_clear_busy(tp, lbsp->lbc_ag, lbsp->lbc_idx); } } lbcp = lbcp->lbc_next; } xfs_trans_free_busy(tp); /* * That's it for the transaction structure. Free it. */ xfs_trans_free(tp);}/* * This is called to perform the commit processing for each * item described by the given chunk. * * The commit processing consists of unlocking items which were * held locked with the SYNC_UNLOCK attribute, calling the committed * routine of each logged item, updating the item's position in the AIL * if necessary, and unpinning each item. If the committed routine * returns -1, then do nothing further with the item because it * may have been freed. * * Since items are unlocked when they are copied to the incore * log, it is possible for two transactions to be completing * and manipulating the same item simultaneously. The AIL lock * will protect the lsn field of each item. The value of this * field can never go backwards. * * We unpin the items after repositioning them in the AIL, because * otherwise they could be immediately flushed and we'd have to race * with the flusher trying to pull the item from the AIL as we add it. */STATIC voidxfs_trans_chunk_committed( xfs_log_item_chunk_t *licp, xfs_lsn_t lsn, int aborted){ xfs_log_item_desc_t *lidp; xfs_log_item_t *lip; xfs_lsn_t item_lsn; struct xfs_mount *mp; int i; SPLDECL(s); lidp = licp->lic_descs; for (i = 0; i < licp->lic_unused; i++, lidp++) { if (XFS_LIC_ISFREE(licp, i)) { continue; } lip = lidp->lid_item; if (aborted) lip->li_flags |= XFS_LI_ABORTED; /* * Send in the ABORTED flag to the COMMITTED routine * so that it knows whether the transaction was aborted * or not. */ item_lsn = IOP_COMMITTED(lip, lsn); /* * If the committed routine returns -1, make * no more references to the item. */ if (XFS_LSN_CMP(item_lsn, (xfs_lsn_t)-1) == 0) { continue; } /* * If the returned lsn is greater than what it * contained before, update the location of the * item in the AIL. If it is not, then do nothing. * Items can never move backwards in the AIL. * * While the new lsn should usually be greater, it * is possible that a later transaction completing * simultaneously with an earlier one using the * same item could complete first with a higher lsn. * This would cause the earlier transaction to fail * the test below. */ mp = lip->li_mountp; AIL_LOCK(mp,s); if (XFS_LSN_CMP(item_lsn, lip->li_lsn) > 0) { /* * This will set the item's lsn to item_lsn * and update the position of the item in * the AIL. * * xfs_trans_update_ail() drops the AIL lock. */ xfs_trans_update_ail(mp, lip, item_lsn, s); } else { AIL_UNLOCK(mp, s); } /* * Now that we've repositioned the item in the AIL, * unpin it so it can be flushed. Pass information * about buffer stale state down from the log item * flags, if anyone else stales the buffer we do not * want to pay any attention to it. */ IOP_UNPIN(lip, lidp->lid_flags & XFS_LID_BUF_STALE); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -