xfs: refactor redo intent item processing
Refactor the EFI intent item recovery (and cancellation) functions into a general function that scans the AIL and an intent item type specific handler. Move the function that recovers a single EFI item into the extent free item code. We'll want the generalized function when we start wiring up more redo item types. Furthermore, ensure that log recovery only replays the redo items that were in the AIL prior to recovery by checking the item LSN against the largest LSN seen during log scanning. As written this should never happen, but we can be defensive anyway. Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com> Reviewed-by: Brian Foster <bfoster@redhat.com> Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Родитель
2c3234d1ef
Коммит
dc42375d5f
|
@ -20,6 +20,7 @@
|
||||||
#include "xfs_format.h"
|
#include "xfs_format.h"
|
||||||
#include "xfs_log_format.h"
|
#include "xfs_log_format.h"
|
||||||
#include "xfs_trans_resv.h"
|
#include "xfs_trans_resv.h"
|
||||||
|
#include "xfs_bit.h"
|
||||||
#include "xfs_mount.h"
|
#include "xfs_mount.h"
|
||||||
#include "xfs_trans.h"
|
#include "xfs_trans.h"
|
||||||
#include "xfs_trans_priv.h"
|
#include "xfs_trans_priv.h"
|
||||||
|
@ -486,3 +487,67 @@ xfs_efd_init(
|
||||||
|
|
||||||
return efdp;
|
return efdp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Process an extent free intent item that was recovered from
|
||||||
|
* the log. We need to free the extents that it describes.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
xfs_efi_recover(
|
||||||
|
struct xfs_mount *mp,
|
||||||
|
struct xfs_efi_log_item *efip)
|
||||||
|
{
|
||||||
|
struct xfs_efd_log_item *efdp;
|
||||||
|
struct xfs_trans *tp;
|
||||||
|
int i;
|
||||||
|
int error = 0;
|
||||||
|
xfs_extent_t *extp;
|
||||||
|
xfs_fsblock_t startblock_fsb;
|
||||||
|
|
||||||
|
ASSERT(!test_bit(XFS_EFI_RECOVERED, &efip->efi_flags));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First check the validity of the extents described by the
|
||||||
|
* EFI. If any are bad, then assume that all are bad and
|
||||||
|
* just toss the EFI.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < efip->efi_format.efi_nextents; i++) {
|
||||||
|
extp = &(efip->efi_format.efi_extents[i]);
|
||||||
|
startblock_fsb = XFS_BB_TO_FSB(mp,
|
||||||
|
XFS_FSB_TO_DADDR(mp, extp->ext_start));
|
||||||
|
if ((startblock_fsb == 0) ||
|
||||||
|
(extp->ext_len == 0) ||
|
||||||
|
(startblock_fsb >= mp->m_sb.sb_dblocks) ||
|
||||||
|
(extp->ext_len >= mp->m_sb.sb_agblocks)) {
|
||||||
|
/*
|
||||||
|
* This will pull the EFI from the AIL and
|
||||||
|
* free the memory associated with it.
|
||||||
|
*/
|
||||||
|
set_bit(XFS_EFI_RECOVERED, &efip->efi_flags);
|
||||||
|
xfs_efi_release(efip);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
|
||||||
|
if (error)
|
||||||
|
return error;
|
||||||
|
efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents);
|
||||||
|
|
||||||
|
for (i = 0; i < efip->efi_format.efi_nextents; i++) {
|
||||||
|
extp = &(efip->efi_format.efi_extents[i]);
|
||||||
|
error = xfs_trans_free_extent(tp, efdp, extp->ext_start,
|
||||||
|
extp->ext_len);
|
||||||
|
if (error)
|
||||||
|
goto abort_error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
set_bit(XFS_EFI_RECOVERED, &efip->efi_flags);
|
||||||
|
error = xfs_trans_commit(tp);
|
||||||
|
return error;
|
||||||
|
|
||||||
|
abort_error:
|
||||||
|
xfs_trans_cancel(tp);
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
|
@ -98,4 +98,7 @@ int xfs_efi_copy_format(xfs_log_iovec_t *buf,
|
||||||
void xfs_efi_item_free(xfs_efi_log_item_t *);
|
void xfs_efi_item_free(xfs_efi_log_item_t *);
|
||||||
void xfs_efi_release(struct xfs_efi_log_item *);
|
void xfs_efi_release(struct xfs_efi_log_item *);
|
||||||
|
|
||||||
|
int xfs_efi_recover(struct xfs_mount *mp,
|
||||||
|
struct xfs_efi_log_item *efip);
|
||||||
|
|
||||||
#endif /* __XFS_EXTFREE_ITEM_H__ */
|
#endif /* __XFS_EXTFREE_ITEM_H__ */
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
#include "xfs_bit.h"
|
#include "xfs_bit.h"
|
||||||
#include "xfs_sb.h"
|
#include "xfs_sb.h"
|
||||||
#include "xfs_mount.h"
|
#include "xfs_mount.h"
|
||||||
#include "xfs_defer.h"
|
|
||||||
#include "xfs_da_format.h"
|
#include "xfs_da_format.h"
|
||||||
#include "xfs_da_btree.h"
|
#include "xfs_da_btree.h"
|
||||||
#include "xfs_inode.h"
|
#include "xfs_inode.h"
|
||||||
|
@ -4165,126 +4164,112 @@ xlog_recover_process_data(
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* Recover the EFI if necessary. */
|
||||||
* Process an extent free intent item that was recovered from
|
|
||||||
* the log. We need to free the extents that it describes.
|
|
||||||
*/
|
|
||||||
STATIC int
|
STATIC int
|
||||||
xlog_recover_process_efi(
|
xlog_recover_process_efi(
|
||||||
xfs_mount_t *mp,
|
struct xfs_mount *mp,
|
||||||
xfs_efi_log_item_t *efip)
|
struct xfs_ail *ailp,
|
||||||
|
struct xfs_log_item *lip)
|
||||||
{
|
{
|
||||||
xfs_efd_log_item_t *efdp;
|
struct xfs_efi_log_item *efip;
|
||||||
xfs_trans_t *tp;
|
int error;
|
||||||
int i;
|
|
||||||
int error = 0;
|
|
||||||
xfs_extent_t *extp;
|
|
||||||
xfs_fsblock_t startblock_fsb;
|
|
||||||
|
|
||||||
ASSERT(!test_bit(XFS_EFI_RECOVERED, &efip->efi_flags));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First check the validity of the extents described by the
|
* Skip EFIs that we've already processed.
|
||||||
* EFI. If any are bad, then assume that all are bad and
|
|
||||||
* just toss the EFI.
|
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < efip->efi_format.efi_nextents; i++) {
|
efip = container_of(lip, struct xfs_efi_log_item, efi_item);
|
||||||
extp = &(efip->efi_format.efi_extents[i]);
|
if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags))
|
||||||
startblock_fsb = XFS_BB_TO_FSB(mp,
|
return 0;
|
||||||
XFS_FSB_TO_DADDR(mp, extp->ext_start));
|
|
||||||
if ((startblock_fsb == 0) ||
|
|
||||||
(extp->ext_len == 0) ||
|
|
||||||
(startblock_fsb >= mp->m_sb.sb_dblocks) ||
|
|
||||||
(extp->ext_len >= mp->m_sb.sb_agblocks)) {
|
|
||||||
/*
|
|
||||||
* This will pull the EFI from the AIL and
|
|
||||||
* free the memory associated with it.
|
|
||||||
*/
|
|
||||||
set_bit(XFS_EFI_RECOVERED, &efip->efi_flags);
|
|
||||||
xfs_efi_release(efip);
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, 0, 0, 0, &tp);
|
spin_unlock(&ailp->xa_lock);
|
||||||
if (error)
|
error = xfs_efi_recover(mp, efip);
|
||||||
return error;
|
spin_lock(&ailp->xa_lock);
|
||||||
efdp = xfs_trans_get_efd(tp, efip, efip->efi_format.efi_nextents);
|
|
||||||
|
|
||||||
for (i = 0; i < efip->efi_format.efi_nextents; i++) {
|
|
||||||
extp = &(efip->efi_format.efi_extents[i]);
|
|
||||||
error = xfs_trans_free_extent(tp, efdp, extp->ext_start,
|
|
||||||
extp->ext_len);
|
|
||||||
if (error)
|
|
||||||
goto abort_error;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
set_bit(XFS_EFI_RECOVERED, &efip->efi_flags);
|
|
||||||
error = xfs_trans_commit(tp);
|
|
||||||
return error;
|
|
||||||
|
|
||||||
abort_error:
|
|
||||||
xfs_trans_cancel(tp);
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Release the EFI since we're cancelling everything. */
|
||||||
|
STATIC void
|
||||||
|
xlog_recover_cancel_efi(
|
||||||
|
struct xfs_mount *mp,
|
||||||
|
struct xfs_ail *ailp,
|
||||||
|
struct xfs_log_item *lip)
|
||||||
|
{
|
||||||
|
struct xfs_efi_log_item *efip;
|
||||||
|
|
||||||
|
efip = container_of(lip, struct xfs_efi_log_item, efi_item);
|
||||||
|
|
||||||
|
spin_unlock(&ailp->xa_lock);
|
||||||
|
xfs_efi_release(efip);
|
||||||
|
spin_lock(&ailp->xa_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Is this log item a deferred action intent? */
|
||||||
|
static inline bool xlog_item_is_intent(struct xfs_log_item *lip)
|
||||||
|
{
|
||||||
|
switch (lip->li_type) {
|
||||||
|
case XFS_LI_EFI:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* When this is called, all of the EFIs which did not have
|
* When this is called, all of the log intent items which did not have
|
||||||
* corresponding EFDs should be in the AIL. What we do now
|
* corresponding log done items should be in the AIL. What we do now
|
||||||
* is free the extents associated with each one.
|
* is update the data structures associated with each one.
|
||||||
*
|
*
|
||||||
* Since we process the EFIs in normal transactions, they
|
* Since we process the log intent items in normal transactions, they
|
||||||
* will be removed at some point after the commit. This prevents
|
* will be removed at some point after the commit. This prevents us
|
||||||
* us from just walking down the list processing each one.
|
* from just walking down the list processing each one. We'll use a
|
||||||
* We'll use a flag in the EFI to skip those that we've already
|
* flag in the intent item to skip those that we've already processed
|
||||||
* processed and use the AIL iteration mechanism's generation
|
* and use the AIL iteration mechanism's generation count to try to
|
||||||
* count to try to speed this up at least a bit.
|
* speed this up at least a bit.
|
||||||
*
|
*
|
||||||
* When we start, we know that the EFIs are the only things in
|
* When we start, we know that the intents are the only things in the
|
||||||
* the AIL. As we process them, however, other items are added
|
* AIL. As we process them, however, other items are added to the
|
||||||
* to the AIL. Since everything added to the AIL must come after
|
* AIL.
|
||||||
* everything already in the AIL, we stop processing as soon as
|
|
||||||
* we see something other than an EFI in the AIL.
|
|
||||||
*/
|
*/
|
||||||
STATIC int
|
STATIC int
|
||||||
xlog_recover_process_efis(
|
xlog_recover_process_intents(
|
||||||
struct xlog *log)
|
struct xlog *log)
|
||||||
{
|
{
|
||||||
struct xfs_log_item *lip;
|
struct xfs_log_item *lip;
|
||||||
struct xfs_efi_log_item *efip;
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
struct xfs_ail_cursor cur;
|
struct xfs_ail_cursor cur;
|
||||||
struct xfs_ail *ailp;
|
struct xfs_ail *ailp;
|
||||||
|
xfs_lsn_t last_lsn;
|
||||||
|
|
||||||
ailp = log->l_ailp;
|
ailp = log->l_ailp;
|
||||||
spin_lock(&ailp->xa_lock);
|
spin_lock(&ailp->xa_lock);
|
||||||
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
|
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
|
||||||
|
last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);
|
||||||
while (lip != NULL) {
|
while (lip != NULL) {
|
||||||
/*
|
/*
|
||||||
* We're done when we see something other than an EFI.
|
* We're done when we see something other than an intent.
|
||||||
* There should be no EFIs left in the AIL now.
|
* There should be no intents left in the AIL now.
|
||||||
*/
|
*/
|
||||||
if (lip->li_type != XFS_LI_EFI) {
|
if (!xlog_item_is_intent(lip)) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
|
for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
|
||||||
ASSERT(lip->li_type != XFS_LI_EFI);
|
ASSERT(!xlog_item_is_intent(lip));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Skip EFIs that we've already processed.
|
* We should never see a redo item with a LSN higher than
|
||||||
|
* the last transaction we found in the log at the start
|
||||||
|
* of recovery.
|
||||||
*/
|
*/
|
||||||
efip = container_of(lip, struct xfs_efi_log_item, efi_item);
|
ASSERT(XFS_LSN_CMP(last_lsn, lip->li_lsn) >= 0);
|
||||||
if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags)) {
|
|
||||||
lip = xfs_trans_ail_cursor_next(ailp, &cur);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_unlock(&ailp->xa_lock);
|
switch (lip->li_type) {
|
||||||
error = xlog_recover_process_efi(log->l_mp, efip);
|
case XFS_LI_EFI:
|
||||||
spin_lock(&ailp->xa_lock);
|
error = xlog_recover_process_efi(log->l_mp, ailp, lip);
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (error)
|
if (error)
|
||||||
goto out;
|
goto out;
|
||||||
lip = xfs_trans_ail_cursor_next(ailp, &cur);
|
lip = xfs_trans_ail_cursor_next(ailp, &cur);
|
||||||
|
@ -4296,15 +4281,14 @@ out:
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A cancel occurs when the mount has failed and we're bailing out. Release all
|
* A cancel occurs when the mount has failed and we're bailing out.
|
||||||
* pending EFIs so they don't pin the AIL.
|
* Release all pending log intent items so they don't pin the AIL.
|
||||||
*/
|
*/
|
||||||
STATIC int
|
STATIC int
|
||||||
xlog_recover_cancel_efis(
|
xlog_recover_cancel_intents(
|
||||||
struct xlog *log)
|
struct xlog *log)
|
||||||
{
|
{
|
||||||
struct xfs_log_item *lip;
|
struct xfs_log_item *lip;
|
||||||
struct xfs_efi_log_item *efip;
|
|
||||||
int error = 0;
|
int error = 0;
|
||||||
struct xfs_ail_cursor cur;
|
struct xfs_ail_cursor cur;
|
||||||
struct xfs_ail *ailp;
|
struct xfs_ail *ailp;
|
||||||
|
@ -4314,22 +4298,22 @@ xlog_recover_cancel_efis(
|
||||||
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
|
lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
|
||||||
while (lip != NULL) {
|
while (lip != NULL) {
|
||||||
/*
|
/*
|
||||||
* We're done when we see something other than an EFI.
|
* We're done when we see something other than an intent.
|
||||||
* There should be no EFIs left in the AIL now.
|
* There should be no intents left in the AIL now.
|
||||||
*/
|
*/
|
||||||
if (lip->li_type != XFS_LI_EFI) {
|
if (!xlog_item_is_intent(lip)) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
|
for (; lip; lip = xfs_trans_ail_cursor_next(ailp, &cur))
|
||||||
ASSERT(lip->li_type != XFS_LI_EFI);
|
ASSERT(!xlog_item_is_intent(lip));
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
efip = container_of(lip, struct xfs_efi_log_item, efi_item);
|
switch (lip->li_type) {
|
||||||
|
case XFS_LI_EFI:
|
||||||
spin_unlock(&ailp->xa_lock);
|
xlog_recover_cancel_efi(log->l_mp, ailp, lip);
|
||||||
xfs_efi_release(efip);
|
break;
|
||||||
spin_lock(&ailp->xa_lock);
|
}
|
||||||
|
|
||||||
lip = xfs_trans_ail_cursor_next(ailp, &cur);
|
lip = xfs_trans_ail_cursor_next(ailp, &cur);
|
||||||
}
|
}
|
||||||
|
@ -5140,16 +5124,16 @@ xlog_recover_finish(
|
||||||
*/
|
*/
|
||||||
if (log->l_flags & XLOG_RECOVERY_NEEDED) {
|
if (log->l_flags & XLOG_RECOVERY_NEEDED) {
|
||||||
int error;
|
int error;
|
||||||
error = xlog_recover_process_efis(log);
|
error = xlog_recover_process_intents(log);
|
||||||
if (error) {
|
if (error) {
|
||||||
xfs_alert(log->l_mp, "Failed to recover EFIs");
|
xfs_alert(log->l_mp, "Failed to recover intents");
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Sync the log to get all the EFIs out of the AIL.
|
* Sync the log to get all the intents out of the AIL.
|
||||||
* This isn't absolutely necessary, but it helps in
|
* This isn't absolutely necessary, but it helps in
|
||||||
* case the unlink transactions would have problems
|
* case the unlink transactions would have problems
|
||||||
* pushing the EFIs out of the way.
|
* pushing the intents out of the way.
|
||||||
*/
|
*/
|
||||||
xfs_log_force(log->l_mp, XFS_LOG_SYNC);
|
xfs_log_force(log->l_mp, XFS_LOG_SYNC);
|
||||||
|
|
||||||
|
@ -5174,7 +5158,7 @@ xlog_recover_cancel(
|
||||||
int error = 0;
|
int error = 0;
|
||||||
|
|
||||||
if (log->l_flags & XLOG_RECOVERY_NEEDED)
|
if (log->l_flags & XLOG_RECOVERY_NEEDED)
|
||||||
error = xlog_recover_cancel_efis(log);
|
error = xlog_recover_cancel_intents(log);
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
Загрузка…
Ссылка в новой задаче