2018-06-06 02:42:14 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2005-11-02 03:58:39 +00:00
|
|
|
* Copyright (c) 2000-2001,2005 Silicon Graphics, Inc.
|
|
|
|
* All Rights Reserved.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
#include "xfs.h"
|
2005-11-02 03:38:42 +00:00
|
|
|
#include "xfs_fs.h"
|
2014-11-28 03:25:04 +00:00
|
|
|
#include "xfs_format.h"
|
2013-10-22 23:50:10 +00:00
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2016-08-03 01:23:49 +00:00
|
|
|
#include "xfs_bit.h"
|
2019-06-29 02:25:35 +00:00
|
|
|
#include "xfs_shared.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_mount.h"
|
2022-07-07 09:07:40 +00:00
|
|
|
#include "xfs_ag.h"
|
2019-06-29 02:28:17 +00:00
|
|
|
#include "xfs_defer.h"
|
2013-10-22 23:50:10 +00:00
|
|
|
#include "xfs_trans.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_trans_priv.h"
|
|
|
|
#include "xfs_extfree_item.h"
|
2013-12-13 00:00:43 +00:00
|
|
|
#include "xfs_log.h"
|
2016-08-03 01:33:42 +00:00
|
|
|
#include "xfs_btree.h"
|
|
|
|
#include "xfs_rmap.h"
|
2019-06-29 02:28:17 +00:00
|
|
|
#include "xfs_alloc.h"
|
|
|
|
#include "xfs_bmap.h"
|
|
|
|
#include "xfs_trace.h"
|
2019-11-02 16:40:53 +00:00
|
|
|
#include "xfs_error.h"
|
2020-05-01 23:00:48 +00:00
|
|
|
#include "xfs_log_priv.h"
|
2020-05-01 23:00:45 +00:00
|
|
|
#include "xfs_log_recover.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-10-12 18:09:23 +00:00
|
|
|
struct kmem_cache *xfs_efi_cache;
|
|
|
|
struct kmem_cache *xfs_efd_cache;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-05-01 23:00:50 +00:00
|
|
|
static const struct xfs_item_ops xfs_efi_item_ops;
|
|
|
|
|
2010-06-23 08:11:15 +00:00
|
|
|
static inline struct xfs_efi_log_item *EFI_ITEM(struct xfs_log_item *lip)
|
|
|
|
{
|
|
|
|
return container_of(lip, struct xfs_efi_log_item, efi_item);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-05-01 23:00:48 +00:00
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_item_free(
|
|
|
|
struct xfs_efi_log_item *efip)
|
2005-06-21 05:41:19 +00:00
|
|
|
{
|
2024-01-15 22:59:42 +00:00
|
|
|
kvfree(efip->efi_item.li_lv_shadow);
|
2010-06-23 08:11:15 +00:00
|
|
|
if (efip->efi_format.efi_nextents > XFS_EFI_MAX_FAST_EXTENTS)
|
2024-01-15 22:59:43 +00:00
|
|
|
kfree(efip);
|
2010-06-23 08:11:15 +00:00
|
|
|
else
|
2021-10-12 18:09:23 +00:00
|
|
|
kmem_cache_free(xfs_efi_cache, efip);
|
2005-06-21 05:41:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2018-04-03 03:08:27 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2020-05-01 23:00:50 +00:00
|
|
|
STATIC void
|
2018-04-03 03:08:27 +00:00
|
|
|
xfs_efi_release(
|
|
|
|
struct xfs_efi_log_item *efip)
|
|
|
|
{
|
|
|
|
ASSERT(atomic_read(&efip->efi_refcount) > 0);
|
2022-05-04 01:46:47 +00:00
|
|
|
if (!atomic_dec_and_test(&efip->efi_refcount))
|
|
|
|
return;
|
|
|
|
|
|
|
|
xfs_trans_ail_delete(&efip->efi_item, 0);
|
|
|
|
xfs_efi_item_free(efip);
|
2018-04-03 03:08:27 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 10:50:04 +00:00
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_item_size(
|
2013-08-12 10:50:04 +00:00
|
|
|
struct xfs_log_item *lip,
|
|
|
|
int *nvecs,
|
|
|
|
int *nbytes)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2022-10-21 16:10:05 +00:00
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
|
|
|
|
2013-08-12 10:50:04 +00:00
|
|
|
*nvecs += 1;
|
2022-10-21 16:10:05 +00:00
|
|
|
*nbytes += xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called to fill in the vector of log iovecs for the
|
|
|
|
* given efi log item. We use only 1 iovec, and we point that
|
|
|
|
* at the efi_log_format structure embedded in the efi item.
|
|
|
|
* It is at this point that we assert that all of the extent
|
|
|
|
* slots in the efi item have been filled.
|
|
|
|
*/
|
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_item_format(
|
|
|
|
struct xfs_log_item *lip,
|
2013-12-13 00:34:02 +00:00
|
|
|
struct xfs_log_vec *lv)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
2013-12-13 00:34:02 +00:00
|
|
|
struct xfs_log_iovec *vecp = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-12-20 00:59:49 +00:00
|
|
|
ASSERT(atomic_read(&efip->efi_next_extent) ==
|
|
|
|
efip->efi_format.efi_nextents);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
efip->efi_format.efi_type = XFS_LI_EFI;
|
|
|
|
efip->efi_format.efi_size = 1;
|
|
|
|
|
2013-12-13 00:34:02 +00:00
|
|
|
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFI_FORMAT,
|
2013-12-13 00:00:43 +00:00
|
|
|
&efip->efi_format,
|
2022-10-21 16:10:05 +00:00
|
|
|
xfs_efi_log_format_sizeof(efip->efi_format.efi_nextents));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
xfs: fix efi/efd error handling to avoid fs shutdown hangs
Freeing an extent in XFS involves logging an EFI (extent free
intention), freeing the actual extent, and logging an EFD (extent
free done). The EFI object is created with a reference count of 2:
one for the current transaction and one for the subsequently created
EFD. Under normal circumstances, the first reference is dropped when
the EFI is unpinned and the second reference is dropped when the EFD
is committed to the on-disk log.
In event of errors or filesystem shutdown, there are various
potential cleanup scenarios depending on the state of the EFI/EFD.
The cleanup scenarios are confusing and racy, as demonstrated by the
following test sequence:
# mount $dev $mnt
# fsstress -d $mnt -n 99999 -p 16 -z -f fallocate=1 \
-f punch=1 -f creat=1 -f unlink=1 &
# sleep 5
# killall -9 fsstress; wait
# godown -f $mnt
# umount
... in which the final umount can hang due to the AIL being pinned
indefinitely by one or more EFI items. This can occur due to several
conditions. For example, if the shutdown occurs after the EFI is
committed to the on-disk log and the EFD committed to the CIL, but
before the EFD committed to the log, the EFD iop_committed() abort
handler does not drop its reference to the EFI. Alternatively,
manual error injection in the xfs_bmap_finish() codepath shows that
if an error occurs after the EFI transaction is committed but before
the EFD is constructed and logged, the EFI is never released from
the AIL.
Update the EFI/EFD item handling code to use a more straightforward
and reliable approach to error handling. If an error occurs after
the EFI transaction is committed and before the EFD is constructed,
release the EFI explicitly from xfs_bmap_finish(). If the EFI
transaction is cancelled, release the EFI in the unlock handler.
Once the EFD is constructed, it is responsible for releasing the EFI
under any circumstances (including whether the EFI item aborts due
to log I/O error). Update the EFD item handlers to release the EFI
if the transaction is cancelled or aborts due to log I/O error.
Finally, update xfs_bmap_finish() to log at least one EFD extent to
the transaction before xfs_free_extent() errors are handled to
ensure the transaction is dirty and EFD item error handling is
triggered.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-18 23:51:16 +00:00
|
|
|
* The unpin operation is the last place an EFI is manipulated in the log. It is
|
|
|
|
* either inserted in the AIL or aborted in the event of a log I/O error. In
|
|
|
|
* either case, the EFI transaction has been successfully committed to make it
|
|
|
|
* this far. Therefore, we expect whoever committed the EFI to either construct
|
|
|
|
* and commit the EFD or drop the EFD's reference in the event of error. Simply
|
|
|
|
* drop the log's EFI reference now that the log is done with it.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_item_unpin(
|
|
|
|
struct xfs_log_item *lip,
|
|
|
|
int remove)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
2015-08-18 23:50:12 +00:00
|
|
|
xfs_efi_release(efip);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
xfs: fix efi/efd error handling to avoid fs shutdown hangs
Freeing an extent in XFS involves logging an EFI (extent free
intention), freeing the actual extent, and logging an EFD (extent
free done). The EFI object is created with a reference count of 2:
one for the current transaction and one for the subsequently created
EFD. Under normal circumstances, the first reference is dropped when
the EFI is unpinned and the second reference is dropped when the EFD
is committed to the on-disk log.
In event of errors or filesystem shutdown, there are various
potential cleanup scenarios depending on the state of the EFI/EFD.
The cleanup scenarios are confusing and racy, as demonstrated by the
following test sequence:
# mount $dev $mnt
# fsstress -d $mnt -n 99999 -p 16 -z -f fallocate=1 \
-f punch=1 -f creat=1 -f unlink=1 &
# sleep 5
# killall -9 fsstress; wait
# godown -f $mnt
# umount
... in which the final umount can hang due to the AIL being pinned
indefinitely by one or more EFI items. This can occur due to several
conditions. For example, if the shutdown occurs after the EFI is
committed to the on-disk log and the EFD committed to the CIL, but
before the EFD committed to the log, the EFD iop_committed() abort
handler does not drop its reference to the EFI. Alternatively,
manual error injection in the xfs_bmap_finish() codepath shows that
if an error occurs after the EFI transaction is committed but before
the EFD is constructed and logged, the EFI is never released from
the AIL.
Update the EFI/EFD item handling code to use a more straightforward
and reliable approach to error handling. If an error occurs after
the EFI transaction is committed and before the EFD is constructed,
release the EFI explicitly from xfs_bmap_finish(). If the EFI
transaction is cancelled, release the EFI in the unlock handler.
Once the EFD is constructed, it is responsible for releasing the EFI
under any circumstances (including whether the EFI item aborts due
to log I/O error). Update the EFD item handlers to release the EFI
if the transaction is cancelled or aborts due to log I/O error.
Finally, update xfs_bmap_finish() to log at least one EFD extent to
the transaction before xfs_free_extent() errors are handled to
ensure the transaction is dirty and EFD item error handling is
triggered.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-18 23:51:16 +00:00
|
|
|
/*
|
|
|
|
* The EFI has been either committed or aborted if the transaction has been
|
|
|
|
* cancelled. If the transaction was cancelled, an EFD isn't going to be
|
|
|
|
* constructed and thus we free the EFI here directly.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
STATIC void
|
2019-06-29 02:27:32 +00:00
|
|
|
xfs_efi_item_release(
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_log_item *lip)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-06-29 02:27:32 +00:00
|
|
|
xfs_efi_release(EFI_ITEM(lip));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allocate and initialize an efi item with the given number of extents.
|
|
|
|
*/
|
2020-05-01 23:00:48 +00:00
|
|
|
STATIC struct xfs_efi_log_item *
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
uint nextents)
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_efi_log_item *efip;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
ASSERT(nextents > 0);
|
|
|
|
if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
|
2022-10-21 16:10:05 +00:00
|
|
|
efip = kzalloc(xfs_efi_log_item_sizeof(nextents),
|
|
|
|
GFP_KERNEL | __GFP_NOFAIL);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2021-10-12 18:09:23 +00:00
|
|
|
efip = kmem_cache_zalloc(xfs_efi_cache,
|
2020-07-22 16:23:10 +00:00
|
|
|
GFP_KERNEL | __GFP_NOFAIL);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 23:10:00 +00:00
|
|
|
xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops);
|
2005-04-16 22:20:36 +00:00
|
|
|
efip->efi_format.efi_nextents = nextents;
|
2015-06-21 23:43:32 +00:00
|
|
|
efip->efi_format.efi_id = (uintptr_t)(void *)efip;
|
2010-12-20 00:59:49 +00:00
|
|
|
atomic_set(&efip->efi_next_extent, 0);
|
xfs: don't free EFIs before the EFDs are committed
Filesystems are occasionally being shut down with this error:
xfs_trans_ail_delete_bulk: attempting to delete a log item that is
not in the AIL.
It was diagnosed to be related to the EFI/EFD commit order when the
EFI and EFD are in different checkpoints and the EFD is committed
before the EFI here:
http://oss.sgi.com/archives/xfs/2013-01/msg00082.html
The real problem is that a single bit cannot fully describe the
states that the EFI/EFD processing can be in. These completion
states are:
EFI EFI in AIL EFD Result
committed/unpinned Yes committed OK
committed/pinned No committed Shutdown
uncommitted No committed Shutdown
Note that the "result" field is what should happen, not what does
happen. The current logic is broken and handles the first two cases
correctly by luck. That is, the code will free the EFI if the
XFS_EFI_COMMITTED bit is *not* set, rather than if it is set. The
inverted logic "works" because if both EFI and EFD are committed,
then the first __xfs_efi_release() call clears the XFS_EFI_COMMITTED
bit, and the second frees the EFI item. Hence as long as
xfs_efi_item_committed() has been called, everything appears to be
fine.
It is the third case where the logic fails - where
xfs_efd_item_committed() is called before xfs_efi_item_committed(),
and that results in the EFI being freed before it has been
committed. That is the bug that triggered the shutdown, and hence
keeping track of whether the EFI has been committed or not is
insufficient to correctly order the EFI/EFD operations w.r.t. the
AIL.
What we really want is this: the EFI is always placed into the
AIL before the last reference goes away. The only way to guarantee
that is that the EFI is not freed until after it has been unpinned
*and* the EFD has been committed. That is, restructure the logic so
that the only case that can occur is the first case.
This can be done easily by replacing the XFS_EFI_COMMITTED with an
EFI reference count. The EFI is initialised with it's own count, and
that is not released until it is unpinned. However, there is a
complication to this method - the high level EFI/EFD code in
xfs_bmap_finish() does not hold direct references to the EFI
structure, and runs a transaction commit between the EFI and EFD
processing. Hence the EFI can be freed even before the EFD is
created using such a method.
Further, log recovery uses the AIL for tracking EFI/EFDs that need
to be recovered, but it uses the AIL *differently* to the EFI
transaction commit. Hence log recovery never pins or unpins EFIs, so
we can't drop the EFI reference count indirectly to free the EFI.
However, this doesn't prevent us from using a reference count here.
There is a 1:1 relationship between EFIs and EFDs, so when we
initialise the EFI we can take a reference count for the EFD as
well. This solves the xfs_bmap_finish() issue - the EFI will never
be freed until the EFD is processed. In terms of log recovery,
during the committing of the EFD we can look for the
XFS_EFI_RECOVERED bit being set and drop the EFI reference as well,
thereby ensuring everything works correctly there as well.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-04-03 03:09:21 +00:00
|
|
|
atomic_set(&efip->efi_refcount, 2);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-06-23 08:11:15 +00:00
|
|
|
return efip;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2006-06-09 04:55:38 +00:00
|
|
|
/*
|
|
|
|
* Copy an EFI format buffer from the given buf, and into the destination
|
|
|
|
* EFI format structure.
|
|
|
|
* The given buffer can be in 32 bit or 64 bit form (which has different padding),
|
|
|
|
* one of which will be the native format for this kernel.
|
|
|
|
* It will handle the conversion of formats if necessary.
|
|
|
|
*/
|
2020-05-01 23:00:48 +00:00
|
|
|
STATIC int
|
2006-06-09 04:55:38 +00:00
|
|
|
xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt)
|
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_log_format_t *src_efi_fmt = buf->i_addr;
|
2006-06-09 04:55:38 +00:00
|
|
|
uint i;
|
2022-10-21 16:10:05 +00:00
|
|
|
uint len = xfs_efi_log_format_sizeof(src_efi_fmt->efi_nextents);
|
|
|
|
uint len32 = xfs_efi_log_format32_sizeof(src_efi_fmt->efi_nextents);
|
|
|
|
uint len64 = xfs_efi_log_format64_sizeof(src_efi_fmt->efi_nextents);
|
2006-06-09 04:55:38 +00:00
|
|
|
|
|
|
|
if (buf->i_len == len) {
|
2022-10-20 23:39:59 +00:00
|
|
|
memcpy(dst_efi_fmt, src_efi_fmt,
|
|
|
|
offsetof(struct xfs_efi_log_format, efi_extents));
|
|
|
|
for (i = 0; i < src_efi_fmt->efi_nextents; i++)
|
|
|
|
memcpy(&dst_efi_fmt->efi_extents[i],
|
|
|
|
&src_efi_fmt->efi_extents[i],
|
|
|
|
sizeof(struct xfs_extent));
|
2006-06-09 04:55:38 +00:00
|
|
|
return 0;
|
|
|
|
} else if (buf->i_len == len32) {
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_log_format_32_t *src_efi_fmt_32 = buf->i_addr;
|
2006-06-09 04:55:38 +00:00
|
|
|
|
|
|
|
dst_efi_fmt->efi_type = src_efi_fmt_32->efi_type;
|
|
|
|
dst_efi_fmt->efi_size = src_efi_fmt_32->efi_size;
|
|
|
|
dst_efi_fmt->efi_nextents = src_efi_fmt_32->efi_nextents;
|
|
|
|
dst_efi_fmt->efi_id = src_efi_fmt_32->efi_id;
|
|
|
|
for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
|
|
|
|
dst_efi_fmt->efi_extents[i].ext_start =
|
|
|
|
src_efi_fmt_32->efi_extents[i].ext_start;
|
|
|
|
dst_efi_fmt->efi_extents[i].ext_len =
|
|
|
|
src_efi_fmt_32->efi_extents[i].ext_len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
} else if (buf->i_len == len64) {
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efi_log_format_64_t *src_efi_fmt_64 = buf->i_addr;
|
2006-06-09 04:55:38 +00:00
|
|
|
|
|
|
|
dst_efi_fmt->efi_type = src_efi_fmt_64->efi_type;
|
|
|
|
dst_efi_fmt->efi_size = src_efi_fmt_64->efi_size;
|
|
|
|
dst_efi_fmt->efi_nextents = src_efi_fmt_64->efi_nextents;
|
|
|
|
dst_efi_fmt->efi_id = src_efi_fmt_64->efi_id;
|
|
|
|
for (i = 0; i < dst_efi_fmt->efi_nextents; i++) {
|
|
|
|
dst_efi_fmt->efi_extents[i].ext_start =
|
|
|
|
src_efi_fmt_64->efi_extents[i].ext_start;
|
|
|
|
dst_efi_fmt->efi_extents[i].ext_len =
|
|
|
|
src_efi_fmt_64->efi_extents[i].ext_len;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
2022-10-25 22:07:14 +00:00
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, NULL, buf->i_addr,
|
|
|
|
buf->i_len);
|
2014-06-25 04:58:08 +00:00
|
|
|
return -EFSCORRUPTED;
|
2006-06-09 04:55:38 +00:00
|
|
|
}
|
|
|
|
|
2010-06-23 08:11:15 +00:00
|
|
|
static inline struct xfs_efd_log_item *EFD_ITEM(struct xfs_log_item *lip)
|
2005-06-21 05:41:19 +00:00
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
return container_of(lip, struct xfs_efd_log_item, efd_item);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-06-23 08:11:15 +00:00
|
|
|
STATIC void
|
|
|
|
xfs_efd_item_free(struct xfs_efd_log_item *efdp)
|
|
|
|
{
|
2024-01-15 22:59:42 +00:00
|
|
|
kvfree(efdp->efd_item.li_lv_shadow);
|
2010-06-23 08:11:15 +00:00
|
|
|
if (efdp->efd_format.efd_nextents > XFS_EFD_MAX_FAST_EXTENTS)
|
2024-01-15 22:59:43 +00:00
|
|
|
kfree(efdp);
|
2010-06-23 08:11:15 +00:00
|
|
|
else
|
2021-10-12 18:09:23 +00:00
|
|
|
kmem_cache_free(xfs_efd_cache, efdp);
|
2005-06-21 05:41:19 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2013-08-12 10:50:04 +00:00
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efd_item_size(
|
2013-08-12 10:50:04 +00:00
|
|
|
struct xfs_log_item *lip,
|
|
|
|
int *nvecs,
|
|
|
|
int *nbytes)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2022-10-21 16:10:05 +00:00
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(lip);
|
|
|
|
|
2013-08-12 10:50:04 +00:00
|
|
|
*nvecs += 1;
|
2022-10-21 16:10:05 +00:00
|
|
|
*nbytes += xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This is called to fill in the vector of log iovecs for the
|
|
|
|
* given efd log item. We use only 1 iovec, and we point that
|
|
|
|
* at the efd_log_format structure embedded in the efd item.
|
|
|
|
* It is at this point that we assert that all of the extent
|
|
|
|
* slots in the efd item have been filled.
|
|
|
|
*/
|
|
|
|
STATIC void
|
2010-06-23 08:11:15 +00:00
|
|
|
xfs_efd_item_format(
|
|
|
|
struct xfs_log_item *lip,
|
2013-12-13 00:34:02 +00:00
|
|
|
struct xfs_log_vec *lv)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(lip);
|
2013-12-13 00:34:02 +00:00
|
|
|
struct xfs_log_iovec *vecp = NULL;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
ASSERT(efdp->efd_next_extent == efdp->efd_format.efd_nextents);
|
|
|
|
|
|
|
|
efdp->efd_format.efd_type = XFS_LI_EFD;
|
|
|
|
efdp->efd_format.efd_size = 1;
|
|
|
|
|
2013-12-13 00:34:02 +00:00
|
|
|
xlog_copy_iovec(lv, &vecp, XLOG_REG_TYPE_EFD_FORMAT,
|
2013-12-13 00:00:43 +00:00
|
|
|
&efdp->efd_format,
|
2022-10-21 16:10:05 +00:00
|
|
|
xfs_efd_log_format_sizeof(efdp->efd_format.efd_nextents));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
xfs: fix efi/efd error handling to avoid fs shutdown hangs
Freeing an extent in XFS involves logging an EFI (extent free
intention), freeing the actual extent, and logging an EFD (extent
free done). The EFI object is created with a reference count of 2:
one for the current transaction and one for the subsequently created
EFD. Under normal circumstances, the first reference is dropped when
the EFI is unpinned and the second reference is dropped when the EFD
is committed to the on-disk log.
In event of errors or filesystem shutdown, there are various
potential cleanup scenarios depending on the state of the EFI/EFD.
The cleanup scenarios are confusing and racy, as demonstrated by the
following test sequence:
# mount $dev $mnt
# fsstress -d $mnt -n 99999 -p 16 -z -f fallocate=1 \
-f punch=1 -f creat=1 -f unlink=1 &
# sleep 5
# killall -9 fsstress; wait
# godown -f $mnt
# umount
... in which the final umount can hang due to the AIL being pinned
indefinitely by one or more EFI items. This can occur due to several
conditions. For example, if the shutdown occurs after the EFI is
committed to the on-disk log and the EFD committed to the CIL, but
before the EFD committed to the log, the EFD iop_committed() abort
handler does not drop its reference to the EFI. Alternatively,
manual error injection in the xfs_bmap_finish() codepath shows that
if an error occurs after the EFI transaction is committed but before
the EFD is constructed and logged, the EFI is never released from
the AIL.
Update the EFI/EFD item handling code to use a more straightforward
and reliable approach to error handling. If an error occurs after
the EFI transaction is committed and before the EFD is constructed,
release the EFI explicitly from xfs_bmap_finish(). If the EFI
transaction is cancelled, release the EFI in the unlock handler.
Once the EFD is constructed, it is responsible for releasing the EFI
under any circumstances (including whether the EFI item aborts due
to log I/O error). Update the EFD item handlers to release the EFI
if the transaction is cancelled or aborts due to log I/O error.
Finally, update xfs_bmap_finish() to log at least one EFD extent to
the transaction before xfs_free_extent() errors are handled to
ensure the transaction is dirty and EFD item error handling is
triggered.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-18 23:51:16 +00:00
|
|
|
/*
|
|
|
|
* The EFD is either committed or aborted if the transaction is cancelled. If
|
|
|
|
* the transaction is cancelled, drop our reference to the EFI and free the EFD.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
STATIC void
|
2019-06-29 02:27:32 +00:00
|
|
|
xfs_efd_item_release(
|
2010-06-23 08:11:15 +00:00
|
|
|
struct xfs_log_item *lip)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
xfs: fix efi/efd error handling to avoid fs shutdown hangs
Freeing an extent in XFS involves logging an EFI (extent free
intention), freeing the actual extent, and logging an EFD (extent
free done). The EFI object is created with a reference count of 2:
one for the current transaction and one for the subsequently created
EFD. Under normal circumstances, the first reference is dropped when
the EFI is unpinned and the second reference is dropped when the EFD
is committed to the on-disk log.
In event of errors or filesystem shutdown, there are various
potential cleanup scenarios depending on the state of the EFI/EFD.
The cleanup scenarios are confusing and racy, as demonstrated by the
following test sequence:
# mount $dev $mnt
# fsstress -d $mnt -n 99999 -p 16 -z -f fallocate=1 \
-f punch=1 -f creat=1 -f unlink=1 &
# sleep 5
# killall -9 fsstress; wait
# godown -f $mnt
# umount
... in which the final umount can hang due to the AIL being pinned
indefinitely by one or more EFI items. This can occur due to several
conditions. For example, if the shutdown occurs after the EFI is
committed to the on-disk log and the EFD committed to the CIL, but
before the EFD committed to the log, the EFD iop_committed() abort
handler does not drop its reference to the EFI. Alternatively,
manual error injection in the xfs_bmap_finish() codepath shows that
if an error occurs after the EFI transaction is committed but before
the EFD is constructed and logged, the EFI is never released from
the AIL.
Update the EFI/EFD item handling code to use a more straightforward
and reliable approach to error handling. If an error occurs after
the EFI transaction is committed and before the EFD is constructed,
release the EFI explicitly from xfs_bmap_finish(). If the EFI
transaction is cancelled, release the EFI in the unlock handler.
Once the EFD is constructed, it is responsible for releasing the EFI
under any circumstances (including whether the EFI item aborts due
to log I/O error). Update the EFD item handlers to release the EFI
if the transaction is cancelled or aborts due to log I/O error.
Finally, update xfs_bmap_finish() to log at least one EFD extent to
the transaction before xfs_free_extent() errors are handled to
ensure the transaction is dirty and EFD item error handling is
triggered.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2015-08-18 23:51:16 +00:00
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(lip);
|
|
|
|
|
2019-06-29 02:27:32 +00:00
|
|
|
xfs_efi_release(efdp->efd_efip);
|
|
|
|
xfs_efd_item_free(efdp);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2022-05-04 01:46:39 +00:00
|
|
|
static struct xfs_log_item *
|
|
|
|
xfs_efd_item_intent(
|
|
|
|
struct xfs_log_item *lip)
|
|
|
|
{
|
|
|
|
return &EFD_ITEM(lip)->efd_efip->efi_item;
|
|
|
|
}
|
|
|
|
|
2011-10-28 09:54:24 +00:00
|
|
|
static const struct xfs_item_ops xfs_efd_item_ops = {
|
2022-05-04 01:46:09 +00:00
|
|
|
.flags = XFS_ITEM_RELEASE_WHEN_COMMITTED |
|
|
|
|
XFS_ITEM_INTENT_DONE,
|
2010-06-23 08:11:15 +00:00
|
|
|
.iop_size = xfs_efd_item_size,
|
|
|
|
.iop_format = xfs_efd_item_format,
|
2019-06-29 02:27:32 +00:00
|
|
|
.iop_release = xfs_efd_item_release,
|
2022-05-04 01:46:39 +00:00
|
|
|
.iop_intent = xfs_efd_item_intent,
|
2005-04-16 22:20:36 +00:00
|
|
|
};
|
|
|
|
|
2023-06-28 18:04:33 +00:00
|
|
|
/*
|
|
|
|
* Fill the EFD with all extents from the EFI when we need to roll the
|
|
|
|
* transaction and continue with a new EFI.
|
|
|
|
*
|
|
|
|
* This simply copies all the extents in the EFI to the EFD rather than make
|
|
|
|
* assumptions about which extents in the EFI have already been processed. We
|
|
|
|
* currently keep the xefi list in the same order as the EFI extent list, but
|
|
|
|
* that may not always be the case. Copying everything avoids leaving a landmine
|
|
|
|
* were we fail to cancel all the extents in an EFI if the xefi list is
|
|
|
|
* processed in a different order to the extents in the EFI.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xfs_efd_from_efi(
|
|
|
|
struct xfs_efd_log_item *efdp)
|
|
|
|
{
|
|
|
|
struct xfs_efi_log_item *efip = efdp->efd_efip;
|
|
|
|
uint i;
|
|
|
|
|
|
|
|
ASSERT(efip->efi_format.efi_nextents > 0);
|
|
|
|
ASSERT(efdp->efd_next_extent < efip->efi_format.efi_nextents);
|
|
|
|
|
|
|
|
for (i = 0; i < efip->efi_format.efi_nextents; i++) {
|
|
|
|
efdp->efd_format.efd_extents[i] =
|
|
|
|
efip->efi_format.efi_extents[i];
|
|
|
|
}
|
|
|
|
efdp->efd_next_extent = efip->efi_format.efi_nextents;
|
|
|
|
}
|
|
|
|
|
2019-06-29 02:28:17 +00:00
|
|
|
/* Sort bmap items by AG. */
|
|
|
|
static int
|
|
|
|
xfs_extent_free_diff_items(
|
|
|
|
void *priv,
|
2021-04-08 18:28:34 +00:00
|
|
|
const struct list_head *a,
|
|
|
|
const struct list_head *b)
|
2019-06-29 02:28:17 +00:00
|
|
|
{
|
|
|
|
struct xfs_extent_free_item *ra;
|
|
|
|
struct xfs_extent_free_item *rb;
|
|
|
|
|
|
|
|
ra = container_of(a, struct xfs_extent_free_item, xefi_list);
|
|
|
|
rb = container_of(b, struct xfs_extent_free_item, xefi_list);
|
2023-04-12 01:59:54 +00:00
|
|
|
|
|
|
|
return ra->xefi_pag->pag_agno - rb->xefi_pag->pag_agno;
|
2019-06-29 02:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Log a free extent to the intent item. */
|
|
|
|
STATIC void
|
|
|
|
xfs_extent_free_log_item(
|
|
|
|
struct xfs_trans *tp,
|
2020-04-30 19:52:20 +00:00
|
|
|
struct xfs_efi_log_item *efip,
|
2023-02-01 18:16:02 +00:00
|
|
|
struct xfs_extent_free_item *xefi)
|
2019-06-29 02:28:17 +00:00
|
|
|
{
|
|
|
|
uint next_extent;
|
|
|
|
struct xfs_extent *extp;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* atomic_inc_return gives us the value after the increment;
|
|
|
|
* we want to use it as an array index so we need to subtract 1 from
|
|
|
|
* it.
|
|
|
|
*/
|
|
|
|
next_extent = atomic_inc_return(&efip->efi_next_extent) - 1;
|
|
|
|
ASSERT(next_extent < efip->efi_format.efi_nextents);
|
|
|
|
extp = &efip->efi_format.efi_extents[next_extent];
|
2023-02-01 18:16:02 +00:00
|
|
|
extp->ext_start = xefi->xefi_startblock;
|
|
|
|
extp->ext_len = xefi->xefi_blockcount;
|
2019-06-29 02:28:17 +00:00
|
|
|
}
|
|
|
|
|
2020-04-30 19:52:21 +00:00
|
|
|
static struct xfs_log_item *
|
2020-04-30 19:52:20 +00:00
|
|
|
xfs_extent_free_create_intent(
|
|
|
|
struct xfs_trans *tp,
|
|
|
|
struct list_head *items,
|
2020-04-30 19:52:20 +00:00
|
|
|
unsigned int count,
|
|
|
|
bool sort)
|
2020-04-30 19:52:20 +00:00
|
|
|
{
|
|
|
|
struct xfs_mount *mp = tp->t_mountp;
|
|
|
|
struct xfs_efi_log_item *efip = xfs_efi_init(mp, count);
|
2023-02-01 18:16:02 +00:00
|
|
|
struct xfs_extent_free_item *xefi;
|
2020-04-30 19:52:20 +00:00
|
|
|
|
|
|
|
ASSERT(count > 0);
|
|
|
|
|
2020-04-30 19:52:20 +00:00
|
|
|
if (sort)
|
|
|
|
list_sort(mp, items, xfs_extent_free_diff_items);
|
2023-02-01 18:16:02 +00:00
|
|
|
list_for_each_entry(xefi, items, xefi_list)
|
|
|
|
xfs_extent_free_log_item(tp, efip, xefi);
|
2020-04-30 19:52:21 +00:00
|
|
|
return &efip->efi_item;
|
2020-04-30 19:52:20 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 02:28:17 +00:00
|
|
|
/* Get an EFD so we can process all the free extents. */
|
2020-04-30 19:52:22 +00:00
|
|
|
static struct xfs_log_item *
|
2019-06-29 02:28:17 +00:00
|
|
|
xfs_extent_free_create_done(
|
|
|
|
struct xfs_trans *tp,
|
2020-04-30 19:52:21 +00:00
|
|
|
struct xfs_log_item *intent,
|
2019-06-29 02:28:17 +00:00
|
|
|
unsigned int count)
|
|
|
|
{
|
2023-11-30 20:17:37 +00:00
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(intent);
|
|
|
|
struct xfs_efd_log_item *efdp;
|
|
|
|
|
|
|
|
ASSERT(count > 0);
|
|
|
|
|
|
|
|
if (count > XFS_EFD_MAX_FAST_EXTENTS) {
|
|
|
|
efdp = kzalloc(xfs_efd_log_item_sizeof(count),
|
|
|
|
GFP_KERNEL | __GFP_NOFAIL);
|
|
|
|
} else {
|
|
|
|
efdp = kmem_cache_zalloc(xfs_efd_cache,
|
|
|
|
GFP_KERNEL | __GFP_NOFAIL);
|
|
|
|
}
|
|
|
|
|
|
|
|
xfs_log_item_init(tp->t_mountp, &efdp->efd_item, XFS_LI_EFD,
|
|
|
|
&xfs_efd_item_ops);
|
|
|
|
efdp->efd_efip = efip;
|
|
|
|
efdp->efd_format.efd_nextents = count;
|
|
|
|
efdp->efd_format.efd_efi_id = efip->efi_format.efi_id;
|
|
|
|
|
|
|
|
return &efdp->efd_item;
|
2019-06-29 02:28:17 +00:00
|
|
|
}
|
|
|
|
|
2023-04-12 01:59:54 +00:00
|
|
|
/* Take a passive ref to the AG containing the space we're freeing. */
|
|
|
|
void
|
|
|
|
xfs_extent_free_get_group(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_extent_free_item *xefi)
|
|
|
|
{
|
|
|
|
xfs_agnumber_t agno;
|
|
|
|
|
|
|
|
agno = XFS_FSB_TO_AGNO(mp, xefi->xefi_startblock);
|
xfs: allow queued AG intents to drain before scrubbing
When a writer thread executes a chain of log intent items, the AG header
buffer locks will cycle during a transaction roll to get from one intent
item to the next in a chain. Although scrub takes all AG header buffer
locks, this isn't sufficient to guard against scrub checking an AG while
that writer thread is in the middle of finishing a chain because there's
no higher level locking primitive guarding allocation groups.
When there's a collision, cross-referencing between data structures
(e.g. rmapbt and refcountbt) yields false corruption events; if repair
is running, this results in incorrect repairs, which is catastrophic.
Fix this by adding to the perag structure the count of active intents
and make scrub wait until it has both AG header buffer locks and the
intent counter reaches zero.
One quirk of the drain code is that deferred bmap updates also bump and
drop the intent counter. A fundamental decision made during the design
phase of the reverse mapping feature is that updates to the rmapbt
records are always made by the same code that updates the primary
metadata. In other words, callers of bmapi functions expect that the
bmapi functions will queue deferred rmap updates.
Some parts of the reflink code queue deferred refcount (CUI) and bmap
(BUI) updates in the same head transaction, but the deferred work
manager completely finishes the CUI before the BUI work is started. As
a result, the CUI drops the intent count long before the deferred rmap
(RUI) update even has a chance to bump the intent count. The only way
to keep the intent count elevated between the CUI and RUI is for the BUI
to bump the counter until the RUI has been created.
A second quirk of the intent drain code is that deferred work items must
increment the intent counter as soon as the work item is added to the
transaction. When a BUI completes and queues an RUI, the RUI must
increment the counter before the BUI decrements it. The only way to
accomplish this is to require that the counter be bumped as soon as the
deferred work item is created in memory.
In the next patches we'll improve on this facility, but this patch
provides the basic functionality.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 01:59:58 +00:00
|
|
|
xefi->xefi_pag = xfs_perag_intent_get(mp, agno);
|
2023-04-12 01:59:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Release a passive AG ref after some freeing work. */
|
|
|
|
static inline void
|
|
|
|
xfs_extent_free_put_group(
|
|
|
|
struct xfs_extent_free_item *xefi)
|
|
|
|
{
|
xfs: allow queued AG intents to drain before scrubbing
When a writer thread executes a chain of log intent items, the AG header
buffer locks will cycle during a transaction roll to get from one intent
item to the next in a chain. Although scrub takes all AG header buffer
locks, this isn't sufficient to guard against scrub checking an AG while
that writer thread is in the middle of finishing a chain because there's
no higher level locking primitive guarding allocation groups.
When there's a collision, cross-referencing between data structures
(e.g. rmapbt and refcountbt) yields false corruption events; if repair
is running, this results in incorrect repairs, which is catastrophic.
Fix this by adding to the perag structure the count of active intents
and make scrub wait until it has both AG header buffer locks and the
intent counter reaches zero.
One quirk of the drain code is that deferred bmap updates also bump and
drop the intent counter. A fundamental decision made during the design
phase of the reverse mapping feature is that updates to the rmapbt
records are always made by the same code that updates the primary
metadata. In other words, callers of bmapi functions expect that the
bmapi functions will queue deferred rmap updates.
Some parts of the reflink code queue deferred refcount (CUI) and bmap
(BUI) updates in the same head transaction, but the deferred work
manager completely finishes the CUI before the BUI work is started. As
a result, the CUI drops the intent count long before the deferred rmap
(RUI) update even has a chance to bump the intent count. The only way
to keep the intent count elevated between the CUI and RUI is for the BUI
to bump the counter until the RUI has been created.
A second quirk of the intent drain code is that deferred work items must
increment the intent counter as soon as the work item is added to the
transaction. When a BUI completes and queues an RUI, the RUI must
increment the counter before the BUI decrements it. The only way to
accomplish this is to require that the counter be bumped as soon as the
deferred work item is created in memory.
In the next patches we'll improve on this facility, but this patch
provides the basic functionality.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-04-12 01:59:58 +00:00
|
|
|
xfs_perag_intent_put(xefi->xefi_pag);
|
2023-04-12 01:59:54 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 02:28:17 +00:00
|
|
|
/* Process a free extent. */
|
|
|
|
STATIC int
|
|
|
|
xfs_extent_free_finish_item(
|
|
|
|
struct xfs_trans *tp,
|
2020-04-30 19:52:22 +00:00
|
|
|
struct xfs_log_item *done,
|
2019-06-29 02:28:17 +00:00
|
|
|
struct list_head *item,
|
2020-04-30 19:52:22 +00:00
|
|
|
struct xfs_btree_cur **state)
|
2019-06-29 02:28:17 +00:00
|
|
|
{
|
2023-11-30 18:23:19 +00:00
|
|
|
struct xfs_owner_info oinfo = { };
|
2023-02-01 18:16:02 +00:00
|
|
|
struct xfs_extent_free_item *xefi;
|
2023-11-30 18:23:19 +00:00
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(done);
|
|
|
|
struct xfs_mount *mp = tp->t_mountp;
|
|
|
|
struct xfs_extent *extp;
|
|
|
|
uint next_extent;
|
|
|
|
xfs_agblock_t agbno;
|
xfs: automatic freeing of freshly allocated unwritten space
As mentioned in the previous commit, online repair wants to allocate
space to write out a new metadata structure, and it also wants to hedge
against system crashes during repairs by logging (and later cancelling)
EFIs to free the space if we crash before committing the new data
structure.
Therefore, create a trio of functions to schedule automatic reaping of
freshly allocated unwritten space. xfs_alloc_schedule_autoreap creates
a paused EFI representing the space we just allocated. Once the
allocations are made and the autoreaps scheduled, we can start writing
to disk.
If the writes succeed, xfs_alloc_cancel_autoreap marks the EFI work
items as stale and unpauses the pending deferred work item. Assuming
that's done in the same transaction that commits the new structure into
the filesystem, we guarantee that either the new object is fully
visible, or that all the space gets reclaimed.
If the writes succeed but only part of an extent was used, repair must
call the same _cancel_autoreap function to kill the first EFI and then
log a new EFI to free the unused space. The first EFI is already
committed, so it cannot be changed.
For full extents that aren't used, xfs_alloc_commit_autoreap will
unpause the EFI, which results in the space being freed during the next
_defer_finish cycle.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
2023-12-07 02:40:57 +00:00
|
|
|
int error = 0;
|
2019-06-29 02:28:17 +00:00
|
|
|
|
2023-02-01 18:16:02 +00:00
|
|
|
xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
|
2023-11-30 18:23:19 +00:00
|
|
|
agbno = XFS_FSB_TO_AGBNO(mp, xefi->xefi_startblock);
|
2023-02-01 18:16:02 +00:00
|
|
|
|
2023-11-30 18:23:19 +00:00
|
|
|
oinfo.oi_owner = xefi->xefi_owner;
|
|
|
|
if (xefi->xefi_flags & XFS_EFI_ATTR_FORK)
|
|
|
|
oinfo.oi_flags |= XFS_OWNER_INFO_ATTR_FORK;
|
|
|
|
if (xefi->xefi_flags & XFS_EFI_BMBT_BLOCK)
|
|
|
|
oinfo.oi_flags |= XFS_OWNER_INFO_BMBT_BLOCK;
|
|
|
|
|
|
|
|
trace_xfs_bmap_free_deferred(tp->t_mountp, xefi->xefi_pag->pag_agno, 0,
|
|
|
|
agbno, xefi->xefi_blockcount);
|
2023-04-12 01:59:54 +00:00
|
|
|
|
2023-06-28 18:04:33 +00:00
|
|
|
/*
|
2023-11-30 18:23:19 +00:00
|
|
|
* If we need a new transaction to make progress, the caller will log a
|
|
|
|
* new EFI with the current contents. It will also log an EFD to cancel
|
|
|
|
* the existing EFI, and so we need to copy all the unprocessed extents
|
|
|
|
* in this EFI to the EFD so this works correctly.
|
2023-06-28 18:04:33 +00:00
|
|
|
*/
|
xfs: automatic freeing of freshly allocated unwritten space
As mentioned in the previous commit, online repair wants to allocate
space to write out a new metadata structure, and it also wants to hedge
against system crashes during repairs by logging (and later cancelling)
EFIs to free the space if we crash before committing the new data
structure.
Therefore, create a trio of functions to schedule automatic reaping of
freshly allocated unwritten space. xfs_alloc_schedule_autoreap creates
a paused EFI representing the space we just allocated. Once the
allocations are made and the autoreaps scheduled, we can start writing
to disk.
If the writes succeed, xfs_alloc_cancel_autoreap marks the EFI work
items as stale and unpauses the pending deferred work item. Assuming
that's done in the same transaction that commits the new structure into
the filesystem, we guarantee that either the new object is fully
visible, or that all the space gets reclaimed.
If the writes succeed but only part of an extent was used, repair must
call the same _cancel_autoreap function to kill the first EFI and then
log a new EFI to free the unused space. The first EFI is already
committed, so it cannot be changed.
For full extents that aren't used, xfs_alloc_commit_autoreap will
unpause the EFI, which results in the space being freed during the next
_defer_finish cycle.
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
2023-12-07 02:40:57 +00:00
|
|
|
if (!(xefi->xefi_flags & XFS_EFI_CANCELLED))
|
|
|
|
error = __xfs_free_extent(tp, xefi->xefi_pag, agbno,
|
|
|
|
xefi->xefi_blockcount, &oinfo, xefi->xefi_agresv,
|
|
|
|
xefi->xefi_flags & XFS_EFI_SKIP_DISCARD);
|
2023-11-30 18:23:19 +00:00
|
|
|
if (error == -EAGAIN) {
|
|
|
|
xfs_efd_from_efi(efdp);
|
2023-06-28 18:04:33 +00:00
|
|
|
return error;
|
2023-11-30 18:23:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Add the work we finished to the EFD, even though nobody uses that */
|
|
|
|
next_extent = efdp->efd_next_extent;
|
|
|
|
ASSERT(next_extent < efdp->efd_format.efd_nextents);
|
|
|
|
extp = &(efdp->efd_format.efd_extents[next_extent]);
|
|
|
|
extp->ext_start = xefi->xefi_startblock;
|
|
|
|
extp->ext_len = xefi->xefi_blockcount;
|
|
|
|
efdp->efd_next_extent++;
|
2023-06-28 18:04:33 +00:00
|
|
|
|
2023-04-12 01:59:54 +00:00
|
|
|
xfs_extent_free_put_group(xefi);
|
2023-02-01 18:16:02 +00:00
|
|
|
kmem_cache_free(xfs_extfree_item_cache, xefi);
|
2019-06-29 02:28:17 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Abort all pending EFIs. */
|
|
|
|
STATIC void
|
|
|
|
xfs_extent_free_abort_intent(
|
2020-04-30 19:52:21 +00:00
|
|
|
struct xfs_log_item *intent)
|
2019-06-29 02:28:17 +00:00
|
|
|
{
|
2020-04-30 19:52:21 +00:00
|
|
|
xfs_efi_release(EFI_ITEM(intent));
|
2019-06-29 02:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Cancel a free extent. */
|
|
|
|
STATIC void
|
|
|
|
xfs_extent_free_cancel_item(
|
|
|
|
struct list_head *item)
|
|
|
|
{
|
2023-02-01 18:16:02 +00:00
|
|
|
struct xfs_extent_free_item *xefi;
|
2019-06-29 02:28:17 +00:00
|
|
|
|
2023-02-01 18:16:02 +00:00
|
|
|
xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
|
2023-04-12 01:59:54 +00:00
|
|
|
|
|
|
|
xfs_extent_free_put_group(xefi);
|
2023-02-01 18:16:02 +00:00
|
|
|
kmem_cache_free(xfs_extfree_item_cache, xefi);
|
2019-06-29 02:28:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* AGFL blocks are accounted differently in the reserve pools and are not
|
|
|
|
* inserted into the busy extent list.
|
|
|
|
*/
|
|
|
|
STATIC int
|
|
|
|
xfs_agfl_free_finish_item(
|
|
|
|
struct xfs_trans *tp,
|
2020-04-30 19:52:22 +00:00
|
|
|
struct xfs_log_item *done,
|
2019-06-29 02:28:17 +00:00
|
|
|
struct list_head *item,
|
2020-04-30 19:52:22 +00:00
|
|
|
struct xfs_btree_cur **state)
|
2019-06-29 02:28:17 +00:00
|
|
|
{
|
2021-10-12 22:55:54 +00:00
|
|
|
struct xfs_owner_info oinfo = { };
|
2019-06-29 02:28:17 +00:00
|
|
|
struct xfs_mount *mp = tp->t_mountp;
|
2020-04-30 19:52:22 +00:00
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(done);
|
2023-02-01 18:16:02 +00:00
|
|
|
struct xfs_extent_free_item *xefi;
|
2019-06-29 02:28:17 +00:00
|
|
|
struct xfs_extent *extp;
|
|
|
|
struct xfs_buf *agbp;
|
|
|
|
int error;
|
|
|
|
xfs_agblock_t agbno;
|
|
|
|
uint next_extent;
|
|
|
|
|
2023-02-01 18:16:02 +00:00
|
|
|
xefi = container_of(item, struct xfs_extent_free_item, xefi_list);
|
|
|
|
ASSERT(xefi->xefi_blockcount == 1);
|
|
|
|
agbno = XFS_FSB_TO_AGBNO(mp, xefi->xefi_startblock);
|
|
|
|
oinfo.oi_owner = xefi->xefi_owner;
|
2019-06-29 02:28:17 +00:00
|
|
|
|
2023-04-12 01:59:54 +00:00
|
|
|
trace_xfs_agfl_free_deferred(mp, xefi->xefi_pag->pag_agno, 0, agbno,
|
|
|
|
xefi->xefi_blockcount);
|
2019-06-29 02:28:17 +00:00
|
|
|
|
2023-04-12 01:59:54 +00:00
|
|
|
error = xfs_alloc_read_agf(xefi->xefi_pag, tp, 0, &agbp);
|
2019-06-29 02:28:17 +00:00
|
|
|
if (!error)
|
2023-04-12 01:59:54 +00:00
|
|
|
error = xfs_free_agfl_block(tp, xefi->xefi_pag->pag_agno,
|
|
|
|
agbno, agbp, &oinfo);
|
2019-06-29 02:28:17 +00:00
|
|
|
|
|
|
|
next_extent = efdp->efd_next_extent;
|
|
|
|
ASSERT(next_extent < efdp->efd_format.efd_nextents);
|
|
|
|
extp = &(efdp->efd_format.efd_extents[next_extent]);
|
2023-02-01 18:16:02 +00:00
|
|
|
extp->ext_start = xefi->xefi_startblock;
|
|
|
|
extp->ext_len = xefi->xefi_blockcount;
|
2019-06-29 02:28:17 +00:00
|
|
|
efdp->efd_next_extent++;
|
|
|
|
|
2023-04-12 01:59:54 +00:00
|
|
|
xfs_extent_free_put_group(xefi);
|
2023-02-01 18:16:02 +00:00
|
|
|
kmem_cache_free(xfs_extfree_item_cache, xefi);
|
2019-06-29 02:28:17 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2020-11-30 00:33:38 +00:00
|
|
|
/* Is this recovered EFI ok? */
|
|
|
|
static inline bool
|
|
|
|
xfs_efi_validate_ext(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_extent *extp)
|
|
|
|
{
|
2020-12-04 21:20:00 +00:00
|
|
|
return xfs_verify_fsbext(mp, extp->ext_start, extp->ext_len);
|
2020-11-30 00:33:38 +00:00
|
|
|
}
|
|
|
|
|
2023-11-22 19:13:03 +00:00
|
|
|
static inline void
|
|
|
|
xfs_efi_recover_work(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_defer_pending *dfp,
|
|
|
|
struct xfs_extent *extp)
|
|
|
|
{
|
|
|
|
struct xfs_extent_free_item *xefi;
|
|
|
|
|
|
|
|
xefi = kmem_cache_zalloc(xfs_extfree_item_cache,
|
|
|
|
GFP_KERNEL | __GFP_NOFAIL);
|
|
|
|
xefi->xefi_startblock = extp->ext_start;
|
|
|
|
xefi->xefi_blockcount = extp->ext_len;
|
|
|
|
xefi->xefi_agresv = XFS_AG_RESV_NONE;
|
|
|
|
xefi->xefi_owner = XFS_RMAP_OWN_UNKNOWN;
|
2023-11-22 19:25:45 +00:00
|
|
|
xfs_extent_free_get_group(mp, xefi);
|
2023-11-22 19:13:03 +00:00
|
|
|
|
|
|
|
xfs_defer_add_item(dfp, &xefi->xefi_list);
|
|
|
|
}
|
|
|
|
|
2016-08-03 01:23:49 +00:00
|
|
|
/*
|
|
|
|
* Process an extent free intent item that was recovered from
|
|
|
|
* the log. We need to free the extents that it describes.
|
|
|
|
*/
|
2020-05-01 23:00:50 +00:00
|
|
|
STATIC int
|
2023-11-22 21:39:25 +00:00
|
|
|
xfs_extent_free_recover_work(
|
2023-11-22 18:38:10 +00:00
|
|
|
struct xfs_defer_pending *dfp,
|
xfs: proper replay of deferred ops queued during log recovery
When we replay unfinished intent items that have been recovered from the
log, it's possible that the replay will cause the creation of more
deferred work items. As outlined in commit 509955823cc9c ("xfs: log
recovery should replay deferred ops in order"), later work items have an
implicit ordering dependency on earlier work items. Therefore, recovery
must replay the items (both recovered and created) in the same order
that they would have been during normal operation.
For log recovery, we enforce this ordering by using an empty transaction
to collect deferred ops that get created in the process of recovering a
log intent item to prevent them from being committed before the rest of
the recovered intent items. After we finish committing all the
recovered log items, we allocate a transaction with an enormous block
reservation, splice our huge list of created deferred ops into that
transaction, and commit it, thereby finishing all those ops.
This is /really/ hokey -- it's the one place in XFS where we allow
nested transactions; the splicing of the defer ops list is is inelegant
and has to be done twice per recovery function; and the broken way we
handle inode pointers and block reservations cause subtle use-after-free
and allocator problems that will be fixed by this patch and the two
patches after it.
Therefore, replace the hokey empty transaction with a structure designed
to capture each chain of deferred ops that are created as part of
recovering a single unfinished log intent. Finally, refactor the loop
that replays those chains to do so using one transaction per chain.
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
2020-09-26 00:39:37 +00:00
|
|
|
struct list_head *capture_list)
|
2016-08-03 01:23:49 +00:00
|
|
|
{
|
xfs: reserve less log space when recovering log intent items
Wengang Wang reports that a customer's system was running a number of
truncate operations on a filesystem with a very small log. Contention
on the reserve heads lead to other threads stalling on smaller updates
(e.g. mtime updates) long enough to result in the node being rebooted
on account of the lack of responsivenes. The node failed to recover
because log recovery of an EFI became stuck waiting for a grant of
reserve space. From Wengang's report:
"For the file deletion, log bytes are reserved basing on
xfs_mount->tr_itruncate which is:
tr_logres = 175488,
tr_logcount = 2,
tr_logflags = XFS_TRANS_PERM_LOG_RES,
"You see it's a permanent log reservation with two log operations (two
transactions in rolling mode). After calculation (xlog_calc_unit_res()
adds space for various log headers), the final log space needed per
transaction changes from 175488 to 180208 bytes. So the total log
space needed is 360416 bytes (180208 * 2). [That quantity] of log space
(360416 bytes) needs to be reserved for both run time inode removing
(xfs_inactive_truncate()) and EFI recover (xfs_efi_item_recover())."
In other words, runtime pre-reserves 360K of space in anticipation of
running a chain of two transactions in which each transaction gets a
180K reservation.
Now that we've allocated the transaction, we delete the bmap mapping,
log an EFI to free the space, and roll the transaction as part of
finishing the deferops chain. Rolling creates a new xfs_trans which
shares its ticket with the old transaction. Next, xfs_trans_roll calls
__xfs_trans_commit with regrant == true, which calls xlog_cil_commit
with the same regrant parameter.
xlog_cil_commit calls xfs_log_ticket_regrant, which decrements t_cnt and
subtracts t_curr_res from the reservation and write heads.
If the filesystem is fresh and the first transaction only used (say)
20K, then t_curr_res will be 160K, and we give that much reservation
back to the reservation head. Or if the file is really fragmented and
the first transaction actually uses 170K, then t_curr_res will be 10K,
and that's what we give back to the reservation.
Having done that, we're now headed into the second transaction with an
EFI and 180K of reservation. Other threads apparently consumed all the
reservation for smaller transactions, such as timestamp updates.
Now let's say the first transaction gets written to disk and we crash
without ever completing the second transaction. Now we remount the fs,
log recovery finds the unfinished EFI, and calls xfs_efi_recover to
finish the EFI. However, xfs_efi_recover starts a new tr_itruncate
tranasction, which asks for 360K log reservation. This is a lot more
than the 180K that we had reserved at the time of the crash. If the
first EFI to be recovered is also pinning the tail of the log, we will
be unable to free any space in the log, and recovery livelocks.
Wengang confirmed this:
"Now we have the second transaction which has 180208 log bytes reserved
too. The second transaction is supposed to process intents including
extent freeing. With my hacking patch, I blocked the extent freeing 5
hours. So in that 5 hours, 180208 (NOT 360416) log bytes are reserved.
"With my test case, other transactions (update timestamps) then happen.
As my hacking patch pins the journal tail, those timestamp-updating
transactions finally use up (almost) all the left available log space
(in memory in on disk). And finally the on disk (and in memory)
available log space goes down near to 180208 bytes. Those 180208 bytes
are reserved by [the] second (extent-free) transaction [in the chain]."
Wengang and I noticed that EFI recovery starts a transaction, completes
one step of the chain, and commits the transaction without completing
any other steps of the chain. Those subsequent steps are completed by
xlog_finish_defer_ops, which allocates yet another transaction to
finish the rest of the chain. That transaction gets the same tr_logres
as the head transaction, but with tr_logcount = 1 to force regranting
with every roll to avoid livelocks.
In other words, we already figured this out in commit 929b92f64048d
("xfs: xfs_defer_capture should absorb remaining transaction
reservation"), but should have applied that logic to each intent item's
recovery function. For Wengang's case, the xfs_trans_alloc call in the
EFI recovery function should only be asking for a single transaction's
worth of log reservation -- 180K, not 360K.
Quoting Wengang again:
"With log recovery, during EFI recovery, we use tr_itruncate again to
reserve two transactions that needs 360416 log bytes. Reserving 360416
bytes fails [stalls] because we now only have about 180208 available.
"Actually during the EFI recover, we only need one transaction to free
the extents just like the 2nd transaction at RUNTIME. So it only needs
to reserve 180208 rather than 360416 bytes. We have (a bit) more than
180208 available log bytes on disk, so [if we decrease the reservation
to 180K] the reservation goes and the recovery [finishes]. That is to
say: we can fix the log recover part to fix the issue. We can introduce
a new xfs_trans_res xfs_mount->tr_ext_free
{
tr_logres = 175488,
tr_logcount = 0,
tr_logflags = 0,
}
"and use tr_ext_free instead of tr_itruncate in EFI recover."
However, I don't think it quite makes sense to create an entirely new
transaction reservation type to handle single-stepping during log
recovery. Instead, we should copy the transaction reservation
information in the xfs_mount, change tr_logcount to 1, and pass that
into xfs_trans_alloc. We know this won't risk changing the min log size
computation since we always ask for a fraction of the reservation for
all known transaction types.
This looks like it's been lurking in the codebase since commit
3d3c8b5222b92, which changed the xfs_trans_reserve call in
xlog_recover_process_efi to use the tr_logcount in tr_itruncate.
That changed the EFI recovery transaction from making a
non-XFS_TRANS_PERM_LOG_RES request for one transaction's worth of log
space to a XFS_TRANS_PERM_LOG_RES request for two transactions worth.
Fixes: 3d3c8b5222b92 ("xfs: refactor xfs_trans_reserve() interface")
Complements: 929b92f64048d ("xfs: xfs_defer_capture should absorb remaining transaction reservation")
Suggested-by: Wengang Wang <wen.gang.wang@oracle.com>
Cc: Srikanth C S <srikanth.c.s@oracle.com>
[djwong: apply the same transformation to all log intent recovery]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-09-11 15:39:05 +00:00
|
|
|
struct xfs_trans_res resv;
|
2023-11-22 18:38:10 +00:00
|
|
|
struct xfs_log_item *lip = dfp->dfp_intent;
|
2020-05-01 23:00:55 +00:00
|
|
|
struct xfs_efi_log_item *efip = EFI_ITEM(lip);
|
2022-03-17 16:09:12 +00:00
|
|
|
struct xfs_mount *mp = lip->li_log->l_mp;
|
2020-05-01 23:00:55 +00:00
|
|
|
struct xfs_trans *tp;
|
|
|
|
int i;
|
|
|
|
int error = 0;
|
2016-08-03 01:23:49 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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++) {
|
2020-11-30 00:33:38 +00:00
|
|
|
if (!xfs_efi_validate_ext(mp,
|
|
|
|
&efip->efi_format.efi_extents[i])) {
|
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
|
|
|
|
&efip->efi_format,
|
|
|
|
sizeof(efip->efi_format));
|
2019-11-06 17:17:43 +00:00
|
|
|
return -EFSCORRUPTED;
|
2020-11-30 00:33:38 +00:00
|
|
|
}
|
2023-11-22 19:13:03 +00:00
|
|
|
|
|
|
|
xfs_efi_recover_work(mp, dfp, &efip->efi_format.efi_extents[i]);
|
2016-08-03 01:23:49 +00:00
|
|
|
}
|
|
|
|
|
xfs: reserve less log space when recovering log intent items
Wengang Wang reports that a customer's system was running a number of
truncate operations on a filesystem with a very small log. Contention
on the reserve heads lead to other threads stalling on smaller updates
(e.g. mtime updates) long enough to result in the node being rebooted
on account of the lack of responsivenes. The node failed to recover
because log recovery of an EFI became stuck waiting for a grant of
reserve space. From Wengang's report:
"For the file deletion, log bytes are reserved basing on
xfs_mount->tr_itruncate which is:
tr_logres = 175488,
tr_logcount = 2,
tr_logflags = XFS_TRANS_PERM_LOG_RES,
"You see it's a permanent log reservation with two log operations (two
transactions in rolling mode). After calculation (xlog_calc_unit_res()
adds space for various log headers), the final log space needed per
transaction changes from 175488 to 180208 bytes. So the total log
space needed is 360416 bytes (180208 * 2). [That quantity] of log space
(360416 bytes) needs to be reserved for both run time inode removing
(xfs_inactive_truncate()) and EFI recover (xfs_efi_item_recover())."
In other words, runtime pre-reserves 360K of space in anticipation of
running a chain of two transactions in which each transaction gets a
180K reservation.
Now that we've allocated the transaction, we delete the bmap mapping,
log an EFI to free the space, and roll the transaction as part of
finishing the deferops chain. Rolling creates a new xfs_trans which
shares its ticket with the old transaction. Next, xfs_trans_roll calls
__xfs_trans_commit with regrant == true, which calls xlog_cil_commit
with the same regrant parameter.
xlog_cil_commit calls xfs_log_ticket_regrant, which decrements t_cnt and
subtracts t_curr_res from the reservation and write heads.
If the filesystem is fresh and the first transaction only used (say)
20K, then t_curr_res will be 160K, and we give that much reservation
back to the reservation head. Or if the file is really fragmented and
the first transaction actually uses 170K, then t_curr_res will be 10K,
and that's what we give back to the reservation.
Having done that, we're now headed into the second transaction with an
EFI and 180K of reservation. Other threads apparently consumed all the
reservation for smaller transactions, such as timestamp updates.
Now let's say the first transaction gets written to disk and we crash
without ever completing the second transaction. Now we remount the fs,
log recovery finds the unfinished EFI, and calls xfs_efi_recover to
finish the EFI. However, xfs_efi_recover starts a new tr_itruncate
tranasction, which asks for 360K log reservation. This is a lot more
than the 180K that we had reserved at the time of the crash. If the
first EFI to be recovered is also pinning the tail of the log, we will
be unable to free any space in the log, and recovery livelocks.
Wengang confirmed this:
"Now we have the second transaction which has 180208 log bytes reserved
too. The second transaction is supposed to process intents including
extent freeing. With my hacking patch, I blocked the extent freeing 5
hours. So in that 5 hours, 180208 (NOT 360416) log bytes are reserved.
"With my test case, other transactions (update timestamps) then happen.
As my hacking patch pins the journal tail, those timestamp-updating
transactions finally use up (almost) all the left available log space
(in memory in on disk). And finally the on disk (and in memory)
available log space goes down near to 180208 bytes. Those 180208 bytes
are reserved by [the] second (extent-free) transaction [in the chain]."
Wengang and I noticed that EFI recovery starts a transaction, completes
one step of the chain, and commits the transaction without completing
any other steps of the chain. Those subsequent steps are completed by
xlog_finish_defer_ops, which allocates yet another transaction to
finish the rest of the chain. That transaction gets the same tr_logres
as the head transaction, but with tr_logcount = 1 to force regranting
with every roll to avoid livelocks.
In other words, we already figured this out in commit 929b92f64048d
("xfs: xfs_defer_capture should absorb remaining transaction
reservation"), but should have applied that logic to each intent item's
recovery function. For Wengang's case, the xfs_trans_alloc call in the
EFI recovery function should only be asking for a single transaction's
worth of log reservation -- 180K, not 360K.
Quoting Wengang again:
"With log recovery, during EFI recovery, we use tr_itruncate again to
reserve two transactions that needs 360416 log bytes. Reserving 360416
bytes fails [stalls] because we now only have about 180208 available.
"Actually during the EFI recover, we only need one transaction to free
the extents just like the 2nd transaction at RUNTIME. So it only needs
to reserve 180208 rather than 360416 bytes. We have (a bit) more than
180208 available log bytes on disk, so [if we decrease the reservation
to 180K] the reservation goes and the recovery [finishes]. That is to
say: we can fix the log recover part to fix the issue. We can introduce
a new xfs_trans_res xfs_mount->tr_ext_free
{
tr_logres = 175488,
tr_logcount = 0,
tr_logflags = 0,
}
"and use tr_ext_free instead of tr_itruncate in EFI recover."
However, I don't think it quite makes sense to create an entirely new
transaction reservation type to handle single-stepping during log
recovery. Instead, we should copy the transaction reservation
information in the xfs_mount, change tr_logcount to 1, and pass that
into xfs_trans_alloc. We know this won't risk changing the min log size
computation since we always ask for a fraction of the reservation for
all known transaction types.
This looks like it's been lurking in the codebase since commit
3d3c8b5222b92, which changed the xfs_trans_reserve call in
xlog_recover_process_efi to use the tr_logcount in tr_itruncate.
That changed the EFI recovery transaction from making a
non-XFS_TRANS_PERM_LOG_RES request for one transaction's worth of log
space to a XFS_TRANS_PERM_LOG_RES request for two transactions worth.
Fixes: 3d3c8b5222b92 ("xfs: refactor xfs_trans_reserve() interface")
Complements: 929b92f64048d ("xfs: xfs_defer_capture should absorb remaining transaction reservation")
Suggested-by: Wengang Wang <wen.gang.wang@oracle.com>
Cc: Srikanth C S <srikanth.c.s@oracle.com>
[djwong: apply the same transformation to all log intent recovery]
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2023-09-11 15:39:05 +00:00
|
|
|
resv = xlog_recover_resv(&M_RES(mp)->tr_itruncate);
|
|
|
|
error = xfs_trans_alloc(mp, &resv, 0, 0, 0, &tp);
|
2016-08-03 01:23:49 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2023-11-22 18:47:10 +00:00
|
|
|
|
2023-11-22 19:25:45 +00:00
|
|
|
error = xlog_recover_finish_intent(tp, dfp);
|
|
|
|
if (error == -EFSCORRUPTED)
|
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
|
|
|
|
&efip->efi_format,
|
|
|
|
sizeof(efip->efi_format));
|
|
|
|
if (error)
|
|
|
|
goto abort_error;
|
2016-08-03 01:23:49 +00:00
|
|
|
|
2021-09-17 00:28:07 +00:00
|
|
|
return xfs_defer_ops_capture_and_commit(tp, capture_list);
|
2016-08-03 01:23:49 +00:00
|
|
|
|
|
|
|
abort_error:
|
|
|
|
xfs_trans_cancel(tp);
|
|
|
|
return error;
|
|
|
|
}
|
2020-05-01 23:00:45 +00:00
|
|
|
|
2023-11-30 20:31:30 +00:00
|
|
|
/* Relog an intent item to push the log tail forward. */
|
|
|
|
static struct xfs_log_item *
|
|
|
|
xfs_extent_free_relog_intent(
|
|
|
|
struct xfs_trans *tp,
|
|
|
|
struct xfs_log_item *intent,
|
|
|
|
struct xfs_log_item *done_item)
|
|
|
|
{
|
|
|
|
struct xfs_efd_log_item *efdp = EFD_ITEM(done_item);
|
|
|
|
struct xfs_efi_log_item *efip;
|
|
|
|
struct xfs_extent *extp;
|
|
|
|
unsigned int count;
|
|
|
|
|
|
|
|
count = EFI_ITEM(intent)->efi_format.efi_nextents;
|
|
|
|
extp = EFI_ITEM(intent)->efi_format.efi_extents;
|
|
|
|
|
|
|
|
efdp->efd_next_extent = count;
|
|
|
|
memcpy(efdp->efd_format.efd_extents, extp, count * sizeof(*extp));
|
|
|
|
|
|
|
|
efip = xfs_efi_init(tp->t_mountp, count);
|
|
|
|
memcpy(efip->efi_format.efi_extents, extp, count * sizeof(*extp));
|
|
|
|
atomic_set(&efip->efi_next_extent, count);
|
|
|
|
|
|
|
|
return &efip->efi_item;
|
|
|
|
}
|
|
|
|
|
2023-11-22 21:39:25 +00:00
|
|
|
const struct xfs_defer_op_type xfs_extent_free_defer_type = {
|
2023-12-13 09:06:31 +00:00
|
|
|
.name = "extent_free",
|
2023-11-22 21:39:25 +00:00
|
|
|
.max_items = XFS_EFI_MAX_FAST_EXTENTS,
|
|
|
|
.create_intent = xfs_extent_free_create_intent,
|
|
|
|
.abort_intent = xfs_extent_free_abort_intent,
|
|
|
|
.create_done = xfs_extent_free_create_done,
|
|
|
|
.finish_item = xfs_extent_free_finish_item,
|
|
|
|
.cancel_item = xfs_extent_free_cancel_item,
|
|
|
|
.recover_work = xfs_extent_free_recover_work,
|
2023-11-30 20:31:30 +00:00
|
|
|
.relog_intent = xfs_extent_free_relog_intent,
|
2023-11-22 21:39:25 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* sub-type with special handling for AGFL deferred frees */
|
|
|
|
const struct xfs_defer_op_type xfs_agfl_free_defer_type = {
|
2023-12-13 09:06:31 +00:00
|
|
|
.name = "agfl_free",
|
2023-11-22 21:39:25 +00:00
|
|
|
.max_items = XFS_EFI_MAX_FAST_EXTENTS,
|
|
|
|
.create_intent = xfs_extent_free_create_intent,
|
|
|
|
.abort_intent = xfs_extent_free_abort_intent,
|
|
|
|
.create_done = xfs_extent_free_create_done,
|
|
|
|
.finish_item = xfs_agfl_free_finish_item,
|
|
|
|
.cancel_item = xfs_extent_free_cancel_item,
|
|
|
|
.recover_work = xfs_extent_free_recover_work,
|
2023-11-30 20:31:30 +00:00
|
|
|
.relog_intent = xfs_extent_free_relog_intent,
|
2023-11-22 21:39:25 +00:00
|
|
|
};
|
|
|
|
|
2020-05-01 23:00:54 +00:00
|
|
|
STATIC bool
|
|
|
|
xfs_efi_item_match(
|
|
|
|
struct xfs_log_item *lip,
|
|
|
|
uint64_t intent_id)
|
|
|
|
{
|
|
|
|
return EFI_ITEM(lip)->efi_format.efi_id == intent_id;
|
|
|
|
}
|
|
|
|
|
2020-05-01 23:00:50 +00:00
|
|
|
static const struct xfs_item_ops xfs_efi_item_ops = {
|
2022-05-04 01:46:09 +00:00
|
|
|
.flags = XFS_ITEM_INTENT,
|
2020-05-01 23:00:50 +00:00
|
|
|
.iop_size = xfs_efi_item_size,
|
|
|
|
.iop_format = xfs_efi_item_format,
|
|
|
|
.iop_unpin = xfs_efi_item_unpin,
|
|
|
|
.iop_release = xfs_efi_item_release,
|
2020-05-01 23:00:54 +00:00
|
|
|
.iop_match = xfs_efi_item_match,
|
2020-05-01 23:00:50 +00:00
|
|
|
};
|
|
|
|
|
2020-05-01 23:00:48 +00:00
|
|
|
/*
|
|
|
|
* This routine is called to create an in-core extent free intent
|
|
|
|
* item from the efi format structure which was logged on disk.
|
|
|
|
* It allocates an in-core efi, copies the extents from the format
|
|
|
|
* structure into it, and adds the efi to the AIL with the given
|
|
|
|
* LSN.
|
|
|
|
*/
|
|
|
|
STATIC int
|
|
|
|
xlog_recover_efi_commit_pass2(
|
|
|
|
struct xlog *log,
|
|
|
|
struct list_head *buffer_list,
|
|
|
|
struct xlog_recover_item *item,
|
|
|
|
xfs_lsn_t lsn)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = log->l_mp;
|
|
|
|
struct xfs_efi_log_item *efip;
|
|
|
|
struct xfs_efi_log_format *efi_formatp;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
efi_formatp = item->ri_buf[0].i_addr;
|
|
|
|
|
2022-10-21 16:10:05 +00:00
|
|
|
if (item->ri_buf[0].i_len < xfs_efi_log_format_sizeof(0)) {
|
2022-10-25 22:07:14 +00:00
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp,
|
|
|
|
item->ri_buf[0].i_addr, item->ri_buf[0].i_len);
|
2022-10-20 23:39:59 +00:00
|
|
|
return -EFSCORRUPTED;
|
|
|
|
}
|
|
|
|
|
2020-05-01 23:00:48 +00:00
|
|
|
efip = xfs_efi_init(mp, efi_formatp->efi_nextents);
|
|
|
|
error = xfs_efi_copy_format(&item->ri_buf[0], &efip->efi_format);
|
|
|
|
if (error) {
|
|
|
|
xfs_efi_item_free(efip);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
atomic_set(&efip->efi_next_extent, efi_formatp->efi_nextents);
|
2023-11-22 18:23:23 +00:00
|
|
|
|
|
|
|
xlog_recover_intent_item(log, &efip->efi_item, lsn,
|
2023-12-14 05:16:32 +00:00
|
|
|
&xfs_extent_free_defer_type);
|
2020-05-01 23:00:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-01 23:00:45 +00:00
|
|
|
const struct xlog_recover_item_ops xlog_efi_item_ops = {
|
|
|
|
.item_type = XFS_LI_EFI,
|
2020-05-01 23:00:48 +00:00
|
|
|
.commit_pass2 = xlog_recover_efi_commit_pass2,
|
2020-05-01 23:00:45 +00:00
|
|
|
};
|
|
|
|
|
2020-05-01 23:00:48 +00:00
|
|
|
/*
|
|
|
|
* This routine is called when an EFD format structure is found in a committed
|
|
|
|
* transaction in the log. Its purpose is to cancel the corresponding EFI if it
|
|
|
|
* was still in the log. To do this it searches the AIL for the EFI with an id
|
|
|
|
* equal to that in the EFD format structure. If we find it we drop the EFD
|
|
|
|
* reference, which removes the EFI from the AIL and frees it.
|
|
|
|
*/
|
|
|
|
STATIC int
|
|
|
|
xlog_recover_efd_commit_pass2(
|
|
|
|
struct xlog *log,
|
|
|
|
struct list_head *buffer_list,
|
|
|
|
struct xlog_recover_item *item,
|
|
|
|
xfs_lsn_t lsn)
|
|
|
|
{
|
|
|
|
struct xfs_efd_log_format *efd_formatp;
|
2022-10-25 22:14:06 +00:00
|
|
|
int buflen = item->ri_buf[0].i_len;
|
2020-05-01 23:00:48 +00:00
|
|
|
|
|
|
|
efd_formatp = item->ri_buf[0].i_addr;
|
2022-10-25 22:14:06 +00:00
|
|
|
|
|
|
|
if (buflen < sizeof(struct xfs_efd_log_format)) {
|
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
|
|
|
|
efd_formatp, buflen);
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item->ri_buf[0].i_len != xfs_efd_log_format32_sizeof(
|
|
|
|
efd_formatp->efd_nextents) &&
|
|
|
|
item->ri_buf[0].i_len != xfs_efd_log_format64_sizeof(
|
|
|
|
efd_formatp->efd_nextents)) {
|
|
|
|
XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, log->l_mp,
|
|
|
|
efd_formatp, buflen);
|
|
|
|
return -EFSCORRUPTED;
|
|
|
|
}
|
2020-05-01 23:00:48 +00:00
|
|
|
|
2020-05-01 23:00:54 +00:00
|
|
|
xlog_recover_release_intent(log, XFS_LI_EFI, efd_formatp->efd_efi_id);
|
2020-05-01 23:00:48 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-05-01 23:00:45 +00:00
|
|
|
const struct xlog_recover_item_ops xlog_efd_item_ops = {
|
|
|
|
.item_type = XFS_LI_EFD,
|
2020-05-01 23:00:48 +00:00
|
|
|
.commit_pass2 = xlog_recover_efd_commit_pass2,
|
2020-05-01 23:00:45 +00:00
|
|
|
};
|