xfs: use EFI refcount consistently in log recovery

The EFI is initialized with a reference count of 2. One for the EFI to
ensure the item makes it to the AIL and one for the subsequently created
EFD to release the EFI once the EFD is committed. Log recovery uses the
EFI in a similar manner, but implements a hack to remove both references
in one call once the EFD is handled.

Update log recovery to use EFI reference counting in a manner consistent
with the log. When an EFI is encountered during recovery, an EFI item is
allocated and inserted to the AIL directly. Since the EFI reference is
typically dropped when the EFI is unpinned and this is analogous with
AIL insertion, drop the EFI reference at this point.

When a corresponding EFD is encountered in the log, this indicates that
the extents were freed, no processing is required and the EFI can be
dropped. Update xlog_recover_efd_pass2() to simply drop the EFD
reference at this point rather than open code the AIL removal and EFI
free.

Remaining EFIs (i.e., with no corresponding EFD) are processed in
xlog_recover_finish(). An EFD transaction is allocated and the extents
are freed, which transfers ownership of the EFI reference to the EFD
item in the log.

Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
This commit is contained in:
Brian Foster 2015-08-19 09:52:21 +10:00 committed by Dave Chinner
parent 6bc43af3d5
commit e32a1d1fbf
2 changed files with 43 additions and 57 deletions

View File

@ -46,34 +46,6 @@ xfs_efi_item_free(
kmem_zone_free(xfs_efi_zone, efip); kmem_zone_free(xfs_efi_zone, efip);
} }
/*
* Freeing the efi requires that we remove it from the AIL if it has already
* been placed there. However, the EFI may not yet have been placed in the AIL
* when called by xfs_efi_release() from EFD processing due to the ordering of
* committed vs unpin operations in bulk insert operations. Hence the reference
* count to ensure only the last caller frees the EFI.
*/
STATIC void
__xfs_efi_release(
struct xfs_efi_log_item *efip)
{
struct xfs_ail *ailp = efip->efi_item.li_ailp;
if (atomic_dec_and_test(&efip->efi_refcount)) {
spin_lock(&ailp->xa_lock);
/*
* We don't know whether the EFI made it to the AIL. Remove it
* if so. Note that xfs_trans_ail_delete() drops the AIL lock.
*/
if (efip->efi_item.li_flags & XFS_LI_IN_AIL)
xfs_trans_ail_delete(ailp, &efip->efi_item,
SHUTDOWN_LOG_IO_ERROR);
else
spin_unlock(&ailp->xa_lock);
xfs_efi_item_free(efip);
}
}
/* /*
* This returns the number of iovecs needed to log the given efi item. * This returns the number of iovecs needed to log the given efi item.
* We only need 1 iovec for an efi item. It just logs the efi_log_format * We only need 1 iovec for an efi item. It just logs the efi_log_format
@ -304,21 +276,32 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
} }
/* /*
* This is called by the efd item code below to release references to the given * Freeing the efi requires that we remove it from the AIL if it has already
* efi item. Each efd calls this with the number of extents that it has * been placed there. However, the EFI may not yet have been placed in the AIL
* logged, and when the sum of these reaches the total number of extents logged * when called by xfs_efi_release() from EFD processing due to the ordering of
* by this efi item we can free the efi item. * committed vs unpin operations in bulk insert operations. Hence the reference
* count to ensure only the last caller frees the EFI.
*/ */
void void
xfs_efi_release( xfs_efi_release(
struct xfs_efi_log_item *efip) struct xfs_efi_log_item *efip)
{ {
/* recovery needs us to drop the EFI reference, too */ struct xfs_ail *ailp = efip->efi_item.li_ailp;
if (test_bit(XFS_EFI_RECOVERED, &efip->efi_flags))
__xfs_efi_release(efip);
__xfs_efi_release(efip); if (atomic_dec_and_test(&efip->efi_refcount)) {
/* efip may now have been freed, do not reference it again. */ spin_lock(&ailp->xa_lock);
/*
* We don't know whether the EFI made it to the AIL. Remove it
* if so. Note that xfs_trans_ail_delete() drops the AIL lock.
*/
if (efip->efi_item.li_flags & XFS_LI_IN_AIL)
xfs_trans_ail_delete(ailp, &efip->efi_item,
SHUTDOWN_LOG_IO_ERROR);
else
spin_unlock(&ailp->xa_lock);
xfs_efi_item_free(efip);
}
} }
static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip) static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)

View File

@ -2928,16 +2928,16 @@ xlog_recover_efi_pass2(
struct xlog_recover_item *item, struct xlog_recover_item *item,
xfs_lsn_t lsn) xfs_lsn_t lsn)
{ {
int error; int error;
xfs_mount_t *mp = log->l_mp; struct xfs_mount *mp = log->l_mp;
xfs_efi_log_item_t *efip; struct xfs_efi_log_item *efip;
xfs_efi_log_format_t *efi_formatp; struct xfs_efi_log_format *efi_formatp;
efi_formatp = item->ri_buf[0].i_addr; efi_formatp = item->ri_buf[0].i_addr;
efip = xfs_efi_init(mp, efi_formatp->efi_nextents); efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
if ((error = xfs_efi_copy_format(&(item->ri_buf[0]), error = xfs_efi_copy_format(&item->ri_buf[0], &efip->efi_format);
&(efip->efi_format)))) { if (error) {
xfs_efi_item_free(efip); xfs_efi_item_free(efip);
return error; return error;
} }
@ -2945,20 +2945,23 @@ xlog_recover_efi_pass2(
spin_lock(&log->l_ailp->xa_lock); spin_lock(&log->l_ailp->xa_lock);
/* /*
* xfs_trans_ail_update() drops the AIL lock. * The EFI has two references. One for the EFD and one for EFI to ensure
* it makes it into the AIL. Insert the EFI into the AIL directly and
* drop the EFI reference. Note that xfs_trans_ail_update() drops the
* AIL lock.
*/ */
xfs_trans_ail_update(log->l_ailp, &efip->efi_item, lsn); xfs_trans_ail_update(log->l_ailp, &efip->efi_item, lsn);
xfs_efi_release(efip);
return 0; return 0;
} }
/* /*
* This routine is called when an efd format structure is found in * This routine is called when an EFD format structure is found in a committed
* a committed transaction in the log. It's purpose is to cancel * transaction in the log. Its purpose is to cancel the corresponding EFI if it
* the corresponding efi if it was still in the log. To do this * was still in the log. To do this it searches the AIL for the EFI with an id
* it searches the AIL for the efi with an id equal to that in the * equal to that in the EFD format structure. If we find it we drop the EFD
* efd format structure. If we find it, we remove the efi from the * reference, which removes the EFI from the AIL and frees it.
* AIL and free it.
*/ */
STATIC int STATIC int
xlog_recover_efd_pass2( xlog_recover_efd_pass2(
@ -2980,8 +2983,8 @@ xlog_recover_efd_pass2(
efi_id = efd_formatp->efd_efi_id; efi_id = efd_formatp->efd_efi_id;
/* /*
* Search for the efi with the id in the efd format structure * Search for the EFI with the id in the EFD format structure in the
* in the AIL. * AIL.
*/ */
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);
@ -2990,18 +2993,18 @@ xlog_recover_efd_pass2(
efip = (xfs_efi_log_item_t *)lip; efip = (xfs_efi_log_item_t *)lip;
if (efip->efi_format.efi_id == efi_id) { if (efip->efi_format.efi_id == efi_id) {
/* /*
* xfs_trans_ail_delete() drops the * Drop the EFD reference to the EFI. This
* AIL lock. * removes the EFI from the AIL and frees it.
*/ */
xfs_trans_ail_delete(ailp, lip, spin_unlock(&ailp->xa_lock);
SHUTDOWN_CORRUPT_INCORE); xfs_efi_release(efip);
xfs_efi_item_free(efip);
spin_lock(&ailp->xa_lock); spin_lock(&ailp->xa_lock);
break; break;
} }
} }
lip = xfs_trans_ail_cursor_next(ailp, &cur); lip = xfs_trans_ail_cursor_next(ailp, &cur);
} }
xfs_trans_ail_cursor_done(&cur); xfs_trans_ail_cursor_done(&cur);
spin_unlock(&ailp->xa_lock); spin_unlock(&ailp->xa_lock);