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-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"
|
2013-10-22 23:36:05 +00:00
|
|
|
#include "xfs_shared.h"
|
2013-10-22 23:51:50 +00:00
|
|
|
#include "xfs_format.h"
|
2013-10-22 23:50:10 +00:00
|
|
|
#include "xfs_log_format.h"
|
|
|
|
#include "xfs_trans_resv.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_mount.h"
|
2017-10-31 19:04:49 +00:00
|
|
|
#include "xfs_errortag.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_error.h"
|
2013-10-22 23:50:10 +00:00
|
|
|
#include "xfs_trans.h"
|
|
|
|
#include "xfs_trans_priv.h"
|
|
|
|
#include "xfs_log.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
#include "xfs_log_priv.h"
|
2009-12-14 23:14:59 +00:00
|
|
|
#include "xfs_trace.h"
|
2014-07-14 22:07:29 +00:00
|
|
|
#include "xfs_sysfs.h"
|
2015-01-21 22:10:31 +00:00
|
|
|
#include "xfs_sb.h"
|
2019-04-12 14:41:15 +00:00
|
|
|
#include "xfs_health.h"
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-10-12 18:09:23 +00:00
|
|
|
struct kmem_cache *xfs_log_ticket_cache;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* Local miscellaneous function prototypes */
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC struct xlog *
|
|
|
|
xlog_alloc_log(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buftarg *log_target,
|
|
|
|
xfs_daddr_t blk_offset,
|
|
|
|
int num_bblks);
|
2012-06-14 14:22:15 +00:00
|
|
|
STATIC int
|
|
|
|
xlog_space_left(
|
|
|
|
struct xlog *log,
|
|
|
|
atomic64_t *head);
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_dealloc_log(
|
|
|
|
struct xlog *log);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* local state machine functions */
|
2019-06-29 02:27:30 +00:00
|
|
|
STATIC void xlog_state_done_syncing(
|
2020-03-20 15:49:20 +00:00
|
|
|
struct xlog_in_core *iclog);
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
STATIC void xlog_state_do_callback(
|
|
|
|
struct xlog *log);
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC int
|
|
|
|
xlog_state_get_iclog_space(
|
|
|
|
struct xlog *log,
|
|
|
|
int len,
|
|
|
|
struct xlog_in_core **iclog,
|
|
|
|
struct xlog_ticket *ticket,
|
|
|
|
int *logoffsetp);
|
|
|
|
STATIC void
|
2012-06-14 14:22:15 +00:00
|
|
|
xlog_grant_push_ail(
|
2012-06-14 14:22:16 +00:00
|
|
|
struct xlog *log,
|
|
|
|
int need_bytes);
|
|
|
|
STATIC void
|
2019-10-14 17:36:41 +00:00
|
|
|
xlog_sync(
|
|
|
|
struct xlog *log,
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
struct xlog_ticket *ticket);
|
2005-11-02 04:12:04 +00:00
|
|
|
#if defined(DEBUG)
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC void
|
2012-06-14 14:22:15 +00:00
|
|
|
xlog_verify_grant_tail(
|
2012-06-14 14:22:16 +00:00
|
|
|
struct xlog *log);
|
|
|
|
STATIC void
|
|
|
|
xlog_verify_iclog(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
2019-06-29 02:27:24 +00:00
|
|
|
int count);
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_verify_tail_lsn(
|
|
|
|
struct xlog *log,
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
struct xlog_in_core *iclog);
|
2005-04-16 22:20:36 +00:00
|
|
|
#else
|
2010-12-21 01:02:52 +00:00
|
|
|
#define xlog_verify_grant_tail(a)
|
2019-06-29 02:27:24 +00:00
|
|
|
#define xlog_verify_iclog(a,b,c)
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
#define xlog_verify_tail_lsn(a,b)
|
2005-04-16 22:20:36 +00:00
|
|
|
#endif
|
|
|
|
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC int
|
|
|
|
xlog_iclogs_empty(
|
|
|
|
struct xlog *log);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
static int
|
|
|
|
xfs_log_cover(struct xfs_mount *);
|
|
|
|
|
2022-04-21 00:34:59 +00:00
|
|
|
/*
|
|
|
|
* We need to make sure the buffer pointer returned is naturally aligned for the
|
|
|
|
* biggest basic data type we put into it. We have already accounted for this
|
|
|
|
* padding when sizing the buffer.
|
|
|
|
*
|
|
|
|
* However, this padding does not get written into the log, and hence we have to
|
|
|
|
* track the space used by the log vectors separately to prevent log space hangs
|
|
|
|
* due to inaccurate accounting (i.e. a leak) of the used log space through the
|
|
|
|
* CIL context ticket.
|
|
|
|
*
|
|
|
|
* We also add space for the xlog_op_header that describes this region in the
|
|
|
|
* log. This prepends the data region we return to the caller to copy their data
|
|
|
|
* into, so do all the static initialisation of the ophdr now. Because the ophdr
|
|
|
|
* is not 8 byte aligned, we have to be careful to ensure that we align the
|
|
|
|
* start of the buffer such that the region we return to the call is 8 byte
|
|
|
|
* aligned and packed against the tail of the ophdr.
|
|
|
|
*/
|
|
|
|
void *
|
|
|
|
xlog_prepare_iovec(
|
|
|
|
struct xfs_log_vec *lv,
|
|
|
|
struct xfs_log_iovec **vecp,
|
|
|
|
uint type)
|
|
|
|
{
|
|
|
|
struct xfs_log_iovec *vec = *vecp;
|
|
|
|
struct xlog_op_header *oph;
|
|
|
|
uint32_t len;
|
|
|
|
void *buf;
|
|
|
|
|
|
|
|
if (vec) {
|
|
|
|
ASSERT(vec - lv->lv_iovecp < lv->lv_niovecs);
|
|
|
|
vec++;
|
|
|
|
} else {
|
|
|
|
vec = &lv->lv_iovecp[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
len = lv->lv_buf_len + sizeof(struct xlog_op_header);
|
|
|
|
if (!IS_ALIGNED(len, sizeof(uint64_t))) {
|
|
|
|
lv->lv_buf_len = round_up(len, sizeof(uint64_t)) -
|
|
|
|
sizeof(struct xlog_op_header);
|
|
|
|
}
|
|
|
|
|
|
|
|
vec->i_type = type;
|
|
|
|
vec->i_addr = lv->lv_buf + lv->lv_buf_len;
|
|
|
|
|
|
|
|
oph = vec->i_addr;
|
|
|
|
oph->oh_clientid = XFS_TRANSACTION;
|
|
|
|
oph->oh_res2 = 0;
|
|
|
|
oph->oh_flags = 0;
|
|
|
|
|
|
|
|
buf = vec->i_addr + sizeof(struct xlog_op_header);
|
|
|
|
ASSERT(IS_ALIGNED((unsigned long)buf, sizeof(uint64_t)));
|
|
|
|
|
|
|
|
*vecp = vec;
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
|
2006-01-11 04:34:50 +00:00
|
|
|
static void
|
2010-12-21 01:06:05 +00:00
|
|
|
xlog_grant_sub_space(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
|
|
|
atomic64_t *head,
|
|
|
|
int bytes)
|
2006-01-11 04:34:50 +00:00
|
|
|
{
|
2010-12-21 01:29:14 +00:00
|
|
|
int64_t head_val = atomic64_read(head);
|
|
|
|
int64_t new, old;
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
do {
|
|
|
|
int cycle, space;
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
xlog_crack_grant_head_val(head_val, &cycle, &space);
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
space -= bytes;
|
|
|
|
if (space < 0) {
|
|
|
|
space += log->l_logsize;
|
|
|
|
cycle--;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = head_val;
|
|
|
|
new = xlog_assign_grant_head_val(cycle, space);
|
|
|
|
head_val = atomic64_cmpxchg(head, old, new);
|
|
|
|
} while (head_val != old);
|
2006-01-11 04:34:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2010-12-21 01:06:05 +00:00
|
|
|
xlog_grant_add_space(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
|
|
|
atomic64_t *head,
|
|
|
|
int bytes)
|
2006-01-11 04:34:50 +00:00
|
|
|
{
|
2010-12-21 01:29:14 +00:00
|
|
|
int64_t head_val = atomic64_read(head);
|
|
|
|
int64_t new, old;
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
do {
|
|
|
|
int tmp;
|
|
|
|
int cycle, space;
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
xlog_crack_grant_head_val(head_val, &cycle, &space);
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2010-12-21 01:29:14 +00:00
|
|
|
tmp = log->l_logsize - space;
|
|
|
|
if (tmp > bytes)
|
|
|
|
space += bytes;
|
|
|
|
else {
|
|
|
|
space = bytes - tmp;
|
|
|
|
cycle++;
|
|
|
|
}
|
|
|
|
|
|
|
|
old = head_val;
|
|
|
|
new = xlog_assign_grant_head_val(cycle, space);
|
|
|
|
head_val = atomic64_cmpxchg(head, old, new);
|
|
|
|
} while (head_val != old);
|
2006-01-11 04:34:50 +00:00
|
|
|
}
|
2010-12-21 01:08:20 +00:00
|
|
|
|
2012-02-20 02:31:26 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_grant_head_init(
|
|
|
|
struct xlog_grant_head *head)
|
|
|
|
{
|
|
|
|
xlog_assign_grant_head(&head->grant, 1, 0);
|
|
|
|
INIT_LIST_HEAD(&head->waiters);
|
|
|
|
spin_lock_init(&head->lock);
|
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:27 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_grant_head_wake_all(
|
|
|
|
struct xlog_grant_head *head)
|
|
|
|
{
|
|
|
|
struct xlog_ticket *tic;
|
|
|
|
|
|
|
|
spin_lock(&head->lock);
|
|
|
|
list_for_each_entry(tic, &head->waiters, t_queue)
|
|
|
|
wake_up_process(tic->t_task);
|
|
|
|
spin_unlock(&head->lock);
|
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:29 +00:00
|
|
|
static inline int
|
|
|
|
xlog_ticket_reservation(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2012-02-20 02:31:29 +00:00
|
|
|
struct xlog_grant_head *head,
|
|
|
|
struct xlog_ticket *tic)
|
2011-11-28 08:17:36 +00:00
|
|
|
{
|
2012-02-20 02:31:29 +00:00
|
|
|
if (head == &log->l_write_head) {
|
|
|
|
ASSERT(tic->t_flags & XLOG_TIC_PERM_RESERV);
|
|
|
|
return tic->t_unit_res;
|
|
|
|
} else {
|
2011-11-28 08:17:36 +00:00
|
|
|
if (tic->t_flags & XLOG_TIC_PERM_RESERV)
|
2012-02-20 02:31:29 +00:00
|
|
|
return tic->t_unit_res * tic->t_cnt;
|
2011-11-28 08:17:36 +00:00
|
|
|
else
|
2012-02-20 02:31:29 +00:00
|
|
|
return tic->t_unit_res;
|
2011-11-28 08:17:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC bool
|
2012-02-20 02:31:29 +00:00
|
|
|
xlog_grant_head_wake(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2012-02-20 02:31:29 +00:00
|
|
|
struct xlog_grant_head *head,
|
2011-11-28 08:17:36 +00:00
|
|
|
int *free_bytes)
|
|
|
|
{
|
|
|
|
struct xlog_ticket *tic;
|
|
|
|
int need_bytes;
|
xfs: push the AIL in xlog_grant_head_wake
In the situation where the log is full and the CIL has not recently
flushed, the AIL push threshold is throttled back to the where the
last write of the head of the log was completed. This is stored in
log->l_last_sync_lsn. Hence if the CIL holds > 25% of the log space
pinned by flushes and/or aggregation in progress, we can get the
situation where the head of the log lags a long way behind the
reservation grant head.
When this happens, the AIL push target is trimmed back from where
the reservation grant head wants to push the log tail to, back to
where the head of the log currently is. This means the push target
doesn't reach far enough into the log to actually move the tail
before the transaction reservation goes to sleep.
When the CIL push completes, it moves the log head forward such that
the AIL push target can now be moved, but that has no mechanism for
puhsing the log tail. Further, if the next tail movement of the log
is not large enough wake the waiter (i.e. still not enough space for
it to have a reservation granted), we don't wake anything up, and
hence we do not update the AIL push target to take into account the
head of the log moving and allowing the push target to be moved
forwards.
To avoid this particular condition, if we fail to wake the first
waiter on the grant head because we don't have enough space,
push on the AIL again. This will pick up any movement of the log
head and allow the push target to move forward due to completion of
CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-09-06 00:32:48 +00:00
|
|
|
bool woken_task = false;
|
2011-11-28 08:17:36 +00:00
|
|
|
|
2012-02-20 02:31:29 +00:00
|
|
|
list_for_each_entry(tic, &head->waiters, t_queue) {
|
xfs: push the AIL in xlog_grant_head_wake
In the situation where the log is full and the CIL has not recently
flushed, the AIL push threshold is throttled back to the where the
last write of the head of the log was completed. This is stored in
log->l_last_sync_lsn. Hence if the CIL holds > 25% of the log space
pinned by flushes and/or aggregation in progress, we can get the
situation where the head of the log lags a long way behind the
reservation grant head.
When this happens, the AIL push target is trimmed back from where
the reservation grant head wants to push the log tail to, back to
where the head of the log currently is. This means the push target
doesn't reach far enough into the log to actually move the tail
before the transaction reservation goes to sleep.
When the CIL push completes, it moves the log head forward such that
the AIL push target can now be moved, but that has no mechanism for
puhsing the log tail. Further, if the next tail movement of the log
is not large enough wake the waiter (i.e. still not enough space for
it to have a reservation granted), we don't wake anything up, and
hence we do not update the AIL push target to take into account the
head of the log moving and allowing the push target to be moved
forwards.
To avoid this particular condition, if we fail to wake the first
waiter on the grant head because we don't have enough space,
push on the AIL again. This will pick up any movement of the log
head and allow the push target to move forward due to completion of
CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-09-06 00:32:48 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* There is a chance that the size of the CIL checkpoints in
|
|
|
|
* progress at the last AIL push target calculation resulted in
|
|
|
|
* limiting the target to the log head (l_last_sync_lsn) at the
|
|
|
|
* time. This may not reflect where the log head is now as the
|
|
|
|
* CIL checkpoints may have completed.
|
|
|
|
*
|
|
|
|
* Hence when we are woken here, it may be that the head of the
|
|
|
|
* log that has moved rather than the tail. As the tail didn't
|
|
|
|
* move, there still won't be space available for the
|
|
|
|
* reservation we require. However, if the AIL has already
|
|
|
|
* pushed to the target defined by the old log head location, we
|
|
|
|
* will hang here waiting for something else to update the AIL
|
|
|
|
* push target.
|
|
|
|
*
|
|
|
|
* Therefore, if there isn't space to wake the first waiter on
|
|
|
|
* the grant head, we need to push the AIL again to ensure the
|
|
|
|
* target reflects both the current log tail and log head
|
|
|
|
* position before we wait for the tail to move again.
|
|
|
|
*/
|
|
|
|
|
2012-02-20 02:31:29 +00:00
|
|
|
need_bytes = xlog_ticket_reservation(log, head, tic);
|
xfs: push the AIL in xlog_grant_head_wake
In the situation where the log is full and the CIL has not recently
flushed, the AIL push threshold is throttled back to the where the
last write of the head of the log was completed. This is stored in
log->l_last_sync_lsn. Hence if the CIL holds > 25% of the log space
pinned by flushes and/or aggregation in progress, we can get the
situation where the head of the log lags a long way behind the
reservation grant head.
When this happens, the AIL push target is trimmed back from where
the reservation grant head wants to push the log tail to, back to
where the head of the log currently is. This means the push target
doesn't reach far enough into the log to actually move the tail
before the transaction reservation goes to sleep.
When the CIL push completes, it moves the log head forward such that
the AIL push target can now be moved, but that has no mechanism for
puhsing the log tail. Further, if the next tail movement of the log
is not large enough wake the waiter (i.e. still not enough space for
it to have a reservation granted), we don't wake anything up, and
hence we do not update the AIL push target to take into account the
head of the log moving and allowing the push target to be moved
forwards.
To avoid this particular condition, if we fail to wake the first
waiter on the grant head because we don't have enough space,
push on the AIL again. This will pick up any movement of the log
head and allow the push target to move forward due to completion of
CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-09-06 00:32:48 +00:00
|
|
|
if (*free_bytes < need_bytes) {
|
|
|
|
if (!woken_task)
|
|
|
|
xlog_grant_push_ail(log, need_bytes);
|
2011-11-28 08:17:36 +00:00
|
|
|
return false;
|
xfs: push the AIL in xlog_grant_head_wake
In the situation where the log is full and the CIL has not recently
flushed, the AIL push threshold is throttled back to the where the
last write of the head of the log was completed. This is stored in
log->l_last_sync_lsn. Hence if the CIL holds > 25% of the log space
pinned by flushes and/or aggregation in progress, we can get the
situation where the head of the log lags a long way behind the
reservation grant head.
When this happens, the AIL push target is trimmed back from where
the reservation grant head wants to push the log tail to, back to
where the head of the log currently is. This means the push target
doesn't reach far enough into the log to actually move the tail
before the transaction reservation goes to sleep.
When the CIL push completes, it moves the log head forward such that
the AIL push target can now be moved, but that has no mechanism for
puhsing the log tail. Further, if the next tail movement of the log
is not large enough wake the waiter (i.e. still not enough space for
it to have a reservation granted), we don't wake anything up, and
hence we do not update the AIL push target to take into account the
head of the log moving and allowing the push target to be moved
forwards.
To avoid this particular condition, if we fail to wake the first
waiter on the grant head because we don't have enough space,
push on the AIL again. This will pick up any movement of the log
head and allow the push target to move forward due to completion of
CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-09-06 00:32:48 +00:00
|
|
|
}
|
2011-11-28 08:17:36 +00:00
|
|
|
|
2012-02-20 02:31:29 +00:00
|
|
|
*free_bytes -= need_bytes;
|
|
|
|
trace_xfs_log_grant_wake_up(log, tic);
|
2012-02-20 02:31:24 +00:00
|
|
|
wake_up_process(tic->t_task);
|
xfs: push the AIL in xlog_grant_head_wake
In the situation where the log is full and the CIL has not recently
flushed, the AIL push threshold is throttled back to the where the
last write of the head of the log was completed. This is stored in
log->l_last_sync_lsn. Hence if the CIL holds > 25% of the log space
pinned by flushes and/or aggregation in progress, we can get the
situation where the head of the log lags a long way behind the
reservation grant head.
When this happens, the AIL push target is trimmed back from where
the reservation grant head wants to push the log tail to, back to
where the head of the log currently is. This means the push target
doesn't reach far enough into the log to actually move the tail
before the transaction reservation goes to sleep.
When the CIL push completes, it moves the log head forward such that
the AIL push target can now be moved, but that has no mechanism for
puhsing the log tail. Further, if the next tail movement of the log
is not large enough wake the waiter (i.e. still not enough space for
it to have a reservation granted), we don't wake anything up, and
hence we do not update the AIL push target to take into account the
head of the log moving and allowing the push target to be moved
forwards.
To avoid this particular condition, if we fail to wake the first
waiter on the grant head because we don't have enough space,
push on the AIL again. This will pick up any movement of the log
head and allow the push target to move forward due to completion of
CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: Darrick J. Wong <darrick.wong@oracle.com>
2019-09-06 00:32:48 +00:00
|
|
|
woken_task = true;
|
2011-11-28 08:17:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
STATIC int
|
2012-02-20 02:31:28 +00:00
|
|
|
xlog_grant_head_wait(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2012-02-20 02:31:28 +00:00
|
|
|
struct xlog_grant_head *head,
|
2011-11-28 08:17:36 +00:00
|
|
|
struct xlog_ticket *tic,
|
2013-09-02 10:49:36 +00:00
|
|
|
int need_bytes) __releases(&head->lock)
|
|
|
|
__acquires(&head->lock)
|
2011-11-28 08:17:36 +00:00
|
|
|
{
|
2012-02-20 02:31:28 +00:00
|
|
|
list_add_tail(&tic->t_queue, &head->waiters);
|
2011-11-28 08:17:36 +00:00
|
|
|
|
|
|
|
do {
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2011-11-28 08:17:36 +00:00
|
|
|
goto shutdown;
|
|
|
|
xlog_grant_push_ail(log, need_bytes);
|
|
|
|
|
2012-02-20 02:31:24 +00:00
|
|
|
__set_current_state(TASK_UNINTERRUPTIBLE);
|
2012-02-20 02:31:28 +00:00
|
|
|
spin_unlock(&head->lock);
|
2012-02-20 02:31:24 +00:00
|
|
|
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(log->l_mp, xs_sleep_logspace);
|
2011-11-28 08:17:36 +00:00
|
|
|
|
2012-02-20 02:31:24 +00:00
|
|
|
trace_xfs_log_grant_sleep(log, tic);
|
|
|
|
schedule();
|
2011-11-28 08:17:36 +00:00
|
|
|
trace_xfs_log_grant_wake(log, tic);
|
|
|
|
|
2012-02-20 02:31:28 +00:00
|
|
|
spin_lock(&head->lock);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2011-11-28 08:17:36 +00:00
|
|
|
goto shutdown;
|
2012-02-20 02:31:28 +00:00
|
|
|
} while (xlog_space_left(log, &head->grant) < need_bytes);
|
2011-11-28 08:17:36 +00:00
|
|
|
|
|
|
|
list_del_init(&tic->t_queue);
|
|
|
|
return 0;
|
|
|
|
shutdown:
|
|
|
|
list_del_init(&tic->t_queue);
|
2014-06-25 04:58:08 +00:00
|
|
|
return -EIO;
|
2011-11-28 08:17:36 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:30 +00:00
|
|
|
/*
|
|
|
|
* Atomically get the log space required for a log ticket.
|
|
|
|
*
|
|
|
|
* Once a ticket gets put onto head->waiters, it will only return after the
|
|
|
|
* needed reservation is satisfied.
|
|
|
|
*
|
|
|
|
* This function is structured so that it has a lock free fast path. This is
|
|
|
|
* necessary because every new transaction reservation will come through this
|
|
|
|
* path. Hence any lock will be globally hot if we take it unconditionally on
|
|
|
|
* every pass.
|
|
|
|
*
|
|
|
|
* As tickets are only ever moved on and off head->waiters under head->lock, we
|
|
|
|
* only need to take that lock if we are going to add the ticket to the queue
|
|
|
|
* and sleep. We can avoid taking the lock if the ticket was never added to
|
|
|
|
* head->waiters because the t_queue list head will be empty and we hold the
|
|
|
|
* only reference to it so it can safely be checked unlocked.
|
|
|
|
*/
|
|
|
|
STATIC int
|
|
|
|
xlog_grant_head_check(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2012-02-20 02:31:30 +00:00
|
|
|
struct xlog_grant_head *head,
|
|
|
|
struct xlog_ticket *tic,
|
|
|
|
int *need_bytes)
|
|
|
|
{
|
|
|
|
int free_bytes;
|
|
|
|
int error = 0;
|
|
|
|
|
2021-08-11 00:59:02 +00:00
|
|
|
ASSERT(!xlog_in_recovery(log));
|
2012-02-20 02:31:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are other waiters on the queue then give them a chance at
|
|
|
|
* logspace before us. Wake up the first waiters, if we do not wake
|
|
|
|
* up all the waiters then go to sleep waiting for more free space,
|
|
|
|
* otherwise try to get some space for this transaction.
|
|
|
|
*/
|
|
|
|
*need_bytes = xlog_ticket_reservation(log, head, tic);
|
|
|
|
free_bytes = xlog_space_left(log, &head->grant);
|
|
|
|
if (!list_empty_careful(&head->waiters)) {
|
|
|
|
spin_lock(&head->lock);
|
|
|
|
if (!xlog_grant_head_wake(log, head, &free_bytes) ||
|
|
|
|
free_bytes < *need_bytes) {
|
|
|
|
error = xlog_grant_head_wait(log, head, tic,
|
|
|
|
*need_bytes);
|
|
|
|
}
|
|
|
|
spin_unlock(&head->lock);
|
|
|
|
} else if (free_bytes < *need_bytes) {
|
|
|
|
spin_lock(&head->lock);
|
|
|
|
error = xlog_grant_head_wait(log, head, tic, *need_bytes);
|
|
|
|
spin_unlock(&head->lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2021-01-23 00:48:20 +00:00
|
|
|
bool
|
|
|
|
xfs_log_writable(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
/*
|
2021-04-29 21:39:33 +00:00
|
|
|
* Do not write to the log on norecovery mounts, if the data or log
|
|
|
|
* devices are read-only, or if the filesystem is shutdown. Read-only
|
|
|
|
* mounts allow internal writes for log recovery and unmount purposes,
|
|
|
|
* so don't restrict that case.
|
2021-01-23 00:48:20 +00:00
|
|
|
*/
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_has_norecovery(mp))
|
2021-01-23 00:48:20 +00:00
|
|
|
return false;
|
2021-04-29 21:39:33 +00:00
|
|
|
if (xfs_readonly_buftarg(mp->m_ddev_targp))
|
|
|
|
return false;
|
2021-01-23 00:48:20 +00:00
|
|
|
if (xfs_readonly_buftarg(mp->m_log->l_targ))
|
|
|
|
return false;
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(mp->m_log))
|
2021-01-23 00:48:20 +00:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:31 +00:00
|
|
|
/*
|
|
|
|
* Replenish the byte reservation required by moving the grant write head.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_log_regrant(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xlog_ticket *tic)
|
|
|
|
{
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2012-02-20 02:31:31 +00:00
|
|
|
int need_bytes;
|
|
|
|
int error = 0;
|
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2014-06-25 04:58:08 +00:00
|
|
|
return -EIO;
|
2012-02-20 02:31:31 +00:00
|
|
|
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(mp, xs_try_logspace);
|
2012-02-20 02:31:31 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* This is a new transaction on the ticket, so we need to change the
|
|
|
|
* transaction ID so that the next transaction has a different TID in
|
|
|
|
* the log. Just add one to the existing tid so that we can see chains
|
|
|
|
* of rolling transactions in the log easily.
|
|
|
|
*/
|
|
|
|
tic->t_tid++;
|
|
|
|
|
|
|
|
xlog_grant_push_ail(log, tic->t_unit_res);
|
|
|
|
|
|
|
|
tic->t_curr_res = tic->t_unit_res;
|
|
|
|
if (tic->t_cnt > 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
trace_xfs_log_regrant(log, tic);
|
|
|
|
|
|
|
|
error = xlog_grant_head_check(log, &log->l_write_head, tic,
|
|
|
|
&need_bytes);
|
|
|
|
if (error)
|
|
|
|
goto out_error;
|
|
|
|
|
|
|
|
xlog_grant_add_space(log, &log->l_write_head.grant, need_bytes);
|
|
|
|
trace_xfs_log_regrant_exit(log, tic);
|
|
|
|
xlog_verify_grant_tail(log);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_error:
|
|
|
|
/*
|
|
|
|
* If we are failing, make sure the ticket doesn't have any current
|
|
|
|
* reservations. We don't want to add this back when the ticket/
|
|
|
|
* transaction gets cancelled.
|
|
|
|
*/
|
|
|
|
tic->t_curr_res = 0;
|
|
|
|
tic->t_cnt = 0; /* ungrant will give back unit_res * t_cnt. */
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2018-08-03 15:17:54 +00:00
|
|
|
* Reserve log space and return a ticket corresponding to the reservation.
|
2012-02-20 02:31:31 +00:00
|
|
|
*
|
|
|
|
* Each reservation is going to reserve extra space for a log record header.
|
|
|
|
* When writes happen to the on-disk log, we don't subtract the length of the
|
|
|
|
* log record header from any reservation. By wasting space in each
|
|
|
|
* reservation, we prevent over allocation problems.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
xfs_log_reserve(
|
|
|
|
struct xfs_mount *mp,
|
2022-04-21 00:34:33 +00:00
|
|
|
int unit_bytes,
|
|
|
|
int cnt,
|
2012-02-20 02:31:31 +00:00
|
|
|
struct xlog_ticket **ticp,
|
2016-04-05 23:20:36 +00:00
|
|
|
bool permanent)
|
2012-02-20 02:31:31 +00:00
|
|
|
{
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2012-02-20 02:31:31 +00:00
|
|
|
struct xlog_ticket *tic;
|
|
|
|
int need_bytes;
|
|
|
|
int error = 0;
|
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2014-06-25 04:58:08 +00:00
|
|
|
return -EIO;
|
2012-02-20 02:31:31 +00:00
|
|
|
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(mp, xs_try_logspace);
|
2012-02-20 02:31:31 +00:00
|
|
|
|
|
|
|
ASSERT(*ticp == NULL);
|
2022-04-21 00:34:33 +00:00
|
|
|
tic = xlog_ticket_alloc(log, unit_bytes, cnt, permanent);
|
2012-02-20 02:31:31 +00:00
|
|
|
*ticp = tic;
|
|
|
|
|
2012-11-28 02:01:00 +00:00
|
|
|
xlog_grant_push_ail(log, tic->t_cnt ? tic->t_unit_res * tic->t_cnt
|
|
|
|
: tic->t_unit_res);
|
2012-02-20 02:31:31 +00:00
|
|
|
|
|
|
|
trace_xfs_log_reserve(log, tic);
|
|
|
|
|
|
|
|
error = xlog_grant_head_check(log, &log->l_reserve_head, tic,
|
|
|
|
&need_bytes);
|
|
|
|
if (error)
|
|
|
|
goto out_error;
|
|
|
|
|
|
|
|
xlog_grant_add_space(log, &log->l_reserve_head.grant, need_bytes);
|
|
|
|
xlog_grant_add_space(log, &log->l_write_head.grant, need_bytes);
|
|
|
|
trace_xfs_log_reserve_exit(log, tic);
|
|
|
|
xlog_verify_grant_tail(log);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
out_error:
|
|
|
|
/*
|
|
|
|
* If we are failing, make sure the ticket doesn't have any current
|
|
|
|
* reservations. We don't want to add this back when the ticket/
|
|
|
|
* transaction gets cancelled.
|
|
|
|
*/
|
|
|
|
tic->t_curr_res = 0;
|
|
|
|
tic->t_cnt = 0; /* ungrant will give back unit_res * t_cnt. */
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2021-08-11 01:00:40 +00:00
|
|
|
/*
|
|
|
|
* Run all the pending iclog callbacks and wake log force waiters and iclog
|
|
|
|
* space waiters so they can process the newly set shutdown state. We really
|
|
|
|
* don't care what order we process callbacks here because the log is shut down
|
2022-03-30 01:22:00 +00:00
|
|
|
* and so state cannot change on disk anymore. However, we cannot wake waiters
|
|
|
|
* until the callbacks have been processed because we may be in unmount and
|
|
|
|
* we must ensure that all AIL operations the callbacks perform have completed
|
|
|
|
* before we tear down the AIL.
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
*
|
|
|
|
* We avoid processing actively referenced iclogs so that we don't run callbacks
|
|
|
|
* while the iclog owner might still be preparing the iclog for IO submssion.
|
|
|
|
* These will be caught by xlog_state_iclog_release() and call this function
|
|
|
|
* again to process any callbacks that may have been added to that iclog.
|
2021-08-11 01:00:40 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xlog_state_shutdown_callbacks(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
struct xlog_in_core *iclog;
|
|
|
|
LIST_HEAD(cb_list);
|
|
|
|
|
|
|
|
iclog = log->l_iclog;
|
|
|
|
do {
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
if (atomic_read(&iclog->ic_refcnt)) {
|
|
|
|
/* Reference holder will re-run iclog callbacks. */
|
|
|
|
continue;
|
|
|
|
}
|
2021-08-11 01:00:40 +00:00
|
|
|
list_splice_init(&iclog->ic_callbacks, &cb_list);
|
2022-03-30 01:22:00 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
|
|
|
|
xlog_cil_process_committed(&cb_list);
|
|
|
|
|
|
|
|
spin_lock(&log->l_icloglock);
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
wake_up_all(&iclog->ic_write_wait);
|
2021-08-11 01:00:40 +00:00
|
|
|
wake_up_all(&iclog->ic_force_wait);
|
|
|
|
} while ((iclog = iclog->ic_next) != log->l_iclog);
|
|
|
|
|
|
|
|
wake_up_all(&log->l_flush_wait);
|
|
|
|
}
|
|
|
|
|
2019-10-14 17:36:41 +00:00
|
|
|
/*
|
|
|
|
* Flush iclog to disk if this is the last reference to the given iclog and the
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
* it is in the WANT_SYNC state.
|
|
|
|
*
|
|
|
|
* If XLOG_ICL_NEED_FUA is already set on the iclog, we need to ensure that the
|
|
|
|
* log tail is updated correctly. NEED_FUA indicates that the iclog will be
|
|
|
|
* written to stable storage, and implies that a commit record is contained
|
|
|
|
* within the iclog. We need to ensure that the log tail does not move beyond
|
|
|
|
* the tail that the first commit record in the iclog ordered against, otherwise
|
|
|
|
* correct recovery of that checkpoint becomes dependent on future operations
|
|
|
|
* performed on this iclog.
|
|
|
|
*
|
|
|
|
* Hence if NEED_FUA is set and the current iclog tail lsn is empty, write the
|
|
|
|
* current tail into iclog. Once the iclog tail is set, future operations must
|
|
|
|
* not modify it, otherwise they potentially violate ordering constraints for
|
|
|
|
* the checkpoint commit that wrote the initial tail lsn value. The tail lsn in
|
|
|
|
* the iclog will get zeroed on activation of the iclog after sync, so we
|
|
|
|
* always capture the tail lsn on the iclog on the first NEED_FUA release
|
|
|
|
* regardless of the number of active reference counts on this iclog.
|
2019-10-14 17:36:41 +00:00
|
|
|
*/
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
int
|
2019-10-14 17:36:41 +00:00
|
|
|
xlog_state_release_iclog(
|
|
|
|
struct xlog *log,
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
struct xlog_ticket *ticket)
|
2019-10-14 17:36:41 +00:00
|
|
|
{
|
2021-07-27 23:23:47 +00:00
|
|
|
xfs_lsn_t tail_lsn;
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
bool last_ref;
|
|
|
|
|
2019-10-14 17:36:41 +00:00
|
|
|
lockdep_assert_held(&log->l_icloglock);
|
|
|
|
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_release(iclog, _RET_IP_);
|
xfs: fix ordering violation between cache flushes and tail updates
There is a race between the new CIL async data device metadata IO
completion cache flush and the log tail in the iclog the flush
covers being updated. This can be seen by repeating generic/482 in a
loop and eventually log recovery fails with a failures such as this:
XFS (dm-3): Starting recovery (logdev: internal)
XFS (dm-3): bad inode magic/vsn daddr 228352 #0 (magic=0)
XFS (dm-3): Metadata corruption detected at xfs_inode_buf_verify+0x180/0x190, xfs_inode block 0x37c00 xfs_inode_buf_verify
XFS (dm-3): Unmount and run xfs_repair
XFS (dm-3): First 128 bytes of corrupted metadata buffer:
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
XFS (dm-3): metadata I/O error in "xlog_recover_items_pass2+0x55/0xc0" at daddr 0x37c00 len 32 error 117
Analysis of the logwrite replay shows that there were no writes to
the data device between the FUA @ write 124 and the FUA at write @
125, but log recovery @ 125 failed. The difference was the one log
write @ 125 moved the tail of the log forwards from (1,8) to (1,32)
and so the inode create intent in (1,8) was not replayed and so the
inode cluster was zero on disk when replay of the first inode item
in (1,32) was attempted.
What this meant was that the journal write that occurred at @ 125
did not ensure that metadata completed before the iclog was written
was correctly on stable storage. The tail of the log moved forward,
so IO must have been completed between the two iclog writes. This
means that there is a race condition between the unconditional async
cache flush in the CIL push work and the tail LSN that is written to
the iclog. This happens like so:
CIL push work AIL push work
------------- -------------
Add to committing list
start async data dev cache flush
.....
<flush completes>
<all writes to old tail lsn are stable>
xlog_write
.... push inode create buffer
<start IO>
.....
xlog_write(commit record)
.... <IO completes>
log tail moves
xlog_assign_tail_lsn()
start_lsn == commit_lsn
<no iclog preflush!>
xlog_state_release_iclog
__xlog_state_release_iclog()
<writes *new* tail_lsn into iclog>
xlog_sync()
....
submit_bio()
<tail in log moves forward without flushing written metadata>
Essentially, this can only occur if the commit iclog is issued
without a cache flush. If the iclog bio is submitted with
REQ_PREFLUSH, then it will guarantee that all the completed IO is
one stable storage before the iclog bio with the new tail LSN in it
is written to the log.
IOWs, the tail lsn that is written to the iclog needs to be sampled
*before* we issue the cache flush that guarantees all IO up to that
LSN has been completed.
To fix this without giving up the performance advantage of the
flush/FUA optimisations (e.g. g/482 runtime halves with 5.14-rc1
compared to 5.13), we need to ensure that we always issue a cache
flush if the tail LSN changes between the initial async flush and
the commit record being written. THis requires sampling the tail_lsn
before we start the flush, and then passing the sampled tail LSN to
xlog_state_release_iclog() so it can determine if the the tail LSN
has changed while writing the checkpoint. If the tail LSN has
changed, then it needs to set the NEED_FLUSH flag on the iclog and
we'll issue another cache flush before writing the iclog.
Fixes: eef983ffeae7 ("xfs: journal IO cache flush reductions")
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-27 23:23:48 +00:00
|
|
|
/*
|
|
|
|
* Grabbing the current log tail needs to be atomic w.r.t. the writing
|
|
|
|
* of the tail LSN into the iclog so we guarantee that the log tail does
|
xfs: drop async cache flushes from CIL commits.
Jan Kara reported a performance regression in dbench that he
bisected down to commit bad77c375e8d ("xfs: CIL checkpoint
flushes caches unconditionally").
Whilst developing the journal flush/fua optimisations this cache was
part of, it appeared to made a significant difference to
performance. However, now that this patchset has settled and all the
correctness issues fixed, there does not appear to be any
significant performance benefit to asynchronous cache flushes.
In fact, the opposite is true on some storage types and workloads,
where additional cache flushes that can occur from fsync heavy
workloads have measurable and significant impact on overall
throughput.
Local dbench testing shows little difference on dbench runs with
sync vs async cache flushes on either fast or slow SSD storage, and
no difference in streaming concurrent async transaction workloads
like fs-mark.
Fast NVME storage.
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 935.18 0.855 915.64 0.903
8 2404.51 6.873 2341.77 6.511
16 3003.42 6.460 2931.57 6.529
32 3697.23 7.939 3596.28 7.894
128 7237.43 15.495 7217.74 11.588
512 5079.24 90.587 5167.08 95.822
fsmark, 32 threads, create w/ 64 byte xattr w/32k logbsize
create chown unlink
async 1m41s 1m16s 2m03s
sync 1m40s 1m19s 1m54s
Slower SATA SSD storage:
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 78.59 15.792 83.78 10.729
8 367.88 92.067 404.63 59.943
16 564.51 72.524 602.71 76.089
32 831.66 105.984 870.26 110.482
128 1659.76 102.969 1624.73 91.356
512 2135.91 223.054 2603.07 161.160
fsmark, 16 threads, create w/32k logbsize
create unlink
async 5m06s 4m15s
sync 5m00s 4m22s
And on Jan's test machine:
5.18-rc8-vanilla 5.18-rc8-patched
Amean 1 71.22 ( 0.00%) 64.94 * 8.81%*
Amean 2 93.03 ( 0.00%) 84.80 * 8.85%*
Amean 4 150.54 ( 0.00%) 137.51 * 8.66%*
Amean 8 252.53 ( 0.00%) 242.24 * 4.08%*
Amean 16 454.13 ( 0.00%) 439.08 * 3.31%*
Amean 32 835.24 ( 0.00%) 829.74 * 0.66%*
Amean 64 1740.59 ( 0.00%) 1686.73 * 3.09%*
Performance and cache flush behaviour is restored to pre-regression
levels.
As such, we can now consider the async cache flush mechanism an
unnecessary exercise in premature optimisation and hence we can
now remove it and the infrastructure it requires completely.
Fixes: bad77c375e8d ("xfs: CIL checkpoint flushes caches unconditionally")
Reported-and-tested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:02 +00:00
|
|
|
* not move between the first time we know that the iclog needs to be
|
|
|
|
* made stable and when we eventually submit it.
|
xfs: fix ordering violation between cache flushes and tail updates
There is a race between the new CIL async data device metadata IO
completion cache flush and the log tail in the iclog the flush
covers being updated. This can be seen by repeating generic/482 in a
loop and eventually log recovery fails with a failures such as this:
XFS (dm-3): Starting recovery (logdev: internal)
XFS (dm-3): bad inode magic/vsn daddr 228352 #0 (magic=0)
XFS (dm-3): Metadata corruption detected at xfs_inode_buf_verify+0x180/0x190, xfs_inode block 0x37c00 xfs_inode_buf_verify
XFS (dm-3): Unmount and run xfs_repair
XFS (dm-3): First 128 bytes of corrupted metadata buffer:
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
XFS (dm-3): metadata I/O error in "xlog_recover_items_pass2+0x55/0xc0" at daddr 0x37c00 len 32 error 117
Analysis of the logwrite replay shows that there were no writes to
the data device between the FUA @ write 124 and the FUA at write @
125, but log recovery @ 125 failed. The difference was the one log
write @ 125 moved the tail of the log forwards from (1,8) to (1,32)
and so the inode create intent in (1,8) was not replayed and so the
inode cluster was zero on disk when replay of the first inode item
in (1,32) was attempted.
What this meant was that the journal write that occurred at @ 125
did not ensure that metadata completed before the iclog was written
was correctly on stable storage. The tail of the log moved forward,
so IO must have been completed between the two iclog writes. This
means that there is a race condition between the unconditional async
cache flush in the CIL push work and the tail LSN that is written to
the iclog. This happens like so:
CIL push work AIL push work
------------- -------------
Add to committing list
start async data dev cache flush
.....
<flush completes>
<all writes to old tail lsn are stable>
xlog_write
.... push inode create buffer
<start IO>
.....
xlog_write(commit record)
.... <IO completes>
log tail moves
xlog_assign_tail_lsn()
start_lsn == commit_lsn
<no iclog preflush!>
xlog_state_release_iclog
__xlog_state_release_iclog()
<writes *new* tail_lsn into iclog>
xlog_sync()
....
submit_bio()
<tail in log moves forward without flushing written metadata>
Essentially, this can only occur if the commit iclog is issued
without a cache flush. If the iclog bio is submitted with
REQ_PREFLUSH, then it will guarantee that all the completed IO is
one stable storage before the iclog bio with the new tail LSN in it
is written to the log.
IOWs, the tail lsn that is written to the iclog needs to be sampled
*before* we issue the cache flush that guarantees all IO up to that
LSN has been completed.
To fix this without giving up the performance advantage of the
flush/FUA optimisations (e.g. g/482 runtime halves with 5.14-rc1
compared to 5.13), we need to ensure that we always issue a cache
flush if the tail LSN changes between the initial async flush and
the commit record being written. THis requires sampling the tail_lsn
before we start the flush, and then passing the sampled tail LSN to
xlog_state_release_iclog() so it can determine if the the tail LSN
has changed while writing the checkpoint. If the tail LSN has
changed, then it needs to set the NEED_FLUSH flag on the iclog and
we'll issue another cache flush before writing the iclog.
Fixes: eef983ffeae7 ("xfs: journal IO cache flush reductions")
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-27 23:23:48 +00:00
|
|
|
*/
|
xfs: drop async cache flushes from CIL commits.
Jan Kara reported a performance regression in dbench that he
bisected down to commit bad77c375e8d ("xfs: CIL checkpoint
flushes caches unconditionally").
Whilst developing the journal flush/fua optimisations this cache was
part of, it appeared to made a significant difference to
performance. However, now that this patchset has settled and all the
correctness issues fixed, there does not appear to be any
significant performance benefit to asynchronous cache flushes.
In fact, the opposite is true on some storage types and workloads,
where additional cache flushes that can occur from fsync heavy
workloads have measurable and significant impact on overall
throughput.
Local dbench testing shows little difference on dbench runs with
sync vs async cache flushes on either fast or slow SSD storage, and
no difference in streaming concurrent async transaction workloads
like fs-mark.
Fast NVME storage.
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 935.18 0.855 915.64 0.903
8 2404.51 6.873 2341.77 6.511
16 3003.42 6.460 2931.57 6.529
32 3697.23 7.939 3596.28 7.894
128 7237.43 15.495 7217.74 11.588
512 5079.24 90.587 5167.08 95.822
fsmark, 32 threads, create w/ 64 byte xattr w/32k logbsize
create chown unlink
async 1m41s 1m16s 2m03s
sync 1m40s 1m19s 1m54s
Slower SATA SSD storage:
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 78.59 15.792 83.78 10.729
8 367.88 92.067 404.63 59.943
16 564.51 72.524 602.71 76.089
32 831.66 105.984 870.26 110.482
128 1659.76 102.969 1624.73 91.356
512 2135.91 223.054 2603.07 161.160
fsmark, 16 threads, create w/32k logbsize
create unlink
async 5m06s 4m15s
sync 5m00s 4m22s
And on Jan's test machine:
5.18-rc8-vanilla 5.18-rc8-patched
Amean 1 71.22 ( 0.00%) 64.94 * 8.81%*
Amean 2 93.03 ( 0.00%) 84.80 * 8.85%*
Amean 4 150.54 ( 0.00%) 137.51 * 8.66%*
Amean 8 252.53 ( 0.00%) 242.24 * 4.08%*
Amean 16 454.13 ( 0.00%) 439.08 * 3.31%*
Amean 32 835.24 ( 0.00%) 829.74 * 0.66%*
Amean 64 1740.59 ( 0.00%) 1686.73 * 3.09%*
Performance and cache flush behaviour is restored to pre-regression
levels.
As such, we can now consider the async cache flush mechanism an
unnecessary exercise in premature optimisation and hence we can
now remove it and the infrastructure it requires completely.
Fixes: bad77c375e8d ("xfs: CIL checkpoint flushes caches unconditionally")
Reported-and-tested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:02 +00:00
|
|
|
if ((iclog->ic_state == XLOG_STATE_WANT_SYNC ||
|
|
|
|
(iclog->ic_flags & XLOG_ICL_NEED_FUA)) &&
|
|
|
|
!iclog->ic_header.h_tail_lsn) {
|
xfs: fix ordering violation between cache flushes and tail updates
There is a race between the new CIL async data device metadata IO
completion cache flush and the log tail in the iclog the flush
covers being updated. This can be seen by repeating generic/482 in a
loop and eventually log recovery fails with a failures such as this:
XFS (dm-3): Starting recovery (logdev: internal)
XFS (dm-3): bad inode magic/vsn daddr 228352 #0 (magic=0)
XFS (dm-3): Metadata corruption detected at xfs_inode_buf_verify+0x180/0x190, xfs_inode block 0x37c00 xfs_inode_buf_verify
XFS (dm-3): Unmount and run xfs_repair
XFS (dm-3): First 128 bytes of corrupted metadata buffer:
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
XFS (dm-3): metadata I/O error in "xlog_recover_items_pass2+0x55/0xc0" at daddr 0x37c00 len 32 error 117
Analysis of the logwrite replay shows that there were no writes to
the data device between the FUA @ write 124 and the FUA at write @
125, but log recovery @ 125 failed. The difference was the one log
write @ 125 moved the tail of the log forwards from (1,8) to (1,32)
and so the inode create intent in (1,8) was not replayed and so the
inode cluster was zero on disk when replay of the first inode item
in (1,32) was attempted.
What this meant was that the journal write that occurred at @ 125
did not ensure that metadata completed before the iclog was written
was correctly on stable storage. The tail of the log moved forward,
so IO must have been completed between the two iclog writes. This
means that there is a race condition between the unconditional async
cache flush in the CIL push work and the tail LSN that is written to
the iclog. This happens like so:
CIL push work AIL push work
------------- -------------
Add to committing list
start async data dev cache flush
.....
<flush completes>
<all writes to old tail lsn are stable>
xlog_write
.... push inode create buffer
<start IO>
.....
xlog_write(commit record)
.... <IO completes>
log tail moves
xlog_assign_tail_lsn()
start_lsn == commit_lsn
<no iclog preflush!>
xlog_state_release_iclog
__xlog_state_release_iclog()
<writes *new* tail_lsn into iclog>
xlog_sync()
....
submit_bio()
<tail in log moves forward without flushing written metadata>
Essentially, this can only occur if the commit iclog is issued
without a cache flush. If the iclog bio is submitted with
REQ_PREFLUSH, then it will guarantee that all the completed IO is
one stable storage before the iclog bio with the new tail LSN in it
is written to the log.
IOWs, the tail lsn that is written to the iclog needs to be sampled
*before* we issue the cache flush that guarantees all IO up to that
LSN has been completed.
To fix this without giving up the performance advantage of the
flush/FUA optimisations (e.g. g/482 runtime halves with 5.14-rc1
compared to 5.13), we need to ensure that we always issue a cache
flush if the tail LSN changes between the initial async flush and
the commit record being written. THis requires sampling the tail_lsn
before we start the flush, and then passing the sampled tail LSN to
xlog_state_release_iclog() so it can determine if the the tail LSN
has changed while writing the checkpoint. If the tail LSN has
changed, then it needs to set the NEED_FLUSH flag on the iclog and
we'll issue another cache flush before writing the iclog.
Fixes: eef983ffeae7 ("xfs: journal IO cache flush reductions")
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-27 23:23:48 +00:00
|
|
|
tail_lsn = xlog_assign_tail_lsn(log->l_mp);
|
xfs: drop async cache flushes from CIL commits.
Jan Kara reported a performance regression in dbench that he
bisected down to commit bad77c375e8d ("xfs: CIL checkpoint
flushes caches unconditionally").
Whilst developing the journal flush/fua optimisations this cache was
part of, it appeared to made a significant difference to
performance. However, now that this patchset has settled and all the
correctness issues fixed, there does not appear to be any
significant performance benefit to asynchronous cache flushes.
In fact, the opposite is true on some storage types and workloads,
where additional cache flushes that can occur from fsync heavy
workloads have measurable and significant impact on overall
throughput.
Local dbench testing shows little difference on dbench runs with
sync vs async cache flushes on either fast or slow SSD storage, and
no difference in streaming concurrent async transaction workloads
like fs-mark.
Fast NVME storage.
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 935.18 0.855 915.64 0.903
8 2404.51 6.873 2341.77 6.511
16 3003.42 6.460 2931.57 6.529
32 3697.23 7.939 3596.28 7.894
128 7237.43 15.495 7217.74 11.588
512 5079.24 90.587 5167.08 95.822
fsmark, 32 threads, create w/ 64 byte xattr w/32k logbsize
create chown unlink
async 1m41s 1m16s 2m03s
sync 1m40s 1m19s 1m54s
Slower SATA SSD storage:
From `dbench -t 30`, CIL scale:
clients async sync
BW Latency BW Latency
1 78.59 15.792 83.78 10.729
8 367.88 92.067 404.63 59.943
16 564.51 72.524 602.71 76.089
32 831.66 105.984 870.26 110.482
128 1659.76 102.969 1624.73 91.356
512 2135.91 223.054 2603.07 161.160
fsmark, 16 threads, create w/32k logbsize
create unlink
async 5m06s 4m15s
sync 5m00s 4m22s
And on Jan's test machine:
5.18-rc8-vanilla 5.18-rc8-patched
Amean 1 71.22 ( 0.00%) 64.94 * 8.81%*
Amean 2 93.03 ( 0.00%) 84.80 * 8.85%*
Amean 4 150.54 ( 0.00%) 137.51 * 8.66%*
Amean 8 252.53 ( 0.00%) 242.24 * 4.08%*
Amean 16 454.13 ( 0.00%) 439.08 * 3.31%*
Amean 32 835.24 ( 0.00%) 829.74 * 0.66%*
Amean 64 1740.59 ( 0.00%) 1686.73 * 3.09%*
Performance and cache flush behaviour is restored to pre-regression
levels.
As such, we can now consider the async cache flush mechanism an
unnecessary exercise in premature optimisation and hence we can
now remove it and the infrastructure it requires completely.
Fixes: bad77c375e8d ("xfs: CIL checkpoint flushes caches unconditionally")
Reported-and-tested-by: Jan Kara <jack@suse.cz>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:02 +00:00
|
|
|
iclog->ic_header.h_tail_lsn = cpu_to_be64(tail_lsn);
|
xfs: fix ordering violation between cache flushes and tail updates
There is a race between the new CIL async data device metadata IO
completion cache flush and the log tail in the iclog the flush
covers being updated. This can be seen by repeating generic/482 in a
loop and eventually log recovery fails with a failures such as this:
XFS (dm-3): Starting recovery (logdev: internal)
XFS (dm-3): bad inode magic/vsn daddr 228352 #0 (magic=0)
XFS (dm-3): Metadata corruption detected at xfs_inode_buf_verify+0x180/0x190, xfs_inode block 0x37c00 xfs_inode_buf_verify
XFS (dm-3): Unmount and run xfs_repair
XFS (dm-3): First 128 bytes of corrupted metadata buffer:
00000000: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000010: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000050: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000070: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
XFS (dm-3): metadata I/O error in "xlog_recover_items_pass2+0x55/0xc0" at daddr 0x37c00 len 32 error 117
Analysis of the logwrite replay shows that there were no writes to
the data device between the FUA @ write 124 and the FUA at write @
125, but log recovery @ 125 failed. The difference was the one log
write @ 125 moved the tail of the log forwards from (1,8) to (1,32)
and so the inode create intent in (1,8) was not replayed and so the
inode cluster was zero on disk when replay of the first inode item
in (1,32) was attempted.
What this meant was that the journal write that occurred at @ 125
did not ensure that metadata completed before the iclog was written
was correctly on stable storage. The tail of the log moved forward,
so IO must have been completed between the two iclog writes. This
means that there is a race condition between the unconditional async
cache flush in the CIL push work and the tail LSN that is written to
the iclog. This happens like so:
CIL push work AIL push work
------------- -------------
Add to committing list
start async data dev cache flush
.....
<flush completes>
<all writes to old tail lsn are stable>
xlog_write
.... push inode create buffer
<start IO>
.....
xlog_write(commit record)
.... <IO completes>
log tail moves
xlog_assign_tail_lsn()
start_lsn == commit_lsn
<no iclog preflush!>
xlog_state_release_iclog
__xlog_state_release_iclog()
<writes *new* tail_lsn into iclog>
xlog_sync()
....
submit_bio()
<tail in log moves forward without flushing written metadata>
Essentially, this can only occur if the commit iclog is issued
without a cache flush. If the iclog bio is submitted with
REQ_PREFLUSH, then it will guarantee that all the completed IO is
one stable storage before the iclog bio with the new tail LSN in it
is written to the log.
IOWs, the tail lsn that is written to the iclog needs to be sampled
*before* we issue the cache flush that guarantees all IO up to that
LSN has been completed.
To fix this without giving up the performance advantage of the
flush/FUA optimisations (e.g. g/482 runtime halves with 5.14-rc1
compared to 5.13), we need to ensure that we always issue a cache
flush if the tail LSN changes between the initial async flush and
the commit record being written. THis requires sampling the tail_lsn
before we start the flush, and then passing the sampled tail LSN to
xlog_state_release_iclog() so it can determine if the the tail LSN
has changed while writing the checkpoint. If the tail LSN has
changed, then it needs to set the NEED_FLUSH flag on the iclog and
we'll issue another cache flush before writing the iclog.
Fixes: eef983ffeae7 ("xfs: journal IO cache flush reductions")
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-27 23:23:48 +00:00
|
|
|
}
|
|
|
|
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
last_ref = atomic_dec_and_test(&iclog->ic_refcnt);
|
|
|
|
|
|
|
|
if (xlog_is_shutdown(log)) {
|
|
|
|
/*
|
|
|
|
* If there are no more references to this iclog, process the
|
|
|
|
* pending iclog callbacks that were waiting on the release of
|
|
|
|
* this iclog.
|
|
|
|
*/
|
2022-03-30 01:22:00 +00:00
|
|
|
if (last_ref)
|
xfs: don't run shutdown callbacks on active iclogs
When the log is shutdown, it currently walks all the iclogs and runs
callbacks that are attached to the iclogs, regardless of whether the
iclog is queued for IO completion or not. This creates a problem for
contexts attaching callbacks to iclogs in that a racing shutdown can
run the callbacks even before the attaching context has finished
processing the iclog and releasing it for IO submission.
If the callback processing of the iclog frees the structure that is
attached to the iclog, then this leads to an UAF scenario that can
only be protected against by holding the icloglock from the point
callbacks are attached through to the release of the iclog. While we
currently do this, it is not practical or sustainable.
Hence we need to make shutdown processing the responsibility of the
context that holds active references to the iclog. We know that the
contexts attaching callbacks to the iclog must have active
references to the iclog, and that means they must be in either
ACTIVE or WANT_SYNC states. xlog_state_do_callback() will skip over
iclogs in these states -except- when the log is shut down.
xlog_state_do_callback() checks the state of the iclogs while
holding the icloglock, therefore the reference count/state change
that occurs in xlog_state_release_iclog() after the callbacks are
atomic w.r.t. shutdown processing.
We can't push the responsibility of callback cleanup onto the CIL
context because we can have ACTIVE iclogs that have callbacks
attached that have already been released. Hence we really need to
internalise the cleanup of callbacks into xlog_state_release_iclog()
processing.
Indeed, we already have that internalisation via:
xlog_state_release_iclog
drop last reference
->SYNCING
xlog_sync
xlog_write_iclog
if (log_is_shutdown)
xlog_state_done_syncing()
xlog_state_do_callback()
<process shutdown on iclog that is now in SYNCING state>
The problem is that xlog_state_release_iclog() aborts before doing
anything if the log is already shut down. It assumes that the
callbacks have already been cleaned up, and it doesn't need to do
any cleanup.
Hence the fix is to remove the xlog_is_shutdown() check from
xlog_state_release_iclog() so that reference counts are correctly
released from the iclogs, and when the reference count is zero we
always transition to SYNCING if the log is shut down. Hence we'll
always enter the xlog_sync() path in a shutdown and eventually end
up erroring out the iclog IO and running xlog_state_do_callback() to
process the callbacks attached to the iclog.
This allows us to stop processing referenced ACTIVE/WANT_SYNC iclogs
directly in the shutdown code, and in doing so gets rid of the UAF
vector that currently exists. This then decouples the adding of
callbacks to the iclogs from xlog_state_release_iclog() as we
guarantee that xlog_state_release_iclog() will process the callbacks
if the log has been shut down before xlog_state_release_iclog() has
been called.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
xlog_state_shutdown_callbacks(log);
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!last_ref)
|
2021-07-27 23:23:47 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (iclog->ic_state != XLOG_STATE_WANT_SYNC) {
|
|
|
|
ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
|
|
|
|
return 0;
|
2019-10-14 17:36:41 +00:00
|
|
|
}
|
|
|
|
|
2021-07-27 23:23:47 +00:00
|
|
|
iclog->ic_state = XLOG_STATE_SYNCING;
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
xlog_verify_tail_lsn(log, iclog);
|
2021-07-27 23:23:47 +00:00
|
|
|
trace_xlog_iclog_syncing(iclog, _RET_IP_);
|
|
|
|
|
|
|
|
spin_unlock(&log->l_icloglock);
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
xlog_sync(log, iclog, ticket);
|
2021-07-27 23:23:47 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2019-10-14 17:36:41 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Mount a log filesystem
|
|
|
|
*
|
|
|
|
* mp - ubiquitous xfs mount point structure
|
|
|
|
* log_target - buftarg of on-disk log device
|
|
|
|
* blk_offset - Start block # where block size is 512 bytes (BBSIZE)
|
|
|
|
* num_bblocks - Number of BBSIZE blocks in on-disk log
|
|
|
|
*
|
|
|
|
* Return error or zero.
|
|
|
|
*/
|
|
|
|
int
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
xfs_log_mount(
|
|
|
|
xfs_mount_t *mp,
|
|
|
|
xfs_buftarg_t *log_target,
|
|
|
|
xfs_daddr_t blk_offset,
|
|
|
|
int num_bblks)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-08-11 00:59:02 +00:00
|
|
|
struct xlog *log;
|
2021-08-19 01:46:37 +00:00
|
|
|
bool fatal = xfs_has_crc(mp);
|
2013-08-12 10:50:03 +00:00
|
|
|
int error = 0;
|
|
|
|
int min_logfsbs;
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (!xfs_has_norecovery(mp)) {
|
2014-05-05 06:18:37 +00:00
|
|
|
xfs_notice(mp, "Mounting V%d Filesystem",
|
|
|
|
XFS_SB_VERSION_NUM(&mp->m_sb));
|
|
|
|
} else {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_notice(mp,
|
2014-05-05 06:18:37 +00:00
|
|
|
"Mounting V%d filesystem in no-recovery mode. Filesystem will be inconsistent.",
|
|
|
|
XFS_SB_VERSION_NUM(&mp->m_sb));
|
2021-08-19 01:46:52 +00:00
|
|
|
ASSERT(xfs_is_readonly(mp));
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2021-08-11 00:59:02 +00:00
|
|
|
log = xlog_alloc_log(mp, log_target, blk_offset, num_bblks);
|
|
|
|
if (IS_ERR(log)) {
|
|
|
|
error = PTR_ERR(log);
|
2008-11-10 05:50:24 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2021-08-11 00:59:02 +00:00
|
|
|
mp->m_log = log;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2013-08-12 10:50:03 +00:00
|
|
|
/*
|
|
|
|
* Validate the given log space and drop a critical message via syslog
|
|
|
|
* if the log size is too small that would lead to some unexpected
|
|
|
|
* situations in transaction log space reservation stage.
|
|
|
|
*
|
|
|
|
* Note: we can't just reject the mount if the validation fails. This
|
|
|
|
* would mean that people would have to downgrade their kernel just to
|
|
|
|
* remedy the situation as there is no way to grow the log (short of
|
|
|
|
* black magic surgery with xfs_db).
|
|
|
|
*
|
|
|
|
* We can, however, reject mounts for CRC format filesystems, as the
|
|
|
|
* mkfs binary being used to make the filesystem should never create a
|
|
|
|
* filesystem with a log that is too small.
|
|
|
|
*/
|
|
|
|
min_logfsbs = xfs_log_calc_minimum_size(mp);
|
|
|
|
|
|
|
|
if (mp->m_sb.sb_logblocks < min_logfsbs) {
|
|
|
|
xfs_warn(mp,
|
|
|
|
"Log size %d blocks too small, minimum size is %d blocks",
|
|
|
|
mp->m_sb.sb_logblocks, min_logfsbs);
|
2014-06-25 04:58:08 +00:00
|
|
|
error = -EINVAL;
|
2013-08-12 10:50:03 +00:00
|
|
|
} else if (mp->m_sb.sb_logblocks > XFS_MAX_LOG_BLOCKS) {
|
|
|
|
xfs_warn(mp,
|
|
|
|
"Log size %d blocks too large, maximum size is %lld blocks",
|
|
|
|
mp->m_sb.sb_logblocks, XFS_MAX_LOG_BLOCKS);
|
2014-06-25 04:58:08 +00:00
|
|
|
error = -EINVAL;
|
2013-08-12 10:50:03 +00:00
|
|
|
} else if (XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks) > XFS_MAX_LOG_BYTES) {
|
|
|
|
xfs_warn(mp,
|
|
|
|
"log size %lld bytes too large, maximum size is %lld bytes",
|
|
|
|
XFS_FSB_TO_B(mp, mp->m_sb.sb_logblocks),
|
|
|
|
XFS_MAX_LOG_BYTES);
|
2014-06-25 04:58:08 +00:00
|
|
|
error = -EINVAL;
|
2017-10-25 23:59:43 +00:00
|
|
|
} else if (mp->m_sb.sb_logsunit > 1 &&
|
|
|
|
mp->m_sb.sb_logsunit % mp->m_sb.sb_blocksize) {
|
|
|
|
xfs_warn(mp,
|
|
|
|
"log stripe unit %u bytes must be a multiple of block size",
|
|
|
|
mp->m_sb.sb_logsunit);
|
|
|
|
error = -EINVAL;
|
|
|
|
fatal = true;
|
2013-08-12 10:50:03 +00:00
|
|
|
}
|
|
|
|
if (error) {
|
2017-10-25 23:59:43 +00:00
|
|
|
/*
|
|
|
|
* Log check errors are always fatal on v5; or whenever bad
|
|
|
|
* metadata leads to a crash.
|
|
|
|
*/
|
|
|
|
if (fatal) {
|
2013-08-12 10:50:03 +00:00
|
|
|
xfs_crit(mp, "AAIEEE! Log failed size checks. Abort!");
|
|
|
|
ASSERT(0);
|
|
|
|
goto out_free_log;
|
|
|
|
}
|
2015-07-29 01:52:04 +00:00
|
|
|
xfs_crit(mp, "Log size out of supported range.");
|
2013-08-12 10:50:03 +00:00
|
|
|
xfs_crit(mp,
|
2015-07-29 01:52:04 +00:00
|
|
|
"Continuing onwards, but if log hangs are experienced then please report this message in the bug report.");
|
2013-08-12 10:50:03 +00:00
|
|
|
}
|
|
|
|
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
/*
|
|
|
|
* Initialize the AIL now we have a log.
|
|
|
|
*/
|
|
|
|
error = xfs_trans_ail_init(mp);
|
|
|
|
if (error) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp, "AIL initialisation failed: error %d", error);
|
2009-02-12 18:55:48 +00:00
|
|
|
goto out_free_log;
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
}
|
2021-08-11 00:59:02 +00:00
|
|
|
log->l_ailp = mp->m_ail;
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* skip log recovery on a norecovery mount. pretend it all
|
|
|
|
* just worked.
|
|
|
|
*/
|
2021-08-19 01:46:52 +00:00
|
|
|
if (!xfs_has_norecovery(mp)) {
|
2021-08-19 01:46:52 +00:00
|
|
|
/*
|
|
|
|
* log recovery ignores readonly state and so we need to clear
|
|
|
|
* mount-based read only state so it can write to disk.
|
|
|
|
*/
|
|
|
|
bool readonly = test_and_clear_bit(XFS_OPSTATE_READONLY,
|
|
|
|
&mp->m_opstate);
|
2021-08-11 00:59:02 +00:00
|
|
|
error = xlog_recover(log);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (readonly)
|
2021-08-19 01:46:52 +00:00
|
|
|
set_bit(XFS_OPSTATE_READONLY, &mp->m_opstate);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (error) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp, "log mount/recovery failed: error %d",
|
|
|
|
error);
|
2021-08-11 00:59:02 +00:00
|
|
|
xlog_recover_cancel(log);
|
2009-02-12 18:55:48 +00:00
|
|
|
goto out_destroy_ail;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-11 00:59:02 +00:00
|
|
|
error = xfs_sysfs_init(&log->l_kobj, &xfs_log_ktype, &mp->m_kobj,
|
2014-07-14 22:07:29 +00:00
|
|
|
"log");
|
|
|
|
if (error)
|
|
|
|
goto out_destroy_ail;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Normal transactions can now occur */
|
2021-08-11 00:59:02 +00:00
|
|
|
clear_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
/*
|
|
|
|
* Now the log has been fully initialised and we know were our
|
|
|
|
* space grant counters are, we can initialise the permanent ticket
|
|
|
|
* needed for delayed logging to work.
|
|
|
|
*/
|
2021-08-11 00:59:02 +00:00
|
|
|
xlog_cil_init_post_recovery(log);
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
2009-02-12 18:55:48 +00:00
|
|
|
|
|
|
|
out_destroy_ail:
|
|
|
|
xfs_trans_ail_destroy(mp);
|
|
|
|
out_free_log:
|
2021-08-11 00:59:02 +00:00
|
|
|
xlog_dealloc_log(log);
|
2008-11-10 05:50:24 +00:00
|
|
|
out:
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
return error;
|
2009-02-12 18:55:48 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2012-10-08 10:56:02 +00:00
|
|
|
* Finish the recovery of the file system. This is separate from the
|
|
|
|
* xfs_log_mount() call, because it depends on the code in xfs_mountfs() to read
|
|
|
|
* in the root and real-time bitmap inodes between calling xfs_log_mount() and
|
|
|
|
* here.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
2012-10-08 10:56:02 +00:00
|
|
|
* If we finish recovery successfully, start the background log work. If we are
|
|
|
|
* not doing recovery, then we have a RO filesystem and we don't need to start
|
|
|
|
* it.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
int
|
2015-08-18 23:58:36 +00:00
|
|
|
xfs_log_mount_finish(
|
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-08-11 00:59:02 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2021-08-19 01:46:52 +00:00
|
|
|
bool readonly;
|
2021-08-11 00:59:02 +00:00
|
|
|
int error = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_has_norecovery(mp)) {
|
2021-08-19 01:46:52 +00:00
|
|
|
ASSERT(xfs_is_readonly(mp));
|
2015-08-18 23:58:36 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2021-08-19 01:46:52 +00:00
|
|
|
/*
|
|
|
|
* log recovery ignores readonly state and so we need to clear
|
|
|
|
* mount-based read only state so it can write to disk.
|
|
|
|
*/
|
|
|
|
readonly = test_and_clear_bit(XFS_OPSTATE_READONLY, &mp->m_opstate);
|
|
|
|
|
2017-08-10 21:20:28 +00:00
|
|
|
/*
|
|
|
|
* During the second phase of log recovery, we need iget and
|
|
|
|
* iput to behave like they do for an active filesystem.
|
|
|
|
* xfs_fs_drop_inode needs to be able to prevent the deletion
|
|
|
|
* of inodes before we're done replaying log items on those
|
|
|
|
* inodes. Turn it off immediately after recovery finishes
|
|
|
|
* so that we don't leak the quota inodes if subsequent mount
|
|
|
|
* activities fail.
|
2017-08-19 01:08:25 +00:00
|
|
|
*
|
|
|
|
* We let all inodes involved in redo item processing end up on
|
|
|
|
* the LRU instead of being evicted immediately so that if we do
|
|
|
|
* something to an unlinked inode, the irele won't cause
|
|
|
|
* premature truncation and freeing of the inode, which results
|
|
|
|
* in log recovery failure. We have to evict the unreferenced
|
2017-11-27 21:05:09 +00:00
|
|
|
* lru inodes after clearing SB_ACTIVE because we don't
|
2017-08-19 01:08:25 +00:00
|
|
|
* otherwise clean up the lru if there's a subsequent failure in
|
|
|
|
* xfs_mountfs, which leads to us leaking the inodes if nothing
|
|
|
|
* else (e.g. quotacheck) references the inodes before the
|
|
|
|
* mount failure occurs.
|
2017-08-10 21:20:28 +00:00
|
|
|
*/
|
2017-11-27 21:05:09 +00:00
|
|
|
mp->m_super->s_flags |= SB_ACTIVE;
|
xfs: log worker needs to start before intent/unlink recovery
After 963 iterations of generic/530, it deadlocked during recovery
on a pinned inode cluster buffer like so:
XFS (pmem1): Starting recovery (logdev: internal)
INFO: task kworker/8:0:306037 blocked for more than 122 seconds.
Not tainted 5.17.0-rc6-dgc+ #975
"echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
task:kworker/8:0 state:D stack:13024 pid:306037 ppid: 2 flags:0x00004000
Workqueue: xfs-inodegc/pmem1 xfs_inodegc_worker
Call Trace:
<TASK>
__schedule+0x30d/0x9e0
schedule+0x55/0xd0
schedule_timeout+0x114/0x160
__down+0x99/0xf0
down+0x5e/0x70
xfs_buf_lock+0x36/0xf0
xfs_buf_find+0x418/0x850
xfs_buf_get_map+0x47/0x380
xfs_buf_read_map+0x54/0x240
xfs_trans_read_buf_map+0x1bd/0x490
xfs_imap_to_bp+0x4f/0x70
xfs_iunlink_map_ino+0x66/0xd0
xfs_iunlink_map_prev.constprop.0+0x148/0x2f0
xfs_iunlink_remove_inode+0xf2/0x1d0
xfs_inactive_ifree+0x1a3/0x900
xfs_inode_unlink+0xcc/0x210
xfs_inodegc_worker+0x1ac/0x2f0
process_one_work+0x1ac/0x390
worker_thread+0x56/0x3c0
kthread+0xf6/0x120
ret_from_fork+0x1f/0x30
</TASK>
task:mount state:D stack:13248 pid:324509 ppid:324233 flags:0x00004000
Call Trace:
<TASK>
__schedule+0x30d/0x9e0
schedule+0x55/0xd0
schedule_timeout+0x114/0x160
__down+0x99/0xf0
down+0x5e/0x70
xfs_buf_lock+0x36/0xf0
xfs_buf_find+0x418/0x850
xfs_buf_get_map+0x47/0x380
xfs_buf_read_map+0x54/0x240
xfs_trans_read_buf_map+0x1bd/0x490
xfs_imap_to_bp+0x4f/0x70
xfs_iget+0x300/0xb40
xlog_recover_process_one_iunlink+0x4c/0x170
xlog_recover_process_iunlinks.isra.0+0xee/0x130
xlog_recover_finish+0x57/0x110
xfs_log_mount_finish+0xfc/0x1e0
xfs_mountfs+0x540/0x910
xfs_fs_fill_super+0x495/0x850
get_tree_bdev+0x171/0x270
xfs_fs_get_tree+0x15/0x20
vfs_get_tree+0x24/0xc0
path_mount+0x304/0xba0
__x64_sys_mount+0x108/0x140
do_syscall_64+0x35/0x80
entry_SYSCALL_64_after_hwframe+0x44/0xae
</TASK>
task:xfsaild/pmem1 state:D stack:14544 pid:324525 ppid: 2 flags:0x00004000
Call Trace:
<TASK>
__schedule+0x30d/0x9e0
schedule+0x55/0xd0
io_schedule+0x4b/0x80
xfs_buf_wait_unpin+0x9e/0xf0
__xfs_buf_submit+0x14a/0x230
xfs_buf_delwri_submit_buffers+0x107/0x280
xfs_buf_delwri_submit_nowait+0x10/0x20
xfsaild+0x27e/0x9d0
kthread+0xf6/0x120
ret_from_fork+0x1f/0x30
We have the mount process waiting on an inode cluster buffer read,
inodegc doing unlink waiting on the same inode cluster buffer, and
the AIL push thread blocked in writeback waiting for the inode
cluster buffer to become unpinned.
What has happened here is that the AIL push thread has raced with
the inodegc process modifying, committing and pinning the inode
cluster buffer here in xfs_buf_delwri_submit_buffers() here:
blk_start_plug(&plug);
list_for_each_entry_safe(bp, n, buffer_list, b_list) {
if (!wait_list) {
if (xfs_buf_ispinned(bp)) {
pinned++;
continue;
}
Here >>>>>>
if (!xfs_buf_trylock(bp))
continue;
Basically, the AIL has found the buffer wasn't pinned and got the
lock without blocking, but then the buffer was pinned. This implies
the processing here was pre-empted between the pin check and the
lock, because the pin count can only be increased while holding the
buffer locked. Hence when it has gone to submit the IO, it has
blocked waiting for the buffer to be unpinned.
With all executing threads now waiting on the buffer to be unpinned,
we normally get out of situations like this via the background log
worker issuing a log force which will unpinned stuck buffers like
this. But at this point in recovery, we haven't started the log
worker. In fact, the first thing we do after processing intents and
unlinked inodes is *start the log worker*. IOWs, we start it too
late to have it break deadlocks like this.
Avoid this and any other similar deadlock vectors in intent and
unlinked inode recovery by starting the log worker before we recover
intents and unlinked inodes. This part of recovery runs as though
the filesystem is fully active, so we really should have the same
infrastructure running as we normally do at runtime.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Chandan Babu R <chandan.babu@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-17 16:09:10 +00:00
|
|
|
xfs_log_work_queue(mp);
|
2021-08-11 00:59:02 +00:00
|
|
|
if (xlog_recovery_needed(log))
|
2021-08-11 00:59:02 +00:00
|
|
|
error = xlog_recover_finish(log);
|
2017-11-27 21:05:09 +00:00
|
|
|
mp->m_super->s_flags &= ~SB_ACTIVE;
|
2017-08-19 01:08:25 +00:00
|
|
|
evict_inodes(mp->m_super);
|
2015-08-18 23:58:36 +00:00
|
|
|
|
2017-10-26 16:31:16 +00:00
|
|
|
/*
|
|
|
|
* Drain the buffer LRU after log recovery. This is required for v4
|
|
|
|
* filesystems to avoid leaving around buffers with NULL verifier ops,
|
|
|
|
* but we do it unconditionally to make sure we're always in a clean
|
|
|
|
* cache state after mount.
|
|
|
|
*
|
|
|
|
* Don't push in the error case because the AIL may have pending intents
|
|
|
|
* that aren't removed until recovery is cancelled.
|
|
|
|
*/
|
2021-08-11 00:59:02 +00:00
|
|
|
if (xlog_recovery_needed(log)) {
|
2021-08-11 00:59:02 +00:00
|
|
|
if (!error) {
|
|
|
|
xfs_log_force(mp, XFS_LOG_SYNC);
|
|
|
|
xfs_ail_push_all_sync(mp->m_ail);
|
|
|
|
}
|
|
|
|
xfs_notice(mp, "Ending recovery (logdev: %s)",
|
|
|
|
mp->m_logname ? mp->m_logname : "internal");
|
|
|
|
} else {
|
|
|
|
xfs_info(mp, "Ending clean mount");
|
2017-10-26 16:31:16 +00:00
|
|
|
}
|
2021-01-23 00:48:19 +00:00
|
|
|
xfs_buftarg_drain(mp->m_ddev_targp);
|
2017-10-26 16:31:16 +00:00
|
|
|
|
2021-08-11 00:59:02 +00:00
|
|
|
clear_bit(XLOG_RECOVERY_NEEDED, &log->l_opstate);
|
2017-08-09 01:21:49 +00:00
|
|
|
if (readonly)
|
2021-08-19 01:46:52 +00:00
|
|
|
set_bit(XFS_OPSTATE_READONLY, &mp->m_opstate);
|
2017-08-09 01:21:49 +00:00
|
|
|
|
2021-06-18 18:57:07 +00:00
|
|
|
/* Make sure the log is dead if we're returning failure. */
|
2021-08-11 00:59:02 +00:00
|
|
|
ASSERT(!error || xlog_is_shutdown(log));
|
2021-06-18 18:57:07 +00:00
|
|
|
|
2015-08-18 23:58:36 +00:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The mount has failed. Cancel the recovery if it hasn't completed and destroy
|
|
|
|
* the log.
|
|
|
|
*/
|
2019-07-03 14:34:18 +00:00
|
|
|
void
|
2015-08-18 23:58:36 +00:00
|
|
|
xfs_log_mount_cancel(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
2019-07-03 14:34:18 +00:00
|
|
|
xlog_recover_cancel(mp->m_log);
|
2015-08-18 23:58:36 +00:00
|
|
|
xfs_log_unmount(mp);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2021-07-27 23:23:48 +00:00
|
|
|
/*
|
|
|
|
* Flush out the iclog to disk ensuring that device caches are flushed and
|
|
|
|
* the iclog hits stable storage before any completion waiters are woken.
|
|
|
|
*/
|
|
|
|
static inline int
|
|
|
|
xlog_force_iclog(
|
|
|
|
struct xlog_in_core *iclog)
|
|
|
|
{
|
|
|
|
atomic_inc(&iclog->ic_refcnt);
|
2021-07-27 23:23:49 +00:00
|
|
|
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
|
2021-07-27 23:23:48 +00:00
|
|
|
if (iclog->ic_state == XLOG_STATE_ACTIVE)
|
|
|
|
xlog_state_switch_iclogs(iclog->ic_log, iclog, 0);
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
return xlog_state_release_iclog(iclog->ic_log, iclog, NULL);
|
2021-07-27 23:23:48 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 15:49:18 +00:00
|
|
|
/*
|
2021-06-18 15:21:48 +00:00
|
|
|
* Wait for the iclog and all prior iclogs to be written disk as required by the
|
|
|
|
* log force state machine. Waiting on ic_force_wait ensures iclog completions
|
|
|
|
* have been ordered and callbacks run before we are woken here, hence
|
|
|
|
* guaranteeing that all the iclogs up to this one are on stable storage.
|
2020-03-20 15:49:18 +00:00
|
|
|
*/
|
2021-06-18 15:21:48 +00:00
|
|
|
int
|
2020-03-20 15:49:18 +00:00
|
|
|
xlog_wait_on_iclog(
|
|
|
|
struct xlog_in_core *iclog)
|
|
|
|
__releases(iclog->ic_log->l_icloglock)
|
|
|
|
{
|
|
|
|
struct xlog *log = iclog->ic_log;
|
|
|
|
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_wait_on(iclog, _RET_IP_);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (!xlog_is_shutdown(log) &&
|
2020-03-20 15:49:18 +00:00
|
|
|
iclog->ic_state != XLOG_STATE_ACTIVE &&
|
|
|
|
iclog->ic_state != XLOG_STATE_DIRTY) {
|
|
|
|
XFS_STATS_INC(log->l_mp, xs_log_force_sleep);
|
|
|
|
xlog_wait(&iclog->ic_force_wait, &log->l_icloglock);
|
|
|
|
} else {
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
}
|
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2020-03-20 15:49:18 +00:00
|
|
|
return -EIO;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2020-03-26 01:18:24 +00:00
|
|
|
* Write out an unmount record using the ticket provided. We have to account for
|
|
|
|
* the data space used in the unmount ticket as this write is not done from a
|
|
|
|
* transaction context that has already done the accounting for us.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2020-03-26 01:18:24 +00:00
|
|
|
static int
|
|
|
|
xlog_write_unmount_record(
|
|
|
|
struct xlog *log,
|
2021-06-18 15:21:50 +00:00
|
|
|
struct xlog_ticket *ticket)
|
2018-07-20 16:28:39 +00:00
|
|
|
{
|
2022-04-21 00:34:04 +00:00
|
|
|
struct {
|
|
|
|
struct xlog_op_header ophdr;
|
|
|
|
struct xfs_unmount_log_format ulf;
|
|
|
|
} unmount_rec = {
|
|
|
|
.ophdr = {
|
|
|
|
.oh_clientid = XFS_LOG,
|
|
|
|
.oh_tid = cpu_to_be32(ticket->t_tid),
|
|
|
|
.oh_flags = XLOG_UNMOUNT_TRANS,
|
|
|
|
},
|
|
|
|
.ulf = {
|
|
|
|
.magic = XLOG_UNMOUNT_TYPE,
|
|
|
|
},
|
2018-07-20 16:28:39 +00:00
|
|
|
};
|
|
|
|
struct xfs_log_iovec reg = {
|
2022-04-21 00:34:04 +00:00
|
|
|
.i_addr = &unmount_rec,
|
|
|
|
.i_len = sizeof(unmount_rec),
|
2018-07-20 16:28:39 +00:00
|
|
|
.i_type = XLOG_REG_TYPE_UNMOUNT,
|
|
|
|
};
|
|
|
|
struct xfs_log_vec vec = {
|
|
|
|
.lv_niovecs = 1,
|
|
|
|
.lv_iovecp = ®,
|
|
|
|
};
|
2022-07-07 08:55:59 +00:00
|
|
|
LIST_HEAD(lv_chain);
|
|
|
|
list_add(&vec.lv_list, &lv_chain);
|
2020-03-26 01:18:24 +00:00
|
|
|
|
2022-04-21 00:34:04 +00:00
|
|
|
BUILD_BUG_ON((sizeof(struct xlog_op_header) +
|
|
|
|
sizeof(struct xfs_unmount_log_format)) !=
|
|
|
|
sizeof(unmount_rec));
|
|
|
|
|
2020-03-26 01:18:24 +00:00
|
|
|
/* account for space used by record data */
|
2022-04-21 00:34:04 +00:00
|
|
|
ticket->t_curr_res -= sizeof(unmount_rec);
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
|
2022-07-07 08:55:59 +00:00
|
|
|
return xlog_write(log, NULL, &lv_chain, ticket, reg.i_len);
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Mark the filesystem clean by writing an unmount record to the head of the
|
|
|
|
* log.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xlog_unmount_write(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = log->l_mp;
|
2018-07-20 16:28:39 +00:00
|
|
|
struct xlog_in_core *iclog;
|
|
|
|
struct xlog_ticket *tic = NULL;
|
|
|
|
int error;
|
|
|
|
|
2022-04-21 00:34:33 +00:00
|
|
|
error = xfs_log_reserve(mp, 600, 1, &tic, 0);
|
2018-07-20 16:28:39 +00:00
|
|
|
if (error)
|
|
|
|
goto out_err;
|
|
|
|
|
2021-06-18 15:21:50 +00:00
|
|
|
error = xlog_write_unmount_record(log, tic);
|
2018-07-20 16:28:39 +00:00
|
|
|
/*
|
|
|
|
* At this point, we're umounting anyway, so there's no point in
|
2021-08-11 00:59:01 +00:00
|
|
|
* transitioning log state to shutdown. Just continue...
|
2018-07-20 16:28:39 +00:00
|
|
|
*/
|
|
|
|
out_err:
|
|
|
|
if (error)
|
|
|
|
xfs_alert(mp, "%s: unmount record failed", __func__);
|
|
|
|
|
|
|
|
spin_lock(&log->l_icloglock);
|
|
|
|
iclog = log->l_iclog;
|
2021-07-27 23:23:48 +00:00
|
|
|
error = xlog_force_iclog(iclog);
|
2020-03-20 15:49:18 +00:00
|
|
|
xlog_wait_on_iclog(iclog);
|
2018-07-20 16:28:39 +00:00
|
|
|
|
|
|
|
if (tic) {
|
|
|
|
trace_xfs_log_umount_write(log, tic);
|
2020-03-26 01:18:23 +00:00
|
|
|
xfs_log_ticket_ungrant(log, tic);
|
2018-07-20 16:28:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-12 23:52:51 +00:00
|
|
|
static void
|
|
|
|
xfs_log_unmount_verify_iclog(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
struct xlog_in_core *iclog = log->l_iclog;
|
|
|
|
|
|
|
|
do {
|
|
|
|
ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
|
|
|
|
ASSERT(iclog->ic_offset == 0);
|
|
|
|
} while ((iclog = iclog->ic_next) != log->l_iclog);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Unmount record used to have a string "Unmount filesystem--" in the
|
|
|
|
* data section where the "Un" was really a magic number (XLOG_UNMOUNT_TYPE).
|
|
|
|
* We just write the magic number now since that particular field isn't
|
2013-08-12 03:15:00 +00:00
|
|
|
* currently architecture converted and "Unmount" is a bit foo.
|
2005-04-16 22:20:36 +00:00
|
|
|
* As far as I know, there weren't any dependencies on the old behaviour.
|
|
|
|
*/
|
2020-03-12 23:52:50 +00:00
|
|
|
static void
|
2020-03-12 23:52:51 +00:00
|
|
|
xfs_log_unmount_write(
|
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2020-03-12 23:52:51 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-01-23 00:48:20 +00:00
|
|
|
if (!xfs_log_writable(mp))
|
2020-03-12 23:52:50 +00:00
|
|
|
return;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-12 23:52:50 +00:00
|
|
|
xfs_log_force(mp, XFS_LOG_SYNC);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2020-03-12 23:52:51 +00:00
|
|
|
return;
|
2020-03-26 17:26:44 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we think the summary counters are bad, avoid writing the unmount
|
|
|
|
* record to force log recovery at next mount, after which the summary
|
|
|
|
* counters will be recalculated. Refer to xlog_check_unmount_rec for
|
|
|
|
* more details.
|
|
|
|
*/
|
|
|
|
if (XFS_TEST_ERROR(xfs_fs_has_sickness(mp, XFS_SICK_FS_COUNTERS), mp,
|
|
|
|
XFS_ERRTAG_FORCE_SUMMARY_RECALC)) {
|
|
|
|
xfs_alert(mp, "%s: will fix summary counters at next mount",
|
|
|
|
__func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-03-12 23:52:51 +00:00
|
|
|
xfs_log_unmount_verify_iclog(log);
|
2020-03-26 01:18:24 +00:00
|
|
|
xlog_unmount_write(log);
|
2020-03-12 23:52:50 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2012-10-08 10:56:08 +00:00
|
|
|
* Empty the log for unmount/freeze.
|
2012-10-08 10:56:03 +00:00
|
|
|
*
|
|
|
|
* To do this, we first need to shut down the background log work so it is not
|
|
|
|
* trying to cover the log as we clean up. We then need to unpin all objects in
|
|
|
|
* the log so we can then flush them out. Once they have completed their IO and
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
* run the callbacks removing themselves from the AIL, we can cover the log.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
int
|
2012-10-08 10:56:08 +00:00
|
|
|
xfs_log_quiesce(
|
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-08-08 15:27:12 +00:00
|
|
|
/*
|
|
|
|
* Clear log incompat features since we're quiescing the log. Report
|
|
|
|
* failures, though it's not fatal to have a higher log feature
|
|
|
|
* protection level than the log contents actually require.
|
|
|
|
*/
|
|
|
|
if (xfs_clear_incompat_log_features(mp)) {
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = xfs_sync_sb(mp, false);
|
|
|
|
if (error)
|
|
|
|
xfs_warn(mp,
|
|
|
|
"Failed to clear log incompat features on quiesce");
|
|
|
|
}
|
|
|
|
|
2012-10-08 10:56:02 +00:00
|
|
|
cancel_delayed_work_sync(&mp->m_log->l_work);
|
2012-10-08 10:56:03 +00:00
|
|
|
xfs_log_force(mp, XFS_LOG_SYNC);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The superblock buffer is uncached and while xfs_ail_push_all_sync()
|
2021-01-23 00:48:20 +00:00
|
|
|
* will push it, xfs_buftarg_wait() will not wait for it. Further,
|
2012-10-08 10:56:03 +00:00
|
|
|
* xfs_buf_iowait() cannot be used because it was pushed with the
|
|
|
|
* XBF_ASYNC flag set, so we need to use a lock/unlock pair to wait for
|
|
|
|
* the IO to complete.
|
|
|
|
*/
|
|
|
|
xfs_ail_push_all_sync(mp->m_ail);
|
2021-01-23 00:48:20 +00:00
|
|
|
xfs_buftarg_wait(mp->m_ddev_targp);
|
2012-10-08 10:56:03 +00:00
|
|
|
xfs_buf_lock(mp->m_sb_bp);
|
|
|
|
xfs_buf_unlock(mp->m_sb_bp);
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
|
|
|
|
return xfs_log_cover(mp);
|
2021-01-23 00:48:21 +00:00
|
|
|
}
|
2012-10-08 10:56:03 +00:00
|
|
|
|
2021-01-23 00:48:21 +00:00
|
|
|
void
|
|
|
|
xfs_log_clean(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
xfs_log_quiesce(mp);
|
2012-10-08 10:56:03 +00:00
|
|
|
xfs_log_unmount_write(mp);
|
2012-10-08 10:56:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Shut down and release the AIL and Log.
|
|
|
|
*
|
|
|
|
* During unmount, we need to ensure we flush all the dirty metadata objects
|
|
|
|
* from the AIL so that the log is empty before we write the unmount record to
|
|
|
|
* the log. Once this is done, we can tear down the AIL and the log.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xfs_log_unmount(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
2021-01-23 00:48:21 +00:00
|
|
|
xfs_log_clean(mp);
|
2012-10-08 10:56:03 +00:00
|
|
|
|
2021-01-23 00:48:20 +00:00
|
|
|
xfs_buftarg_drain(mp->m_ddev_targp);
|
|
|
|
|
[XFS] Move AIL pushing into it's own thread
When many hundreds to thousands of threads all try to do simultaneous
transactions and the log is in a tail-pushing situation (i.e. full), we
can get multiple threads walking the AIL list and contending on the AIL
lock.
The AIL push is, in effect, a simple I/O dispatch algorithm complicated by
the ordering constraints placed on it by the transaction subsystem. It
really does not need multiple threads to push on it - even when only a
single CPU is pushing the AIL, it can push the I/O out far faster that
pretty much any disk subsystem can handle.
So, to avoid contention problems stemming from multiple list walkers, move
the list walk off into another thread and simply provide a "target" to
push to. When a thread requires a push, it sets the target and wakes the
push thread, then goes to sleep waiting for the required amount of space
to become available in the log.
This mechanism should also be a lot fairer under heavy load as the waiters
will queue in arrival order, rather than queuing in "who completed a push
first" order.
Also, by moving the pushing to a separate thread we can do more
effectively overload detection and prevention as we can keep context from
loop iteration to loop iteration. That is, we can push only part of the
list each loop and not have to loop back to the start of the list every
time we run. This should also help by reducing the number of items we try
to lock and/or push items that we cannot move.
Note that this patch is not intended to solve the inefficiencies in the
AIL structure and the associated issues with extremely large list
contents. That needs to be addresses separately; parallel access would
cause problems to any new structure as well, so I'm only aiming to isolate
the structure from unbounded parallelism here.
SGI-PV: 972759
SGI-Modid: xfs-linux-melb:xfs-kern:30371a
Signed-off-by: David Chinner <dgc@sgi.com>
Signed-off-by: Lachlan McIlroy <lachlan@sgi.com>
2008-02-05 01:13:32 +00:00
|
|
|
xfs_trans_ail_destroy(mp);
|
2014-07-14 22:07:29 +00:00
|
|
|
|
|
|
|
xfs_sysfs_del(&mp->m_log->l_kobj);
|
|
|
|
|
2006-03-28 22:55:14 +00:00
|
|
|
xlog_dealloc_log(mp->m_log);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2010-03-22 23:10:00 +00:00
|
|
|
void
|
|
|
|
xfs_log_item_init(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_log_item *item,
|
|
|
|
int type,
|
2011-10-28 09:54:24 +00:00
|
|
|
const struct xfs_item_ops *ops)
|
2010-03-22 23:10:00 +00:00
|
|
|
{
|
2022-03-17 16:09:12 +00:00
|
|
|
item->li_log = mp->m_log;
|
2010-03-22 23:10:00 +00:00
|
|
|
item->li_ailp = mp->m_ail;
|
|
|
|
item->li_type = type;
|
|
|
|
item->li_ops = ops;
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
item->li_lv = NULL;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&item->li_ail);
|
|
|
|
INIT_LIST_HEAD(&item->li_cil);
|
2018-01-24 21:38:49 +00:00
|
|
|
INIT_LIST_HEAD(&item->li_bio_list);
|
2018-05-09 14:49:37 +00:00
|
|
|
INIT_LIST_HEAD(&item->li_trans);
|
2010-03-22 23:10:00 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:20 +00:00
|
|
|
/*
|
|
|
|
* Wake up processes waiting for log space after we have moved the log tail.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
void
|
2012-02-20 02:31:20 +00:00
|
|
|
xfs_log_space_wake(
|
2012-02-20 02:31:23 +00:00
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2012-02-20 02:31:23 +00:00
|
|
|
int free_bytes;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2005-04-16 22:20:36 +00:00
|
|
|
return;
|
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
if (!list_empty_careful(&log->l_write_head.waiters)) {
|
2021-08-11 00:59:02 +00:00
|
|
|
ASSERT(!xlog_in_recovery(log));
|
2012-02-20 02:31:20 +00:00
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
spin_lock(&log->l_write_head.lock);
|
|
|
|
free_bytes = xlog_space_left(log, &log->l_write_head.grant);
|
2012-02-20 02:31:29 +00:00
|
|
|
xlog_grant_head_wake(log, &log->l_write_head, &free_bytes);
|
2012-02-20 02:31:25 +00:00
|
|
|
spin_unlock(&log->l_write_head.lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-12-21 01:02:25 +00:00
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
if (!list_empty_careful(&log->l_reserve_head.waiters)) {
|
2021-08-11 00:59:02 +00:00
|
|
|
ASSERT(!xlog_in_recovery(log));
|
2012-02-20 02:31:20 +00:00
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
spin_lock(&log->l_reserve_head.lock);
|
|
|
|
free_bytes = xlog_space_left(log, &log->l_reserve_head.grant);
|
2012-02-20 02:31:29 +00:00
|
|
|
xlog_grant_head_wake(log, &log->l_reserve_head, &free_bytes);
|
2012-02-20 02:31:25 +00:00
|
|
|
spin_unlock(&log->l_reserve_head.lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-12-21 01:29:01 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
* Determine if we have a transaction that has gone to disk that needs to be
|
|
|
|
* covered. To begin the transition to the idle state firstly the log needs to
|
|
|
|
* be idle. That means the CIL, the AIL and the iclogs needs to be empty before
|
|
|
|
* we start attempting to cover the log.
|
xfs: ensure that sync updates the log tail correctly
Updates to the VFS layer removed an extra ->sync_fs call into the
filesystem during the sync process (from the quota code).
Unfortunately the sync code was unknowingly relying on this call to
make sure metadata buffers were flushed via a xfs_buftarg_flush()
call to move the tail of the log forward in memory before the final
transactions of the sync process were issued.
As a result, the old code would write a very recent log tail value
to the log by the end of the sync process, and so a subsequent crash
would leave nothing for log recovery to do. Hence in qa test 182,
log recovery only replayed a small handle for inode fsync
transactions in this case.
However, with the removal of the extra ->sync_fs call, the log tail
was now not moved forward with the inode fsync transactions near the
end of the sync procese the first (and only) buftarg flush occurred
after these transactions went to disk. The result is that log
recovery now sees a large number of transactions for metadata that
is already on disk.
This usually isn't a problem, but when the transactions include
inode chunk allocation, the inode create transactions and all
subsequent changes are replayed as we cannt rely on what is on disk
is valid. As a result, if the inode was written and contains
unlogged changes, the unlogged changes are lost, thereby violating
sync semantics.
The fix is to always issue a transaction after the buftarg flush
occurs is the log iѕ not idle or covered. This results in a dummy
transaction being written that contains the up-to-date log tail
value, which will be very recent. Indeed, it will be at least as
recent as the old code would have left on disk, so log recovery
will behave exactly as it used to in this situation.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-04-13 05:06:44 +00:00
|
|
|
*
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
* Only if we are then in a state where covering is needed, the caller is
|
|
|
|
* informed that dummy transactions are required to move the log into the idle
|
|
|
|
* state.
|
|
|
|
*
|
|
|
|
* If there are any items in the AIl or CIL, then we do not want to attempt to
|
|
|
|
* cover the log as we may be in a situation where there isn't log space
|
|
|
|
* available to run a dummy transaction and this can lead to deadlocks when the
|
|
|
|
* tail of the log is pinned by an item that is modified in the CIL. Hence
|
|
|
|
* there's no point in running a dummy transaction at this point because we
|
|
|
|
* can't start trying to idle the log until both the CIL and AIL are empty.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2021-01-23 00:48:21 +00:00
|
|
|
static bool
|
|
|
|
xfs_log_need_covered(
|
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-01-23 00:48:21 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
|
|
|
bool needed = false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
if (!xlog_cil_empty(log))
|
2021-02-11 01:27:31 +00:00
|
|
|
return false;
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
xfs: ensure that sync updates the log tail correctly
Updates to the VFS layer removed an extra ->sync_fs call into the
filesystem during the sync process (from the quota code).
Unfortunately the sync code was unknowingly relying on this call to
make sure metadata buffers were flushed via a xfs_buftarg_flush()
call to move the tail of the log forward in memory before the final
transactions of the sync process were issued.
As a result, the old code would write a very recent log tail value
to the log by the end of the sync process, and so a subsequent crash
would leave nothing for log recovery to do. Hence in qa test 182,
log recovery only replayed a small handle for inode fsync
transactions in this case.
However, with the removal of the extra ->sync_fs call, the log tail
was now not moved forward with the inode fsync transactions near the
end of the sync procese the first (and only) buftarg flush occurred
after these transactions went to disk. The result is that log
recovery now sees a large number of transactions for metadata that
is already on disk.
This usually isn't a problem, but when the transactions include
inode chunk allocation, the inode create transactions and all
subsequent changes are replayed as we cannt rely on what is on disk
is valid. As a result, if the inode was written and contains
unlogged changes, the unlogged changes are lost, thereby violating
sync semantics.
The fix is to always issue a transaction after the buftarg flush
occurs is the log iѕ not idle or covered. This results in a dummy
transaction being written that contains the up-to-date log tail
value, which will be very recent. Indeed, it will be at least as
recent as the old code would have left on disk, so log recovery
will behave exactly as it used to in this situation.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-04-13 05:06:44 +00:00
|
|
|
switch (log->l_covered_state) {
|
|
|
|
case XLOG_STATE_COVER_DONE:
|
|
|
|
case XLOG_STATE_COVER_DONE2:
|
|
|
|
case XLOG_STATE_COVER_IDLE:
|
|
|
|
break;
|
|
|
|
case XLOG_STATE_COVER_NEED:
|
|
|
|
case XLOG_STATE_COVER_NEED2:
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
if (xfs_ail_min_lsn(log->l_ailp))
|
|
|
|
break;
|
|
|
|
if (!xlog_iclogs_empty(log))
|
|
|
|
break;
|
|
|
|
|
2021-01-23 00:48:21 +00:00
|
|
|
needed = true;
|
xfs: prevent deadlock trying to cover an active log
Recent analysis of a deadlocked XFS filesystem from a kernel
crash dump indicated that the filesystem was stuck waiting for log
space. The short story of the hang on the RHEL6 kernel is this:
- the tail of the log is pinned by an inode
- the inode has been pushed by the xfsaild
- the inode has been flushed to it's backing buffer and is
currently flush locked and hence waiting for backing
buffer IO to complete and remove it from the AIL
- the backing buffer is marked for write - it is on the
delayed write queue
- the inode buffer has been modified directly and logged
recently due to unlinked inode list modification
- the backing buffer is pinned in memory as it is in the
active CIL context.
- the xfsbufd won't start buffer writeback because it is
pinned
- xfssyncd won't force the log because it sees the log as
needing to be covered and hence wants to issue a dummy
transaction to move the log covering state machine along.
Hence there is no trigger to force the CIL to the log and hence
unpin the inode buffer and therefore complete the inode IO, remove
it from the AIL and hence move the tail of the log along, allowing
transactions to start again.
Mainline kernels also have the same deadlock, though the signature
is slightly different - the inode buffer never reaches the delayed
write lists because xfs_buf_item_push() sees that it is pinned and
hence never adds it to the delayed write list that the xfsaild
flushes.
There are two possible solutions here. The first is to simply force
the log before trying to cover the log and so ensure that the CIL is
emptied before we try to reserve space for the dummy transaction in
the xfs_log_worker(). While this might work most of the time, it is
still racy and is no guarantee that we don't get stuck in
xfs_trans_reserve waiting for log space to come free. Hence it's not
the best way to solve the problem.
The second solution is to modify xfs_log_need_covered() to be aware
of the CIL. We only should be attempting to cover the log if there
is no current activity in the log - covering the log is the process
of ensuring that the head and tail in the log on disk are identical
(i.e. the log is clean and at idle). Hence, by definition, if there
are items in the CIL then the log is not at idle and so we don't
need to attempt to cover it.
When we don't need to cover the log because it is active or idle, we
issue a log force from xfs_log_worker() - if the log is idle, then
this does nothing. However, if the log is active due to there being
items in the CIL, it will force the items in the CIL to the log and
unpin them.
In the case of the above deadlock scenario, instead of
xfs_log_worker() getting stuck in xfs_trans_reserve() attempting to
cover the log, it will instead force the log, thereby unpinning the
inode buffer, allowing IO to be issued and complete and hence
removing the inode that was pinning the tail of the log from the
AIL. At that point, everything will start moving along again. i.e.
the xfs_log_worker turns back into a watchdog that can alleviate
deadlocks based around pinned items that prevent the tail of the log
from being moved...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Eric Sandeen <sandeen@redhat.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2013-10-14 22:17:49 +00:00
|
|
|
if (log->l_covered_state == XLOG_STATE_COVER_NEED)
|
|
|
|
log->l_covered_state = XLOG_STATE_COVER_DONE;
|
|
|
|
else
|
|
|
|
log->l_covered_state = XLOG_STATE_COVER_DONE2;
|
|
|
|
break;
|
xfs: ensure that sync updates the log tail correctly
Updates to the VFS layer removed an extra ->sync_fs call into the
filesystem during the sync process (from the quota code).
Unfortunately the sync code was unknowingly relying on this call to
make sure metadata buffers were flushed via a xfs_buftarg_flush()
call to move the tail of the log forward in memory before the final
transactions of the sync process were issued.
As a result, the old code would write a very recent log tail value
to the log by the end of the sync process, and so a subsequent crash
would leave nothing for log recovery to do. Hence in qa test 182,
log recovery only replayed a small handle for inode fsync
transactions in this case.
However, with the removal of the extra ->sync_fs call, the log tail
was now not moved forward with the inode fsync transactions near the
end of the sync procese the first (and only) buftarg flush occurred
after these transactions went to disk. The result is that log
recovery now sees a large number of transactions for metadata that
is already on disk.
This usually isn't a problem, but when the transactions include
inode chunk allocation, the inode create transactions and all
subsequent changes are replayed as we cannt rely on what is on disk
is valid. As a result, if the inode was written and contains
unlogged changes, the unlogged changes are lost, thereby violating
sync semantics.
The fix is to always issue a transaction after the buftarg flush
occurs is the log iѕ not idle or covered. This results in a dummy
transaction being written that contains the up-to-date log tail
value, which will be very recent. Indeed, it will be at least as
recent as the old code would have left on disk, so log recovery
will behave exactly as it used to in this situation.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-04-13 05:06:44 +00:00
|
|
|
default:
|
2021-01-23 00:48:21 +00:00
|
|
|
needed = true;
|
xfs: ensure that sync updates the log tail correctly
Updates to the VFS layer removed an extra ->sync_fs call into the
filesystem during the sync process (from the quota code).
Unfortunately the sync code was unknowingly relying on this call to
make sure metadata buffers were flushed via a xfs_buftarg_flush()
call to move the tail of the log forward in memory before the final
transactions of the sync process were issued.
As a result, the old code would write a very recent log tail value
to the log by the end of the sync process, and so a subsequent crash
would leave nothing for log recovery to do. Hence in qa test 182,
log recovery only replayed a small handle for inode fsync
transactions in this case.
However, with the removal of the extra ->sync_fs call, the log tail
was now not moved forward with the inode fsync transactions near the
end of the sync procese the first (and only) buftarg flush occurred
after these transactions went to disk. The result is that log
recovery now sees a large number of transactions for metadata that
is already on disk.
This usually isn't a problem, but when the transactions include
inode chunk allocation, the inode create transactions and all
subsequent changes are replayed as we cannt rely on what is on disk
is valid. As a result, if the inode was written and contains
unlogged changes, the unlogged changes are lost, thereby violating
sync semantics.
The fix is to always issue a transaction after the buftarg flush
occurs is the log iѕ not idle or covered. This results in a dummy
transaction being written that contains the up-to-date log tail
value, which will be very recent. Indeed, it will be at least as
recent as the old code would have left on disk, so log recovery
will behave exactly as it used to in this situation.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-04-13 05:06:44 +00:00
|
|
|
break;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2006-01-15 01:37:08 +00:00
|
|
|
return needed;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
/*
|
|
|
|
* Explicitly cover the log. This is similar to background log covering but
|
|
|
|
* intended for usage in quiesce codepaths. The caller is responsible to ensure
|
|
|
|
* the log is idle and suitable for covering. The CIL, iclog buffers and AIL
|
|
|
|
* must all be empty.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xfs_log_cover(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
int error = 0;
|
2021-01-23 00:48:23 +00:00
|
|
|
bool need_covered;
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
|
2021-01-27 03:14:55 +00:00
|
|
|
ASSERT((xlog_cil_empty(mp->m_log) && xlog_iclogs_empty(mp->m_log) &&
|
|
|
|
!xfs_ail_min_lsn(mp->m_log->l_ailp)) ||
|
2021-08-11 00:59:01 +00:00
|
|
|
xlog_is_shutdown(mp->m_log));
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
|
|
|
|
if (!xfs_log_writable(mp))
|
|
|
|
return 0;
|
|
|
|
|
2021-01-23 00:48:23 +00:00
|
|
|
/*
|
|
|
|
* xfs_log_need_covered() is not idempotent because it progresses the
|
|
|
|
* state machine if the log requires covering. Therefore, we must call
|
|
|
|
* this function once and use the result until we've issued an sb sync.
|
|
|
|
* Do so first to make that abundantly clear.
|
|
|
|
*
|
|
|
|
* Fall into the covering sequence if the log needs covering or the
|
|
|
|
* mount has lazy superblock accounting to sync to disk. The sb sync
|
|
|
|
* used for covering accumulates the in-core counters, so covering
|
|
|
|
* handles this for us.
|
|
|
|
*/
|
|
|
|
need_covered = xfs_log_need_covered(mp);
|
2021-08-19 01:46:37 +00:00
|
|
|
if (!need_covered && !xfs_has_lazysbcount(mp))
|
2021-01-23 00:48:23 +00:00
|
|
|
return 0;
|
|
|
|
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
/*
|
|
|
|
* To cover the log, commit the superblock twice (at most) in
|
|
|
|
* independent checkpoints. The first serves as a reference for the
|
|
|
|
* tail pointer. The sync transaction and AIL push empties the AIL and
|
|
|
|
* updates the in-core tail to the LSN of the first checkpoint. The
|
|
|
|
* second commit updates the on-disk tail with the in-core LSN,
|
|
|
|
* covering the log. Push the AIL one more time to leave it empty, as
|
|
|
|
* we found it.
|
|
|
|
*/
|
2021-01-23 00:48:23 +00:00
|
|
|
do {
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
error = xfs_sync_sb(mp, true);
|
|
|
|
if (error)
|
|
|
|
break;
|
|
|
|
xfs_ail_push_all_sync(mp->m_ail);
|
2021-01-23 00:48:23 +00:00
|
|
|
} while (xfs_log_need_covered(mp));
|
xfs: cover the log during log quiesce
The log quiesce mechanism historically terminates by marking the log
clean with an unmount record. The primary objective is to indicate
that log recovery is no longer required after the quiesce has
flushed all in-core changes and written back filesystem metadata.
While this is perfectly fine, it is somewhat hacky as currently used
in certain contexts. For example, filesystem freeze quiesces (i.e.
cleans) the log and immediately redirties it with a dummy superblock
transaction to ensure that log recovery runs in the event of a
crash.
While this functions correctly, cleaning the log from freeze context
is clearly superfluous given the current redirtying behavior.
Instead, the desired behavior can be achieved by simply covering the
log. This effectively retires all on-disk log items from the active
range of the log by issuing two synchronous and sequential dummy
superblock update transactions that serve to update the on-disk log
head and tail. The subtle difference is that the log technically
remains dirty due to the lack of an unmount record, though recovery
is effectively a no-op due to the content of the checkpoints being
clean (i.e. the unmodified on-disk superblock).
Log covering currently runs in the background and only triggers once
the filesystem and log has idled. The purpose of the background
mechanism is to prevent log recovery from replaying the most
recently logged items long after those items may have been written
back. In the quiesce path, the log has been deliberately idled by
forcing the log and pushing the AIL until empty in a context where
no further mutable filesystem operations are allowed. Therefore, we
can cover the log as the final step in the log quiesce codepath to
reflect that all previously active items have been successfully
written back.
This facilitates selective log covering from certain contexts (i.e.
freeze) that only seek to quiesce, but not necessarily clean the
log. Note that as a side effect of this change, log covering now
occurs when cleaning the log as well. This is harmless, facilitates
subsequent cleanups, and is mostly temporary as various operations
switch to use explicit log covering.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:20 +00:00
|
|
|
/*
|
2005-04-16 22:20:36 +00:00
|
|
|
* We may be holding the log iclog lock upon entering this routine.
|
|
|
|
*/
|
|
|
|
xfs_lsn_t
|
2012-04-23 05:58:33 +00:00
|
|
|
xlog_assign_tail_lsn_locked(
|
2010-12-21 01:28:39 +00:00
|
|
|
struct xfs_mount *mp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2012-04-23 05:58:33 +00:00
|
|
|
struct xfs_log_item *lip;
|
|
|
|
xfs_lsn_t tail_lsn;
|
|
|
|
|
2018-03-07 22:59:39 +00:00
|
|
|
assert_spin_locked(&mp->m_ail->ail_lock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-02-20 02:31:20 +00:00
|
|
|
/*
|
|
|
|
* To make sure we always have a valid LSN for the log tail we keep
|
|
|
|
* track of the last LSN which was committed in log->l_last_sync_lsn,
|
2012-04-23 05:58:33 +00:00
|
|
|
* and use that when the AIL was empty.
|
2012-02-20 02:31:20 +00:00
|
|
|
*/
|
2012-04-23 05:58:33 +00:00
|
|
|
lip = xfs_ail_min(mp->m_ail);
|
|
|
|
if (lip)
|
|
|
|
tail_lsn = lip->li_lsn;
|
|
|
|
else
|
2010-12-03 11:11:29 +00:00
|
|
|
tail_lsn = atomic64_read(&log->l_last_sync_lsn);
|
2013-11-01 04:27:18 +00:00
|
|
|
trace_xfs_log_assign_tail_lsn(log, tail_lsn);
|
2010-12-21 01:28:39 +00:00
|
|
|
atomic64_set(&log->l_tail_lsn, tail_lsn);
|
2005-04-16 22:20:36 +00:00
|
|
|
return tail_lsn;
|
2010-12-21 01:28:39 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-04-23 05:58:33 +00:00
|
|
|
xfs_lsn_t
|
|
|
|
xlog_assign_tail_lsn(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
|
|
|
xfs_lsn_t tail_lsn;
|
|
|
|
|
2018-03-07 22:59:39 +00:00
|
|
|
spin_lock(&mp->m_ail->ail_lock);
|
2012-04-23 05:58:33 +00:00
|
|
|
tail_lsn = xlog_assign_tail_lsn_locked(mp);
|
2018-03-07 22:59:39 +00:00
|
|
|
spin_unlock(&mp->m_ail->ail_lock);
|
2012-04-23 05:58:33 +00:00
|
|
|
|
|
|
|
return tail_lsn;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Return the space in the log between the tail and the head. The head
|
|
|
|
* is passed in the cycle/bytes formal parms. In the special case where
|
|
|
|
* the reserve head has wrapped passed the tail, this calculation is no
|
|
|
|
* longer valid. In this case, just return 0 which means there is no space
|
|
|
|
* in the log. This works for all places where this function is called
|
|
|
|
* with the reserve head. Of course, if the write head were to ever
|
|
|
|
* wrap the tail, we should blow up. Rather than catch this case here,
|
|
|
|
* we depend on other ASSERTions in other parts of the code. XXXmiken
|
|
|
|
*
|
xfs: log head and tail aren't reliable during shutdown
I'm seeing assert failures from xlog_space_left() after a shutdown
has begun that look like:
XFS (dm-0): log I/O error -5
XFS (dm-0): xfs_do_force_shutdown(0x2) called from line 1338 of file fs/xfs/xfs_log.c. Return address = xlog_ioend_work+0x64/0xc0
XFS (dm-0): Log I/O Error Detected.
XFS (dm-0): Shutting down filesystem. Please unmount the filesystem and rectify the problem(s)
XFS (dm-0): xlog_space_left: head behind tail
XFS (dm-0): tail_cycle = 6, tail_bytes = 2706944
XFS (dm-0): GH cycle = 6, GH bytes = 1633867
XFS: Assertion failed: 0, file: fs/xfs/xfs_log.c, line: 1310
------------[ cut here ]------------
Call Trace:
xlog_space_left+0xc3/0x110
xlog_grant_push_threshold+0x3f/0xf0
xlog_grant_push_ail+0x12/0x40
xfs_log_reserve+0xd2/0x270
? __might_sleep+0x4b/0x80
xfs_trans_reserve+0x18b/0x260
.....
There are two things here. Firstly, after a shutdown, the log head
and tail can be out of whack as things abort and release (or don't
release) resources, so checking them for sanity doesn't make much
sense. Secondly, xfs_log_reserve() can race with shutdown and so it
can still fail like this even though it has already checked for a
log shutdown before calling xlog_grant_push_ail().
So, before ASSERT failing in xlog_space_left(), make sure we haven't
already shut down....
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
* If reservation head is behind the tail, we have a problem. Warn about it,
|
|
|
|
* but then treat it as if the log is empty.
|
|
|
|
*
|
|
|
|
* If the log is shut down, the head and tail may be invalid or out of whack, so
|
|
|
|
* shortcut invalidity asserts in this case so that we don't trigger them
|
|
|
|
* falsely.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-11-23 05:28:09 +00:00
|
|
|
STATIC int
|
2010-12-21 01:08:20 +00:00
|
|
|
xlog_space_left(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2010-12-03 13:02:40 +00:00
|
|
|
atomic64_t *head)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-12-21 01:08:20 +00:00
|
|
|
int tail_bytes;
|
|
|
|
int tail_cycle;
|
|
|
|
int head_cycle;
|
|
|
|
int head_bytes;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-12-21 01:08:20 +00:00
|
|
|
xlog_crack_grant_head(head, &head_cycle, &head_bytes);
|
2010-12-21 01:28:39 +00:00
|
|
|
xlog_crack_atomic_lsn(&log->l_tail_lsn, &tail_cycle, &tail_bytes);
|
|
|
|
tail_bytes = BBTOB(tail_bytes);
|
2010-12-21 01:08:20 +00:00
|
|
|
if (tail_cycle == head_cycle && head_bytes >= tail_bytes)
|
xfs: log head and tail aren't reliable during shutdown
I'm seeing assert failures from xlog_space_left() after a shutdown
has begun that look like:
XFS (dm-0): log I/O error -5
XFS (dm-0): xfs_do_force_shutdown(0x2) called from line 1338 of file fs/xfs/xfs_log.c. Return address = xlog_ioend_work+0x64/0xc0
XFS (dm-0): Log I/O Error Detected.
XFS (dm-0): Shutting down filesystem. Please unmount the filesystem and rectify the problem(s)
XFS (dm-0): xlog_space_left: head behind tail
XFS (dm-0): tail_cycle = 6, tail_bytes = 2706944
XFS (dm-0): GH cycle = 6, GH bytes = 1633867
XFS: Assertion failed: 0, file: fs/xfs/xfs_log.c, line: 1310
------------[ cut here ]------------
Call Trace:
xlog_space_left+0xc3/0x110
xlog_grant_push_threshold+0x3f/0xf0
xlog_grant_push_ail+0x12/0x40
xfs_log_reserve+0xd2/0x270
? __might_sleep+0x4b/0x80
xfs_trans_reserve+0x18b/0x260
.....
There are two things here. Firstly, after a shutdown, the log head
and tail can be out of whack as things abort and release (or don't
release) resources, so checking them for sanity doesn't make much
sense. Secondly, xfs_log_reserve() can race with shutdown and so it
can still fail like this even though it has already checked for a
log shutdown before calling xlog_grant_push_ail().
So, before ASSERT failing in xlog_space_left(), make sure we haven't
already shut down....
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
return log->l_logsize - (head_bytes - tail_bytes);
|
|
|
|
if (tail_cycle + 1 < head_cycle)
|
2005-04-16 22:20:36 +00:00
|
|
|
return 0;
|
xfs: log head and tail aren't reliable during shutdown
I'm seeing assert failures from xlog_space_left() after a shutdown
has begun that look like:
XFS (dm-0): log I/O error -5
XFS (dm-0): xfs_do_force_shutdown(0x2) called from line 1338 of file fs/xfs/xfs_log.c. Return address = xlog_ioend_work+0x64/0xc0
XFS (dm-0): Log I/O Error Detected.
XFS (dm-0): Shutting down filesystem. Please unmount the filesystem and rectify the problem(s)
XFS (dm-0): xlog_space_left: head behind tail
XFS (dm-0): tail_cycle = 6, tail_bytes = 2706944
XFS (dm-0): GH cycle = 6, GH bytes = 1633867
XFS: Assertion failed: 0, file: fs/xfs/xfs_log.c, line: 1310
------------[ cut here ]------------
Call Trace:
xlog_space_left+0xc3/0x110
xlog_grant_push_threshold+0x3f/0xf0
xlog_grant_push_ail+0x12/0x40
xfs_log_reserve+0xd2/0x270
? __might_sleep+0x4b/0x80
xfs_trans_reserve+0x18b/0x260
.....
There are two things here. Firstly, after a shutdown, the log head
and tail can be out of whack as things abort and release (or don't
release) resources, so checking them for sanity doesn't make much
sense. Secondly, xfs_log_reserve() can race with shutdown and so it
can still fail like this even though it has already checked for a
log shutdown before calling xlog_grant_push_ail().
So, before ASSERT failing in xlog_space_left(), make sure we haven't
already shut down....
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
|
|
|
|
/* Ignore potential inconsistency when shutdown. */
|
|
|
|
if (xlog_is_shutdown(log))
|
|
|
|
return log->l_logsize;
|
|
|
|
|
|
|
|
if (tail_cycle < head_cycle) {
|
2010-12-21 01:08:20 +00:00
|
|
|
ASSERT(tail_cycle == (head_cycle - 1));
|
xfs: log head and tail aren't reliable during shutdown
I'm seeing assert failures from xlog_space_left() after a shutdown
has begun that look like:
XFS (dm-0): log I/O error -5
XFS (dm-0): xfs_do_force_shutdown(0x2) called from line 1338 of file fs/xfs/xfs_log.c. Return address = xlog_ioend_work+0x64/0xc0
XFS (dm-0): Log I/O Error Detected.
XFS (dm-0): Shutting down filesystem. Please unmount the filesystem and rectify the problem(s)
XFS (dm-0): xlog_space_left: head behind tail
XFS (dm-0): tail_cycle = 6, tail_bytes = 2706944
XFS (dm-0): GH cycle = 6, GH bytes = 1633867
XFS: Assertion failed: 0, file: fs/xfs/xfs_log.c, line: 1310
------------[ cut here ]------------
Call Trace:
xlog_space_left+0xc3/0x110
xlog_grant_push_threshold+0x3f/0xf0
xlog_grant_push_ail+0x12/0x40
xfs_log_reserve+0xd2/0x270
? __might_sleep+0x4b/0x80
xfs_trans_reserve+0x18b/0x260
.....
There are two things here. Firstly, after a shutdown, the log head
and tail can be out of whack as things abort and release (or don't
release) resources, so checking them for sanity doesn't make much
sense. Secondly, xfs_log_reserve() can race with shutdown and so it
can still fail like this even though it has already checked for a
log shutdown before calling xlog_grant_push_ail().
So, before ASSERT failing in xlog_space_left(), make sure we haven't
already shut down....
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
return tail_bytes - head_bytes;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
xfs: log head and tail aren't reliable during shutdown
I'm seeing assert failures from xlog_space_left() after a shutdown
has begun that look like:
XFS (dm-0): log I/O error -5
XFS (dm-0): xfs_do_force_shutdown(0x2) called from line 1338 of file fs/xfs/xfs_log.c. Return address = xlog_ioend_work+0x64/0xc0
XFS (dm-0): Log I/O Error Detected.
XFS (dm-0): Shutting down filesystem. Please unmount the filesystem and rectify the problem(s)
XFS (dm-0): xlog_space_left: head behind tail
XFS (dm-0): tail_cycle = 6, tail_bytes = 2706944
XFS (dm-0): GH cycle = 6, GH bytes = 1633867
XFS: Assertion failed: 0, file: fs/xfs/xfs_log.c, line: 1310
------------[ cut here ]------------
Call Trace:
xlog_space_left+0xc3/0x110
xlog_grant_push_threshold+0x3f/0xf0
xlog_grant_push_ail+0x12/0x40
xfs_log_reserve+0xd2/0x270
? __might_sleep+0x4b/0x80
xfs_trans_reserve+0x18b/0x260
.....
There are two things here. Firstly, after a shutdown, the log head
and tail can be out of whack as things abort and release (or don't
release) resources, so checking them for sanity doesn't make much
sense. Secondly, xfs_log_reserve() can race with shutdown and so it
can still fail like this even though it has already checked for a
log shutdown before calling xlog_grant_push_ail().
So, before ASSERT failing in xlog_space_left(), make sure we haven't
already shut down....
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:41 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The reservation head is behind the tail. In this case we just want to
|
|
|
|
* return the size of the log as the amount of space left.
|
|
|
|
*/
|
|
|
|
xfs_alert(log->l_mp, "xlog_space_left: head behind tail");
|
|
|
|
xfs_alert(log->l_mp, " tail_cycle = %d, tail_bytes = %d",
|
|
|
|
tail_cycle, tail_bytes);
|
|
|
|
xfs_alert(log->l_mp, " GH cycle = %d, GH bytes = %d",
|
|
|
|
head_cycle, head_bytes);
|
|
|
|
ASSERT(0);
|
|
|
|
return log->l_logsize;
|
2010-12-21 01:08:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
2016-06-01 07:38:15 +00:00
|
|
|
static void
|
2019-06-29 02:27:25 +00:00
|
|
|
xlog_ioend_work(
|
|
|
|
struct work_struct *work)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-06-29 02:27:25 +00:00
|
|
|
struct xlog_in_core *iclog =
|
|
|
|
container_of(work, struct xlog_in_core, ic_end_io_work);
|
|
|
|
struct xlog *log = iclog->ic_log;
|
|
|
|
int error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
error = blk_status_to_errno(iclog->ic_bio.bi_status);
|
2019-06-29 02:27:21 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
/* treat writes with injected CRC errors as failed */
|
|
|
|
if (iclog->ic_fail_crc)
|
2019-06-29 02:27:25 +00:00
|
|
|
error = -EIO;
|
2019-06-29 02:27:21 +00:00
|
|
|
#endif
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2019-06-29 02:27:21 +00:00
|
|
|
* Race to shutdown the filesystem if we see an error.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2019-06-29 02:27:25 +00:00
|
|
|
if (XFS_TEST_ERROR(error, log->l_mp, XFS_ERRTAG_IODONE_IOERR)) {
|
|
|
|
xfs_alert(log->l_mp, "log I/O error %d", error);
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2007-05-14 08:24:16 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
xlog_state_done_syncing(iclog);
|
2019-06-29 02:27:25 +00:00
|
|
|
bio_uninit(&iclog->ic_bio);
|
xfs: unmount does not wait for shutdown during unmount
And interesting situation can occur if a log IO error occurs during
the unmount of a filesystem. The cases reported have the same
signature - the update of the superblock counters fails due to a log
write IO error:
XFS (dm-16): xfs_do_force_shutdown(0x2) called from line 1170 of file fs/xfs/xfs_log.c. Return address = 0xffffffffa08a44a1
XFS (dm-16): Log I/O Error Detected. Shutting down filesystem
XFS (dm-16): Unable to update superblock counters. Freespace may not be correct on next mount.
XFS (dm-16): xfs_log_force: error 5 returned.
XFS (¿-¿¿¿): Please umount the filesystem and rectify the problem(s)
It can be seen that the last line of output contains a corrupt
device name - this is because the log and xfs_mount structures have
already been freed by the time this message is printed. A kernel
oops closely follows.
The issue is that the shutdown is occurring in a separate IO
completion thread to the unmount. Once the shutdown processing has
started and all the iclogs are marked with XLOG_STATE_IOERROR, the
log shutdown code wakes anyone waiting on a log force so they can
process the shutdown error. This wakes up the unmount code that
is doing a synchronous transaction to update the superblock
counters.
The unmount path now sees all the iclogs are marked with
XLOG_STATE_IOERROR and so never waits on them again, knowing that if
it does, there will not be a wakeup trigger for it and we will hang
the unmount if we do. Hence the unmount runs through all the
remaining code and frees all the filesystem structures while the
xlog_iodone() is still processing the shutdown. When the log
shutdown processing completes, xfs_do_force_shutdown() emits the
"Please umount the filesystem and rectify the problem(s)" message,
and xlog_iodone() then aborts all the objects attached to the iclog.
An iclog that has already been freed....
The real issue here is that there is no serialisation point between
the log IO and the unmount. We have serialisations points for log
writes, log forces, reservations, etc, but we don't actually have
any code that wakes for log IO to fully complete. We do that for all
other types of object, so why not iclogbufs?
Well, it turns out that we can easily do this. We've got xfs_buf
handles, and that's what everyone else uses for IO serialisation.
i.e. bp->b_sema. So, lets hold iclogbufs locked over IO, and only
release the lock in xlog_iodone() when we are finished with the
buffer. That way before we tear down the iclog, we can lock and
unlock the buffer to ensure IO completion has finished completely
before we tear it down.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Tested-by: Mike Snitzer <snitzer@redhat.com>
Tested-by: Bob Mastors <bob.mastors@solidfire.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2014-04-16 22:15:26 +00:00
|
|
|
|
2007-05-14 08:24:16 +00:00
|
|
|
/*
|
2019-06-29 02:27:25 +00:00
|
|
|
* Drop the lock to signal that we are done. Nothing references the
|
|
|
|
* iclog after this, so an unmount waiting on this lock can now tear it
|
|
|
|
* down safely. As such, it is unsafe to reference the iclog after the
|
|
|
|
* unlock as we could race with it being freed.
|
2007-05-14 08:24:16 +00:00
|
|
|
*/
|
2019-06-29 02:27:25 +00:00
|
|
|
up(&iclog->ic_sema);
|
2012-11-12 11:54:01 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Return size of each in-core log record buffer.
|
|
|
|
*
|
2009-03-29 07:55:42 +00:00
|
|
|
* All machines get 8 x 32kB buffers by default, unless tuned otherwise.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* If the filesystem blocksize is too large, we may need to choose a
|
|
|
|
* larger size since the directory code currently logs entire blocks.
|
|
|
|
*/
|
|
|
|
STATIC void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_get_iclog_buffer_size(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xlog *log)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2007-08-16 06:24:43 +00:00
|
|
|
if (mp->m_logbufs <= 0)
|
2019-06-29 02:27:20 +00:00
|
|
|
mp->m_logbufs = XLOG_MAX_ICLOGS;
|
|
|
|
if (mp->m_logbsize <= 0)
|
|
|
|
mp->m_logbsize = XLOG_BIG_RECORD_BSIZE;
|
|
|
|
|
|
|
|
log->l_iclog_bufs = mp->m_logbufs;
|
|
|
|
log->l_iclog_size = mp->m_logbsize;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2019-06-29 02:27:20 +00:00
|
|
|
* # headers = size / 32k - one header holds cycles from 32k of data.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2019-06-29 02:27:20 +00:00
|
|
|
log->l_iclog_heads =
|
|
|
|
DIV_ROUND_UP(mp->m_logbsize, XLOG_HEADER_CYCLE_SIZE);
|
|
|
|
log->l_iclog_hsize = log->l_iclog_heads << BBSHIFT;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-10-08 10:56:02 +00:00
|
|
|
void
|
|
|
|
xfs_log_work_queue(
|
|
|
|
struct xfs_mount *mp)
|
|
|
|
{
|
2017-03-28 21:51:44 +00:00
|
|
|
queue_delayed_work(mp->m_sync_workqueue, &mp->m_log->l_work,
|
2012-10-08 10:56:02 +00:00
|
|
|
msecs_to_jiffies(xfs_syncd_centisecs * 10));
|
|
|
|
}
|
|
|
|
|
2021-08-08 15:27:12 +00:00
|
|
|
/*
|
|
|
|
* Clear the log incompat flags if we have the opportunity.
|
|
|
|
*
|
|
|
|
* This only happens if we're about to log the second dummy transaction as part
|
|
|
|
* of covering the log and we can get the log incompat feature usage lock.
|
|
|
|
*/
|
|
|
|
static inline void
|
|
|
|
xlog_clear_incompat(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
struct xfs_mount *mp = log->l_mp;
|
|
|
|
|
|
|
|
if (!xfs_sb_has_incompat_log_feature(&mp->m_sb,
|
|
|
|
XFS_SB_FEAT_INCOMPAT_LOG_ALL))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (log->l_covered_state != XLOG_STATE_COVER_DONE2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!down_write_trylock(&log->l_incompat_users))
|
|
|
|
return;
|
|
|
|
|
|
|
|
xfs_clear_incompat_log_features(mp);
|
|
|
|
up_write(&log->l_incompat_users);
|
|
|
|
}
|
|
|
|
|
2012-10-08 10:56:02 +00:00
|
|
|
/*
|
|
|
|
* Every sync period we need to unpin all items in the AIL and push them to
|
|
|
|
* disk. If there is nothing dirty, then we might need to cover the log to
|
|
|
|
* indicate that the filesystem is idle.
|
|
|
|
*/
|
2016-06-01 07:38:15 +00:00
|
|
|
static void
|
2012-10-08 10:56:02 +00:00
|
|
|
xfs_log_worker(
|
|
|
|
struct work_struct *work)
|
|
|
|
{
|
|
|
|
struct xlog *log = container_of(to_delayed_work(work),
|
|
|
|
struct xlog, l_work);
|
|
|
|
struct xfs_mount *mp = log->l_mp;
|
|
|
|
|
|
|
|
/* dgc: errors ignored - not fatal and nowhere to report them */
|
2021-01-23 00:48:21 +00:00
|
|
|
if (xfs_fs_writable(mp, SB_FREEZE_WRITE) && xfs_log_need_covered(mp)) {
|
2015-01-21 22:10:31 +00:00
|
|
|
/*
|
|
|
|
* Dump a transaction into the log that contains no real change.
|
|
|
|
* This is needed to stamp the current tail LSN into the log
|
|
|
|
* during the covering operation.
|
|
|
|
*
|
|
|
|
* We cannot use an inode here for this - that will push dirty
|
|
|
|
* state back up into the VFS and then periodic inode flushing
|
|
|
|
* will prevent log covering from making progress. Hence we
|
|
|
|
* synchronously log the superblock instead to ensure the
|
|
|
|
* superblock is immediately unpinned and can be written back.
|
|
|
|
*/
|
2021-08-08 15:27:12 +00:00
|
|
|
xlog_clear_incompat(log);
|
2015-01-21 22:10:31 +00:00
|
|
|
xfs_sync_sb(mp, true);
|
|
|
|
} else
|
2012-10-08 10:56:02 +00:00
|
|
|
xfs_log_force(mp, 0);
|
|
|
|
|
|
|
|
/* start pushing all the metadata that is currently dirty */
|
|
|
|
xfs_ail_push_all(mp->m_ail);
|
|
|
|
|
|
|
|
/* queue us up again */
|
|
|
|
xfs_log_work_queue(mp);
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* This routine initializes some of the log structure for a given mount point.
|
|
|
|
* Its primary purpose is to fill in enough, so recovery can occur. However,
|
|
|
|
* some other stuff may be filled in too.
|
|
|
|
*/
|
2012-06-14 14:22:16 +00:00
|
|
|
STATIC struct xlog *
|
|
|
|
xlog_alloc_log(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xfs_buftarg *log_target,
|
|
|
|
xfs_daddr_t blk_offset,
|
|
|
|
int num_bblks)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-06-14 14:22:16 +00:00
|
|
|
struct xlog *log;
|
2005-04-16 22:20:36 +00:00
|
|
|
xlog_rec_header_t *head;
|
|
|
|
xlog_in_core_t **iclogp;
|
|
|
|
xlog_in_core_t *iclog, *prev_iclog=NULL;
|
|
|
|
int i;
|
2014-06-25 04:58:08 +00:00
|
|
|
int error = -ENOMEM;
|
2010-04-20 07:09:59 +00:00
|
|
|
uint log2_size = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2012-06-14 14:22:16 +00:00
|
|
|
log = kmem_zalloc(sizeof(struct xlog), KM_MAYFAIL);
|
2009-04-06 16:39:27 +00:00
|
|
|
if (!log) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp, "Log allocation failed: No memory!");
|
2009-04-06 16:39:27 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
log->l_mp = mp;
|
|
|
|
log->l_targ = log_target;
|
|
|
|
log->l_logsize = BBTOB(num_bblks);
|
|
|
|
log->l_logBBstart = blk_offset;
|
|
|
|
log->l_logBBsize = num_bblks;
|
|
|
|
log->l_covered_state = XLOG_STATE_COVER_IDLE;
|
2021-08-11 00:59:02 +00:00
|
|
|
set_bit(XLOG_ACTIVE_RECOVERY, &log->l_opstate);
|
2012-10-08 10:56:02 +00:00
|
|
|
INIT_DELAYED_WORK(&log->l_work, xfs_log_worker);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
log->l_prev_block = -1;
|
|
|
|
/* log->l_tail_lsn = 0x100000000LL; cycle = 1; current block = 0 */
|
2010-12-21 01:28:39 +00:00
|
|
|
xlog_assign_atomic_lsn(&log->l_tail_lsn, 1, 0);
|
|
|
|
xlog_assign_atomic_lsn(&log->l_last_sync_lsn, 1, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
log->l_curr_cycle = 1; /* 0 is bad since this is initial value */
|
2012-02-20 02:31:26 +00:00
|
|
|
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_logv2(mp) && mp->m_sb.sb_logsunit > 1)
|
2021-06-18 15:21:48 +00:00
|
|
|
log->l_iclog_roundoff = mp->m_sb.sb_logsunit;
|
|
|
|
else
|
|
|
|
log->l_iclog_roundoff = BBSIZE;
|
|
|
|
|
2012-02-20 02:31:26 +00:00
|
|
|
xlog_grant_head_init(&log->l_reserve_head);
|
|
|
|
xlog_grant_head_init(&log->l_write_head);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2014-06-25 04:58:08 +00:00
|
|
|
error = -EFSCORRUPTED;
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_sector(mp)) {
|
2010-04-20 07:09:59 +00:00
|
|
|
log2_size = mp->m_sb.sb_logsectlog;
|
|
|
|
if (log2_size < BBSHIFT) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp, "Log sector size too small (0x%x < 0x%x)",
|
|
|
|
log2_size, BBSHIFT);
|
2009-04-06 16:39:27 +00:00
|
|
|
goto out_free_log;
|
|
|
|
}
|
|
|
|
|
2010-04-20 07:09:59 +00:00
|
|
|
log2_size -= BBSHIFT;
|
|
|
|
if (log2_size > mp->m_sectbb_log) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp, "Log sector size too large (0x%x > 0x%x)",
|
|
|
|
log2_size, mp->m_sectbb_log);
|
2009-04-06 16:39:27 +00:00
|
|
|
goto out_free_log;
|
|
|
|
}
|
2010-04-20 07:09:59 +00:00
|
|
|
|
|
|
|
/* for larger sector sizes, must have v2 or external log */
|
|
|
|
if (log2_size && log->l_logBBstart > 0 &&
|
2021-08-19 01:46:37 +00:00
|
|
|
!xfs_has_logv2(mp)) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(mp,
|
|
|
|
"log sector size (0x%x) invalid for configuration.",
|
|
|
|
log2_size);
|
2009-04-06 16:39:27 +00:00
|
|
|
goto out_free_log;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-04-20 07:09:59 +00:00
|
|
|
log->l_sectBBsize = 1 << log2_size;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-08 15:27:12 +00:00
|
|
|
init_rwsem(&log->l_incompat_users);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
xlog_get_iclog_buffer_size(mp, log);
|
|
|
|
|
2007-10-11 07:43:56 +00:00
|
|
|
spin_lock_init(&log->l_icloglock);
|
2010-12-21 01:09:01 +00:00
|
|
|
init_waitqueue_head(&log->l_flush_wait);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
iclogp = &log->l_iclog;
|
|
|
|
/*
|
|
|
|
* The amount of memory to allocate for the iclog structure is
|
|
|
|
* rather funky due to the way the structure is defined. It is
|
|
|
|
* done this way so that we can use different sizes for machines
|
|
|
|
* with different amounts of memory. See the definition of
|
|
|
|
* xlog_in_core_t in xfs_log_priv.h for details.
|
|
|
|
*/
|
|
|
|
ASSERT(log->l_iclog_size >= 4096);
|
2019-06-29 02:27:25 +00:00
|
|
|
for (i = 0; i < log->l_iclog_bufs; i++) {
|
2019-06-29 02:31:36 +00:00
|
|
|
size_t bvec_size = howmany(log->l_iclog_size, PAGE_SIZE) *
|
|
|
|
sizeof(struct bio_vec);
|
2019-06-29 02:27:25 +00:00
|
|
|
|
|
|
|
iclog = kmem_zalloc(sizeof(*iclog) + bvec_size, KM_MAYFAIL);
|
|
|
|
if (!iclog)
|
2008-11-10 05:50:24 +00:00
|
|
|
goto out_free_iclog;
|
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
*iclogp = iclog;
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog->ic_prev = prev_iclog;
|
|
|
|
prev_iclog = iclog;
|
2007-05-14 08:23:50 +00:00
|
|
|
|
2021-08-09 17:10:01 +00:00
|
|
|
iclog->ic_data = kvzalloc(log->l_iclog_size,
|
|
|
|
GFP_KERNEL | __GFP_RETRY_MAYFAIL);
|
2019-06-29 02:27:25 +00:00
|
|
|
if (!iclog->ic_data)
|
2008-11-10 05:50:24 +00:00
|
|
|
goto out_free_iclog;
|
2005-04-16 22:20:36 +00:00
|
|
|
head = &iclog->ic_header;
|
|
|
|
memset(head, 0, sizeof(xlog_rec_header_t));
|
2007-10-12 00:59:34 +00:00
|
|
|
head->h_magicno = cpu_to_be32(XLOG_HEADER_MAGIC_NUM);
|
|
|
|
head->h_version = cpu_to_be32(
|
2021-08-19 01:46:37 +00:00
|
|
|
xfs_has_logv2(log->l_mp) ? 2 : 1);
|
2007-10-12 00:59:34 +00:00
|
|
|
head->h_size = cpu_to_be32(log->l_iclog_size);
|
2005-04-16 22:20:36 +00:00
|
|
|
/* new fields */
|
2007-10-12 00:59:34 +00:00
|
|
|
head->h_fmt = cpu_to_be32(XLOG_FMT);
|
2005-04-16 22:20:36 +00:00
|
|
|
memcpy(&head->h_fs_uuid, &mp->m_sb.sb_uuid, sizeof(uuid_t));
|
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
iclog->ic_size = log->l_iclog_size - log->l_iclog_hsize;
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog->ic_state = XLOG_STATE_ACTIVE;
|
|
|
|
iclog->ic_log = log;
|
2008-04-10 02:18:39 +00:00
|
|
|
atomic_set(&iclog->ic_refcnt, 0);
|
2019-06-29 02:27:34 +00:00
|
|
|
INIT_LIST_HEAD(&iclog->ic_callbacks);
|
2022-04-21 00:35:53 +00:00
|
|
|
iclog->ic_datap = (void *)iclog->ic_data + log->l_iclog_hsize;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-12-21 01:09:01 +00:00
|
|
|
init_waitqueue_head(&iclog->ic_force_wait);
|
|
|
|
init_waitqueue_head(&iclog->ic_write_wait);
|
2019-06-29 02:27:25 +00:00
|
|
|
INIT_WORK(&iclog->ic_end_io_work, xlog_ioend_work);
|
|
|
|
sema_init(&iclog->ic_sema, 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
iclogp = &iclog->ic_next;
|
|
|
|
}
|
|
|
|
*iclogp = log->l_iclog; /* complete ring */
|
|
|
|
log->l_iclog->ic_prev = prev_iclog; /* re-write 1st prev ptr */
|
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
log->l_ioend_workqueue = alloc_workqueue("xfs-log/%s",
|
2021-01-23 00:48:42 +00:00
|
|
|
XFS_WQFLAGS(WQ_FREEZABLE | WQ_MEM_RECLAIM |
|
|
|
|
WQ_HIGHPRI),
|
|
|
|
0, mp->m_super->s_id);
|
2019-06-29 02:27:25 +00:00
|
|
|
if (!log->l_ioend_workqueue)
|
|
|
|
goto out_free_iclog;
|
|
|
|
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
error = xlog_cil_init(log);
|
|
|
|
if (error)
|
2019-06-29 02:27:25 +00:00
|
|
|
goto out_destroy_workqueue;
|
2005-04-16 22:20:36 +00:00
|
|
|
return log;
|
2008-11-10 05:50:24 +00:00
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
out_destroy_workqueue:
|
|
|
|
destroy_workqueue(log->l_ioend_workqueue);
|
2008-11-10 05:50:24 +00:00
|
|
|
out_free_iclog:
|
|
|
|
for (iclog = log->l_iclog; iclog; iclog = prev_iclog) {
|
|
|
|
prev_iclog = iclog->ic_next;
|
2019-06-29 02:27:25 +00:00
|
|
|
kmem_free(iclog->ic_data);
|
2008-11-10 05:50:24 +00:00
|
|
|
kmem_free(iclog);
|
2019-12-03 15:53:15 +00:00
|
|
|
if (prev_iclog == log->l_iclog)
|
|
|
|
break;
|
2008-11-10 05:50:24 +00:00
|
|
|
}
|
|
|
|
out_free_log:
|
|
|
|
kmem_free(log);
|
2009-04-06 16:39:27 +00:00
|
|
|
out:
|
2014-06-25 04:58:08 +00:00
|
|
|
return ERR_PTR(error);
|
2005-04-16 22:20:36 +00:00
|
|
|
} /* xlog_alloc_log */
|
|
|
|
|
|
|
|
/*
|
2020-09-26 00:39:51 +00:00
|
|
|
* Compute the LSN that we'd need to push the log tail towards in order to have
|
|
|
|
* (a) enough on-disk log space to log the number of bytes specified, (b) at
|
|
|
|
* least 25% of the log space free, and (c) at least 256 blocks free. If the
|
|
|
|
* log free space already meets all three thresholds, this function returns
|
|
|
|
* NULLCOMMITLSN.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2020-09-26 00:39:51 +00:00
|
|
|
xfs_lsn_t
|
|
|
|
xlog_grant_push_threshold(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2010-12-21 01:09:20 +00:00
|
|
|
int need_bytes)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-12-21 01:09:20 +00:00
|
|
|
xfs_lsn_t threshold_lsn = 0;
|
2010-12-03 11:11:29 +00:00
|
|
|
xfs_lsn_t last_sync_lsn;
|
2010-12-21 01:09:20 +00:00
|
|
|
int free_blocks;
|
|
|
|
int free_bytes;
|
|
|
|
int threshold_block;
|
|
|
|
int threshold_cycle;
|
|
|
|
int free_threshold;
|
|
|
|
|
|
|
|
ASSERT(BTOBB(need_bytes) < log->l_logBBsize);
|
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
free_bytes = xlog_space_left(log, &log->l_reserve_head.grant);
|
2010-12-21 01:09:20 +00:00
|
|
|
free_blocks = BTOBBT(free_bytes);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the threshold for the minimum number of free blocks in the
|
|
|
|
* log to the maximum of what the caller needs, one quarter of the
|
|
|
|
* log, and 256 blocks.
|
|
|
|
*/
|
|
|
|
free_threshold = BTOBB(need_bytes);
|
2018-06-07 14:54:02 +00:00
|
|
|
free_threshold = max(free_threshold, (log->l_logBBsize >> 2));
|
|
|
|
free_threshold = max(free_threshold, 256);
|
2010-12-21 01:09:20 +00:00
|
|
|
if (free_blocks >= free_threshold)
|
2020-09-26 00:39:51 +00:00
|
|
|
return NULLCOMMITLSN;
|
2010-12-21 01:09:20 +00:00
|
|
|
|
2010-12-21 01:28:39 +00:00
|
|
|
xlog_crack_atomic_lsn(&log->l_tail_lsn, &threshold_cycle,
|
|
|
|
&threshold_block);
|
|
|
|
threshold_block += free_threshold;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (threshold_block >= log->l_logBBsize) {
|
2010-12-21 01:09:20 +00:00
|
|
|
threshold_block -= log->l_logBBsize;
|
|
|
|
threshold_cycle += 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2010-12-21 01:09:20 +00:00
|
|
|
threshold_lsn = xlog_assign_lsn(threshold_cycle,
|
|
|
|
threshold_block);
|
|
|
|
/*
|
|
|
|
* Don't pass in an lsn greater than the lsn of the last
|
2010-12-03 11:11:29 +00:00
|
|
|
* log record known to be on disk. Use a snapshot of the last sync lsn
|
|
|
|
* so that it doesn't change between the compare and the set.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2010-12-03 11:11:29 +00:00
|
|
|
last_sync_lsn = atomic64_read(&log->l_last_sync_lsn);
|
|
|
|
if (XFS_LSN_CMP(threshold_lsn, last_sync_lsn) > 0)
|
|
|
|
threshold_lsn = last_sync_lsn;
|
2010-12-21 01:09:20 +00:00
|
|
|
|
2020-09-26 00:39:51 +00:00
|
|
|
return threshold_lsn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Push the tail of the log if we need to do so to maintain the free log space
|
|
|
|
* thresholds set out by xlog_grant_push_threshold. We may need to adopt a
|
|
|
|
* policy which pushes on an lsn which is further along in the log once we
|
|
|
|
* reach the high water mark. In this manner, we would be creating a low water
|
|
|
|
* mark.
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xlog_grant_push_ail(
|
|
|
|
struct xlog *log,
|
|
|
|
int need_bytes)
|
|
|
|
{
|
|
|
|
xfs_lsn_t threshold_lsn;
|
|
|
|
|
|
|
|
threshold_lsn = xlog_grant_push_threshold(log, need_bytes);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (threshold_lsn == NULLCOMMITLSN || xlog_is_shutdown(log))
|
2020-09-26 00:39:51 +00:00
|
|
|
return;
|
|
|
|
|
2010-12-21 01:09:20 +00:00
|
|
|
/*
|
|
|
|
* Get the transaction layer to kick the dirty buffers out to
|
|
|
|
* disk asynchronously. No point in trying to do this if
|
|
|
|
* the filesystem is shutting down.
|
|
|
|
*/
|
2020-09-26 00:39:51 +00:00
|
|
|
xfs_ail_push(log->l_ailp, threshold_lsn);
|
2010-12-21 01:09:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
/*
|
|
|
|
* Stamp cycle number in every block
|
|
|
|
*/
|
|
|
|
STATIC void
|
|
|
|
xlog_pack_data(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
int roundoff)
|
|
|
|
{
|
|
|
|
int i, j, k;
|
|
|
|
int size = iclog->ic_offset + roundoff;
|
|
|
|
__be32 cycle_lsn;
|
2015-06-21 23:45:10 +00:00
|
|
|
char *dp;
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
|
|
|
|
cycle_lsn = CYCLE_LSN_DISK(iclog->ic_header.h_lsn);
|
|
|
|
|
|
|
|
dp = iclog->ic_datap;
|
|
|
|
for (i = 0; i < BTOBB(size); i++) {
|
|
|
|
if (i >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE))
|
|
|
|
break;
|
|
|
|
iclog->ic_header.h_cycle_data[i] = *(__be32 *)dp;
|
|
|
|
*(__be32 *)dp = cycle_lsn;
|
|
|
|
dp += BBSIZE;
|
|
|
|
}
|
|
|
|
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_logv2(log->l_mp)) {
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
xlog_in_core_2_t *xhdr = iclog->ic_data;
|
|
|
|
|
|
|
|
for ( ; i < BTOBB(size); i++) {
|
|
|
|
j = i / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
|
|
|
k = i % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
|
|
|
xhdr[j].hic_xheader.xh_cycle_data[k] = *(__be32 *)dp;
|
|
|
|
*(__be32 *)dp = cycle_lsn;
|
|
|
|
dp += BBSIZE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 1; i < log->l_iclog_heads; i++)
|
|
|
|
xhdr[i].hic_xheader.xh_cycle = cycle_lsn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Calculate the checksum for a log buffer.
|
|
|
|
*
|
|
|
|
* This is a little more complicated than it should be because the various
|
|
|
|
* headers and the actual data are non-contiguous.
|
|
|
|
*/
|
2012-11-28 02:01:03 +00:00
|
|
|
__le32
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
xlog_cksum(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_rec_header *rhead,
|
|
|
|
char *dp,
|
|
|
|
int size)
|
|
|
|
{
|
2017-06-16 18:00:05 +00:00
|
|
|
uint32_t crc;
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
|
|
|
|
/* first generate the crc for the record header ... */
|
2016-12-05 03:40:32 +00:00
|
|
|
crc = xfs_start_cksum_update((char *)rhead,
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
sizeof(struct xlog_rec_header),
|
|
|
|
offsetof(struct xlog_rec_header, h_crc));
|
|
|
|
|
|
|
|
/* ... then for additional cycle data for v2 logs ... */
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_logv2(log->l_mp)) {
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
union xlog_in_core2 *xhdr = (union xlog_in_core2 *)rhead;
|
|
|
|
int i;
|
2015-08-18 23:59:50 +00:00
|
|
|
int xheads;
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
|
2020-09-22 16:41:06 +00:00
|
|
|
xheads = DIV_ROUND_UP(size, XLOG_HEADER_CYCLE_SIZE);
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
|
2015-08-18 23:59:50 +00:00
|
|
|
for (i = 1; i < xheads; i++) {
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
crc = crc32c(crc, &xhdr[i].hic_xheader,
|
|
|
|
sizeof(struct xlog_rec_ext_header));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ... and finally for the payload */
|
|
|
|
crc = crc32c(crc, dp, size);
|
|
|
|
|
|
|
|
return xfs_end_cksum(crc);
|
|
|
|
}
|
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
static void
|
|
|
|
xlog_bio_end_io(
|
|
|
|
struct bio *bio)
|
|
|
|
{
|
|
|
|
struct xlog_in_core *iclog = bio->bi_private;
|
|
|
|
|
2019-06-29 02:27:25 +00:00
|
|
|
queue_work(iclog->ic_log->l_ioend_workqueue,
|
2019-06-29 02:27:25 +00:00
|
|
|
&iclog->ic_end_io_work);
|
|
|
|
}
|
|
|
|
|
2020-03-25 16:17:13 +00:00
|
|
|
static int
|
2019-06-29 02:27:25 +00:00
|
|
|
xlog_map_iclog_data(
|
|
|
|
struct bio *bio,
|
|
|
|
void *data,
|
|
|
|
size_t count)
|
|
|
|
{
|
|
|
|
do {
|
|
|
|
struct page *page = kmem_to_page(data);
|
|
|
|
unsigned int off = offset_in_page(data);
|
|
|
|
size_t len = min_t(size_t, count, PAGE_SIZE - off);
|
|
|
|
|
2020-03-25 16:17:13 +00:00
|
|
|
if (bio_add_page(bio, page, len, off) != len)
|
|
|
|
return -EIO;
|
2019-06-29 02:27:25 +00:00
|
|
|
|
|
|
|
data += len;
|
|
|
|
count -= len;
|
|
|
|
} while (count);
|
2020-03-25 16:17:13 +00:00
|
|
|
|
|
|
|
return 0;
|
2019-06-29 02:27:25 +00:00
|
|
|
}
|
|
|
|
|
2019-06-29 02:27:22 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_write_iclog(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
uint64_t bno,
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
unsigned int count)
|
2010-01-13 22:17:57 +00:00
|
|
|
{
|
2019-06-29 02:27:22 +00:00
|
|
|
ASSERT(bno < log->l_logBBsize);
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_write(iclog, _RET_IP_);
|
2019-06-29 02:27:22 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We lock the iclogbufs here so that we can serialise against I/O
|
|
|
|
* completion during unmount. We might be processing a shutdown
|
|
|
|
* triggered during unmount, and that can occur asynchronously to the
|
|
|
|
* unmount thread, and hence we need to ensure that completes before
|
|
|
|
* tearing down the iclogbufs. Hence we need to hold the buffer lock
|
|
|
|
* across the log IO to archieve that.
|
|
|
|
*/
|
2019-06-29 02:27:25 +00:00
|
|
|
down(&iclog->ic_sema);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log)) {
|
2010-01-13 22:17:57 +00:00
|
|
|
/*
|
|
|
|
* It would seem logical to return EIO here, but we rely on
|
|
|
|
* the log state machine to propagate I/O errors instead of
|
2019-06-29 02:27:25 +00:00
|
|
|
* doing it here. We kick of the state machine and unlock
|
|
|
|
* the buffer manually, the code needs to be kept in sync
|
|
|
|
* with the I/O completion path.
|
2010-01-13 22:17:57 +00:00
|
|
|
*/
|
2020-03-20 15:49:20 +00:00
|
|
|
xlog_state_done_syncing(iclog);
|
2019-06-29 02:27:25 +00:00
|
|
|
up(&iclog->ic_sema);
|
2019-06-29 02:27:22 +00:00
|
|
|
return;
|
2010-01-13 22:17:57 +00:00
|
|
|
}
|
|
|
|
|
2020-03-25 03:10:27 +00:00
|
|
|
/*
|
|
|
|
* We use REQ_SYNC | REQ_IDLE here to tell the block layer the are more
|
|
|
|
* IOs coming immediately after this one. This prevents the block layer
|
|
|
|
* writeback throttle from throttling log writes behind background
|
|
|
|
* metadata writeback and causing priority inversions.
|
|
|
|
*/
|
2022-01-24 09:11:06 +00:00
|
|
|
bio_init(&iclog->ic_bio, log->l_targ->bt_bdev, iclog->ic_bvec,
|
|
|
|
howmany(count, PAGE_SIZE),
|
|
|
|
REQ_OP_WRITE | REQ_META | REQ_SYNC | REQ_IDLE);
|
|
|
|
iclog->ic_bio.bi_iter.bi_sector = log->l_logBBstart + bno;
|
|
|
|
iclog->ic_bio.bi_end_io = xlog_bio_end_io;
|
|
|
|
iclog->ic_bio.bi_private = iclog;
|
|
|
|
|
2021-07-27 23:23:47 +00:00
|
|
|
if (iclog->ic_flags & XLOG_ICL_NEED_FLUSH) {
|
2019-06-29 02:27:25 +00:00
|
|
|
iclog->ic_bio.bi_opf |= REQ_PREFLUSH;
|
2021-07-27 23:23:47 +00:00
|
|
|
/*
|
|
|
|
* For external log devices, we also need to flush the data
|
|
|
|
* device cache first to ensure all metadata writeback covered
|
|
|
|
* by the LSN in this iclog is on stable storage. This is slow,
|
|
|
|
* but it *must* complete before we issue the external log IO.
|
|
|
|
*/
|
|
|
|
if (log->l_targ != log->l_mp->m_ddev_targp)
|
|
|
|
blkdev_issue_flush(log->l_mp->m_ddev_targp->bt_bdev);
|
|
|
|
}
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
if (iclog->ic_flags & XLOG_ICL_NEED_FUA)
|
|
|
|
iclog->ic_bio.bi_opf |= REQ_FUA;
|
2021-07-27 23:23:47 +00:00
|
|
|
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
iclog->ic_flags &= ~(XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA);
|
2019-06-29 02:27:25 +00:00
|
|
|
|
2020-03-25 16:17:13 +00:00
|
|
|
if (xlog_map_iclog_data(&iclog->ic_bio, iclog->ic_data, count)) {
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR);
|
2020-03-25 16:17:13 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-06-29 02:27:25 +00:00
|
|
|
if (is_vmalloc_addr(iclog->ic_data))
|
2019-10-14 17:36:40 +00:00
|
|
|
flush_kernel_vmap_range(iclog->ic_data, count);
|
2019-06-29 02:27:25 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If this log buffer would straddle the end of the log we will have
|
|
|
|
* to split it up into two bios, so that we can continue at the start.
|
|
|
|
*/
|
|
|
|
if (bno + BTOBB(count) > log->l_logBBsize) {
|
|
|
|
struct bio *split;
|
|
|
|
|
|
|
|
split = bio_split(&iclog->ic_bio, log->l_logBBsize - bno,
|
|
|
|
GFP_NOIO, &fs_bio_set);
|
|
|
|
bio_chain(split, &iclog->ic_bio);
|
|
|
|
submit_bio(split);
|
|
|
|
|
|
|
|
/* restart at logical offset zero for the remainder */
|
|
|
|
iclog->ic_bio.bi_iter.bi_sector = log->l_logBBstart;
|
|
|
|
}
|
|
|
|
|
|
|
|
submit_bio(&iclog->ic_bio);
|
2010-01-13 22:17:57 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-06-29 02:27:22 +00:00
|
|
|
/*
|
|
|
|
* We need to bump cycle number for the part of the iclog that is
|
|
|
|
* written to the start of the log. Watch out for the header magic
|
|
|
|
* number case, though.
|
|
|
|
*/
|
2019-06-29 02:27:25 +00:00
|
|
|
static void
|
2019-06-29 02:27:22 +00:00
|
|
|
xlog_split_iclog(
|
|
|
|
struct xlog *log,
|
|
|
|
void *data,
|
|
|
|
uint64_t bno,
|
|
|
|
unsigned int count)
|
|
|
|
{
|
|
|
|
unsigned int split_offset = BBTOB(log->l_logBBsize - bno);
|
|
|
|
unsigned int i;
|
|
|
|
|
|
|
|
for (i = split_offset; i < count; i += BBSIZE) {
|
|
|
|
uint32_t cycle = get_unaligned_be32(data + i);
|
|
|
|
|
|
|
|
if (++cycle == XLOG_HEADER_MAGIC_NUM)
|
|
|
|
cycle++;
|
|
|
|
put_unaligned_be32(cycle, data + i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-29 02:27:23 +00:00
|
|
|
static int
|
|
|
|
xlog_calc_iclog_size(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
uint32_t *roundoff)
|
|
|
|
{
|
|
|
|
uint32_t count_init, count;
|
|
|
|
|
|
|
|
/* Add for LR header */
|
|
|
|
count_init = log->l_iclog_hsize + iclog->ic_offset;
|
2021-06-18 15:21:48 +00:00
|
|
|
count = roundup(count_init, log->l_iclog_roundoff);
|
2019-06-29 02:27:23 +00:00
|
|
|
|
|
|
|
*roundoff = count - count_init;
|
|
|
|
|
2021-06-18 15:21:48 +00:00
|
|
|
ASSERT(count >= count_init);
|
|
|
|
ASSERT(*roundoff < log->l_iclog_roundoff);
|
2019-06-29 02:27:23 +00:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2022-07-31 16:20:25 +00:00
|
|
|
* Flush out the in-core log (iclog) to the on-disk log in an asynchronous
|
2005-04-16 22:20:36 +00:00
|
|
|
* fashion. Previously, we should have moved the current iclog
|
|
|
|
* ptr in the log to point to the next available iclog. This allows further
|
|
|
|
* write to continue while this code syncs out an iclog ready to go.
|
|
|
|
* Before an in-core log can be written out, the data section must be scanned
|
|
|
|
* to save away the 1st word of each BBSIZE block into the header. We replace
|
|
|
|
* it with the current cycle count. Each BBSIZE block is tagged with the
|
|
|
|
* cycle count because there in an implicit assumption that drives will
|
|
|
|
* guarantee that entire 512 byte blocks get written at once. In other words,
|
|
|
|
* we can't have part of a 512 byte block written and part not written. By
|
|
|
|
* tagging each block, we will know which blocks are valid when recovering
|
|
|
|
* after an unclean shutdown.
|
|
|
|
*
|
|
|
|
* This routine is single threaded on the iclog. No other thread can be in
|
|
|
|
* this routine with the same iclog. Changing contents of iclog can there-
|
|
|
|
* fore be done without grabbing the state machine lock. Updating the global
|
|
|
|
* log will require grabbing the lock though.
|
|
|
|
*
|
|
|
|
* The entire log manager uses a logical block numbering scheme. Only
|
2019-06-29 02:27:22 +00:00
|
|
|
* xlog_write_iclog knows about the fact that the log may not start with
|
|
|
|
* block zero on a given device.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2019-06-29 02:27:22 +00:00
|
|
|
STATIC void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_sync(
|
|
|
|
struct xlog *log,
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
struct xlog_ticket *ticket)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-06-29 02:27:23 +00:00
|
|
|
unsigned int count; /* byte count of bwrite */
|
|
|
|
unsigned int roundoff; /* roundoff to BB or stripe */
|
|
|
|
uint64_t bno;
|
|
|
|
unsigned int size;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-03-06 02:44:14 +00:00
|
|
|
ASSERT(atomic_read(&iclog->ic_refcnt) == 0);
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_sync(iclog, _RET_IP_);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-06-29 02:27:23 +00:00
|
|
|
count = xlog_calc_iclog_size(log, iclog, &roundoff);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
/*
|
|
|
|
* If we have a ticket, account for the roundoff via the ticket
|
|
|
|
* reservation to avoid touching the hot grant heads needlessly.
|
|
|
|
* Otherwise, we have to move grant heads directly.
|
|
|
|
*/
|
|
|
|
if (ticket) {
|
|
|
|
ticket->t_curr_res -= roundoff;
|
|
|
|
} else {
|
|
|
|
xlog_grant_add_space(log, &log->l_reserve_head.grant, roundoff);
|
|
|
|
xlog_grant_add_space(log, &log->l_write_head.grant, roundoff);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* put cycle number in every block */
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
xlog_pack_data(log, iclog, roundoff);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* real byte length */
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
size = iclog->ic_offset;
|
2021-08-19 01:46:37 +00:00
|
|
|
if (xfs_has_logv2(log->l_mp))
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
size += roundoff;
|
|
|
|
iclog->ic_header.h_len = cpu_to_be32(size);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-06-29 02:27:23 +00:00
|
|
|
XFS_STATS_INC(log->l_mp, xs_log_writes);
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_ADD(log->l_mp, xs_log_blocks, BTOBB(count));
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-06-29 02:27:22 +00:00
|
|
|
bno = BLOCK_LSN(be64_to_cpu(iclog->ic_header.h_lsn));
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* Do we need to split this write into 2 parts? */
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
if (bno + BTOBB(count) > log->l_logBBsize)
|
2019-06-29 02:27:25 +00:00
|
|
|
xlog_split_iclog(log, &iclog->ic_header, bno, count);
|
xfs: add CRC checks to the log
Implement CRCs for the log buffers. We re-use a field in
struct xlog_rec_header that was used for a weak checksum of the
log buffer payload in debug builds before.
The new checksumming uses the crc32c checksum we will use elsewhere
in XFS, and also protects the record header and addition cycle data.
Due to this there are some interesting changes in xlog_sync, as we
need to do the cycle wrapping for the split buffer case much earlier,
as we would touch the buffer after generating the checksum otherwise.
The CRC calculation is always enabled, even for non-CRC filesystems,
as adding this CRC does not change the log format. On non-CRC
filesystems, only issue an alert if a CRC mismatch is found and
allow recovery to continue - this will act as an indicator that
log recovery problems are a result of log corruption. On CRC enabled
filesystems, however, log recovery will fail.
Note that existing debug kernels will write a simple checksum value
to the log, so the first time this is run on a filesystem taht was
last used on a debug kernel it will through CRC mismatch warning
errors. These can be ignored.
Initially based on a patch from Dave Chinner, then modified
significantly by Christoph Hellwig. Modified again by Dave Chinner
to get to this version.
Signed-off-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Mark Tinguely <tinguely@sgi.com>
Signed-off-by: Ben Myers <bpm@sgi.com>
2012-11-12 11:54:24 +00:00
|
|
|
|
|
|
|
/* calculcate the checksum */
|
|
|
|
iclog->ic_header.h_crc = xlog_cksum(log, &iclog->ic_header,
|
|
|
|
iclog->ic_datap, size);
|
2016-01-04 20:41:16 +00:00
|
|
|
/*
|
|
|
|
* Intentionally corrupt the log record CRC based on the error injection
|
|
|
|
* frequency, if defined. This facilitates testing log recovery in the
|
|
|
|
* event of torn writes. Hence, set the IOABORT state to abort the log
|
|
|
|
* write on I/O completion and shutdown the fs. The subsequent mount
|
|
|
|
* detects the bad CRC and attempts to recover.
|
|
|
|
*/
|
2019-06-29 02:27:21 +00:00
|
|
|
#ifdef DEBUG
|
2017-06-27 16:52:32 +00:00
|
|
|
if (XFS_TEST_ERROR(false, log->l_mp, XFS_ERRTAG_LOG_BAD_CRC)) {
|
2017-04-21 18:24:40 +00:00
|
|
|
iclog->ic_header.h_crc &= cpu_to_le32(0xAAAAAAAA);
|
2019-06-29 02:27:21 +00:00
|
|
|
iclog->ic_fail_crc = true;
|
2016-01-04 20:41:16 +00:00
|
|
|
xfs_warn(log->l_mp,
|
|
|
|
"Intentionally corrupted log record at LSN 0x%llx. Shutdown imminent.",
|
|
|
|
be64_to_cpu(iclog->ic_header.h_lsn));
|
|
|
|
}
|
2019-06-29 02:27:21 +00:00
|
|
|
#endif
|
2019-06-29 02:27:24 +00:00
|
|
|
xlog_verify_iclog(log, iclog, count);
|
xfs: journal IO cache flush reductions
Currently every journal IO is issued as REQ_PREFLUSH | REQ_FUA to
guarantee the ordering requirements the journal has w.r.t. metadata
writeback. THe two ordering constraints are:
1. we cannot overwrite metadata in the journal until we guarantee
that the dirty metadata has been written back in place and is
stable.
2. we cannot write back dirty metadata until it has been written to
the journal and guaranteed to be stable (and hence recoverable) in
the journal.
The ordering guarantees of #1 are provided by REQ_PREFLUSH. This
causes the journal IO to issue a cache flush and wait for it to
complete before issuing the write IO to the journal. Hence all
completed metadata IO is guaranteed to be stable before the journal
overwrites the old metadata.
The ordering guarantees of #2 are provided by the REQ_FUA, which
ensures the journal writes do not complete until they are on stable
storage. Hence by the time the last journal IO in a checkpoint
completes, we know that the entire checkpoint is on stable storage
and we can unpin the dirty metadata and allow it to be written back.
This is the mechanism by which ordering was first implemented in XFS
way back in 2002 by commit 95d97c36e5155075ba2eb22b17562cfcc53fcf96
("Add support for drive write cache flushing") in the xfs-archive
tree.
A lot has changed since then, most notably we now use delayed
logging to checkpoint the filesystem to the journal rather than
write each individual transaction to the journal. Cache flushes on
journal IO are necessary when individual transactions are wholly
contained within a single iclog. However, CIL checkpoints are single
transactions that typically span hundreds to thousands of individual
journal writes, and so the requirements for device cache flushing
have changed.
That is, the ordering rules I state above apply to ordering of
atomic transactions recorded in the journal, not to the journal IO
itself. Hence we need to ensure metadata is stable before we start
writing a new transaction to the journal (guarantee #1), and we need
to ensure the entire transaction is stable in the journal before we
start metadata writeback (guarantee #2).
Hence we only need a REQ_PREFLUSH on the journal IO that starts a
new journal transaction to provide #1, and it is not on any other
journal IO done within the context of that journal transaction.
The CIL checkpoint already issues a cache flush before it starts
writing to the log, so we no longer need the iclog IO to issue a
REQ_REFLUSH for us. Hence if XLOG_START_TRANS is passed
to xlog_write(), we no longer need to mark the first iclog in
the log write with REQ_PREFLUSH for this case. As an added bonus,
this ordering mechanism works for both internal and external logs,
meaning we can remove the explicit data device cache flushes from
the iclog write code when using external logs.
Given the new ordering semantics of commit records for the CIL, we
need iclogs containing commit records to issue a REQ_PREFLUSH. We
also require unmount records to do this. Hence for both
XLOG_COMMIT_TRANS and XLOG_UNMOUNT_TRANS xlog_write() calls we need
to mark the first iclog being written with REQ_PREFLUSH.
For both commit records and unmount records, we also want them
immediately on stable storage, so we want to also mark the iclogs
that contain these records to be marked REQ_FUA. That means if a
record is split across multiple iclogs, they are all marked REQ_FUA
and not just the last one so that when the transaction is completed
all the parts of the record are on stable storage.
And for external logs, unmount records need a pre-write data device
cache flush similar to the CIL checkpoint cache pre-flush as the
internal iclog write code does not do this implicitly anymore.
As an optimisation, when the commit record lands in the same iclog
as the journal transaction starts, we don't need to wait for
anything and can simply use REQ_FUA to provide guarantee #2. This
means that for fsync() heavy workloads, the cache flush behaviour is
completely unchanged and there is no degradation in performance as a
result of optimise the multi-IO transaction case.
The most notable sign that there is less IO latency on my test
machine (nvme SSDs) is that the "noiclogs" rate has dropped
substantially. This metric indicates that the CIL push is blocking
in xlog_get_iclog_space() waiting for iclog IO completion to occur.
With 8 iclogs of 256kB, the rate is appoximately 1 noiclog event to
every 4 iclog writes. IOWs, every 4th call to xlog_get_iclog_space()
is blocking waiting for log IO. With the changes in this patch, this
drops to 1 noiclog event for every 100 iclog writes. Hence it is
clear that log IO is completing much faster than it was previously,
but it is also clear that for large iclog sizes, this isn't the
performance limiting factor on this hardware.
With smaller iclogs (32kB), however, there is a substantial
difference. With the cache flush modifications, the journal is now
running at over 4000 write IOPS, and the journal throughput is
largely identical to the 256kB iclogs and the noiclog event rate
stays low at about 1:50 iclog writes. The existing code tops out at
about 2500 IOPS as the number of cache flushes dominate performance
and latency. The noiclog event rate is about 1:4, and the
performance variance is quite large as the journal throughput can
fall to less than half the peak sustained rate when the cache flush
rate prevents metadata writeback from keeping up and the log runs
out of space and throttles reservations.
As a result:
logbsize fsmark create rate rm -rf
before 32kb 152851+/-5.3e+04 5m28s
patched 32kb 221533+/-1.1e+04 5m24s
before 256kb 220239+/-6.2e+03 4m58s
patched 256kb 228286+/-9.2e+03 5m06s
The rm -rf times are included because I ran them, but the
differences are largely noise. This workload is largely metadata
read IO latency bound and the changes to the journal cache flushing
doesn't really make any noticable difference to behaviour apart from
a reduction in noiclog events from background CIL pushing.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Chandan Babu R <chandanrlinux@gmail.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-06-18 15:21:51 +00:00
|
|
|
xlog_write_iclog(log, iclog, bno, count);
|
2019-06-29 02:27:22 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2006-03-28 22:55:14 +00:00
|
|
|
* Deallocate a log structure
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-11-23 05:28:09 +00:00
|
|
|
STATIC void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_dealloc_log(
|
|
|
|
struct xlog *log)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
xlog_in_core_t *iclog, *next_iclog;
|
|
|
|
int i;
|
|
|
|
|
2011-04-21 09:34:27 +00:00
|
|
|
/*
|
xfs: unmount does not wait for shutdown during unmount
And interesting situation can occur if a log IO error occurs during
the unmount of a filesystem. The cases reported have the same
signature - the update of the superblock counters fails due to a log
write IO error:
XFS (dm-16): xfs_do_force_shutdown(0x2) called from line 1170 of file fs/xfs/xfs_log.c. Return address = 0xffffffffa08a44a1
XFS (dm-16): Log I/O Error Detected. Shutting down filesystem
XFS (dm-16): Unable to update superblock counters. Freespace may not be correct on next mount.
XFS (dm-16): xfs_log_force: error 5 returned.
XFS (¿-¿¿¿): Please umount the filesystem and rectify the problem(s)
It can be seen that the last line of output contains a corrupt
device name - this is because the log and xfs_mount structures have
already been freed by the time this message is printed. A kernel
oops closely follows.
The issue is that the shutdown is occurring in a separate IO
completion thread to the unmount. Once the shutdown processing has
started and all the iclogs are marked with XLOG_STATE_IOERROR, the
log shutdown code wakes anyone waiting on a log force so they can
process the shutdown error. This wakes up the unmount code that
is doing a synchronous transaction to update the superblock
counters.
The unmount path now sees all the iclogs are marked with
XLOG_STATE_IOERROR and so never waits on them again, knowing that if
it does, there will not be a wakeup trigger for it and we will hang
the unmount if we do. Hence the unmount runs through all the
remaining code and frees all the filesystem structures while the
xlog_iodone() is still processing the shutdown. When the log
shutdown processing completes, xfs_do_force_shutdown() emits the
"Please umount the filesystem and rectify the problem(s)" message,
and xlog_iodone() then aborts all the objects attached to the iclog.
An iclog that has already been freed....
The real issue here is that there is no serialisation point between
the log IO and the unmount. We have serialisations points for log
writes, log forces, reservations, etc, but we don't actually have
any code that wakes for log IO to fully complete. We do that for all
other types of object, so why not iclogbufs?
Well, it turns out that we can easily do this. We've got xfs_buf
handles, and that's what everyone else uses for IO serialisation.
i.e. bp->b_sema. So, lets hold iclogbufs locked over IO, and only
release the lock in xlog_iodone() when we are finished with the
buffer. That way before we tear down the iclog, we can lock and
unlock the buffer to ensure IO completion has finished completely
before we tear it down.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Tested-by: Mike Snitzer <snitzer@redhat.com>
Tested-by: Bob Mastors <bob.mastors@solidfire.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2014-04-16 22:15:26 +00:00
|
|
|
* Cycle all the iclogbuf locks to make sure all log IO completion
|
|
|
|
* is done before we tear down these buffers.
|
|
|
|
*/
|
|
|
|
iclog = log->l_iclog;
|
|
|
|
for (i = 0; i < log->l_iclog_bufs; i++) {
|
2019-06-29 02:27:25 +00:00
|
|
|
down(&iclog->ic_sema);
|
|
|
|
up(&iclog->ic_sema);
|
xfs: unmount does not wait for shutdown during unmount
And interesting situation can occur if a log IO error occurs during
the unmount of a filesystem. The cases reported have the same
signature - the update of the superblock counters fails due to a log
write IO error:
XFS (dm-16): xfs_do_force_shutdown(0x2) called from line 1170 of file fs/xfs/xfs_log.c. Return address = 0xffffffffa08a44a1
XFS (dm-16): Log I/O Error Detected. Shutting down filesystem
XFS (dm-16): Unable to update superblock counters. Freespace may not be correct on next mount.
XFS (dm-16): xfs_log_force: error 5 returned.
XFS (¿-¿¿¿): Please umount the filesystem and rectify the problem(s)
It can be seen that the last line of output contains a corrupt
device name - this is because the log and xfs_mount structures have
already been freed by the time this message is printed. A kernel
oops closely follows.
The issue is that the shutdown is occurring in a separate IO
completion thread to the unmount. Once the shutdown processing has
started and all the iclogs are marked with XLOG_STATE_IOERROR, the
log shutdown code wakes anyone waiting on a log force so they can
process the shutdown error. This wakes up the unmount code that
is doing a synchronous transaction to update the superblock
counters.
The unmount path now sees all the iclogs are marked with
XLOG_STATE_IOERROR and so never waits on them again, knowing that if
it does, there will not be a wakeup trigger for it and we will hang
the unmount if we do. Hence the unmount runs through all the
remaining code and frees all the filesystem structures while the
xlog_iodone() is still processing the shutdown. When the log
shutdown processing completes, xfs_do_force_shutdown() emits the
"Please umount the filesystem and rectify the problem(s)" message,
and xlog_iodone() then aborts all the objects attached to the iclog.
An iclog that has already been freed....
The real issue here is that there is no serialisation point between
the log IO and the unmount. We have serialisations points for log
writes, log forces, reservations, etc, but we don't actually have
any code that wakes for log IO to fully complete. We do that for all
other types of object, so why not iclogbufs?
Well, it turns out that we can easily do this. We've got xfs_buf
handles, and that's what everyone else uses for IO serialisation.
i.e. bp->b_sema. So, lets hold iclogbufs locked over IO, and only
release the lock in xlog_iodone() when we are finished with the
buffer. That way before we tear down the iclog, we can lock and
unlock the buffer to ensure IO completion has finished completely
before we tear it down.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Tested-by: Mike Snitzer <snitzer@redhat.com>
Tested-by: Bob Mastors <bob.mastors@solidfire.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2014-04-16 22:15:26 +00:00
|
|
|
iclog = iclog->ic_next;
|
|
|
|
}
|
|
|
|
|
xfs: prevent a UAF when log IO errors race with unmount
KASAN reported the following use after free bug when running
generic/475:
XFS (dm-0): Mounting V5 Filesystem
XFS (dm-0): Starting recovery (logdev: internal)
XFS (dm-0): Ending recovery (logdev: internal)
Buffer I/O error on dev dm-0, logical block 20639616, async page read
Buffer I/O error on dev dm-0, logical block 20639617, async page read
XFS (dm-0): log I/O error -5
XFS (dm-0): Filesystem has been shut down due to log error (0x2).
XFS (dm-0): Unmounting Filesystem
XFS (dm-0): Please unmount the filesystem and rectify the problem(s).
==================================================================
BUG: KASAN: use-after-free in do_raw_spin_lock+0x246/0x270
Read of size 4 at addr ffff888109dd84c4 by task 3:1H/136
CPU: 3 PID: 136 Comm: 3:1H Not tainted 5.19.0-rc4-xfsx #rc4 8e53ab5ad0fddeb31cee5e7063ff9c361915a9c4
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.15.0-1 04/01/2014
Workqueue: xfs-log/dm-0 xlog_ioend_work [xfs]
Call Trace:
<TASK>
dump_stack_lvl+0x34/0x44
print_report.cold+0x2b8/0x661
? do_raw_spin_lock+0x246/0x270
kasan_report+0xab/0x120
? do_raw_spin_lock+0x246/0x270
do_raw_spin_lock+0x246/0x270
? rwlock_bug.part.0+0x90/0x90
xlog_force_shutdown+0xf6/0x370 [xfs 4ad76ae0d6add7e8183a553e624c31e9ed567318]
xlog_ioend_work+0x100/0x190 [xfs 4ad76ae0d6add7e8183a553e624c31e9ed567318]
process_one_work+0x672/0x1040
worker_thread+0x59b/0xec0
? __kthread_parkme+0xc6/0x1f0
? process_one_work+0x1040/0x1040
? process_one_work+0x1040/0x1040
kthread+0x29e/0x340
? kthread_complete_and_exit+0x20/0x20
ret_from_fork+0x1f/0x30
</TASK>
Allocated by task 154099:
kasan_save_stack+0x1e/0x40
__kasan_kmalloc+0x81/0xa0
kmem_alloc+0x8d/0x2e0 [xfs]
xlog_cil_init+0x1f/0x540 [xfs]
xlog_alloc_log+0xd1e/0x1260 [xfs]
xfs_log_mount+0xba/0x640 [xfs]
xfs_mountfs+0xf2b/0x1d00 [xfs]
xfs_fs_fill_super+0x10af/0x1910 [xfs]
get_tree_bdev+0x383/0x670
vfs_get_tree+0x7d/0x240
path_mount+0xdb7/0x1890
__x64_sys_mount+0x1fa/0x270
do_syscall_64+0x2b/0x80
entry_SYSCALL_64_after_hwframe+0x46/0xb0
Freed by task 154151:
kasan_save_stack+0x1e/0x40
kasan_set_track+0x21/0x30
kasan_set_free_info+0x20/0x30
____kasan_slab_free+0x110/0x190
slab_free_freelist_hook+0xab/0x180
kfree+0xbc/0x310
xlog_dealloc_log+0x1b/0x2b0 [xfs]
xfs_unmountfs+0x119/0x200 [xfs]
xfs_fs_put_super+0x6e/0x2e0 [xfs]
generic_shutdown_super+0x12b/0x3a0
kill_block_super+0x95/0xd0
deactivate_locked_super+0x80/0x130
cleanup_mnt+0x329/0x4d0
task_work_run+0xc5/0x160
exit_to_user_mode_prepare+0xd4/0xe0
syscall_exit_to_user_mode+0x1d/0x40
entry_SYSCALL_64_after_hwframe+0x46/0xb0
This appears to be a race between the unmount process, which frees the
CIL and waits for in-flight iclog IO; and the iclog IO completion. When
generic/475 runs, it starts fsstress in the background, waits a few
seconds, and substitutes a dm-error device to simulate a disk falling
out of a machine. If the fsstress encounters EIO on a pure data write,
it will exit but the filesystem will still be online.
The next thing the test does is unmount the filesystem, which tries to
clean the log, free the CIL, and wait for iclog IO completion. If an
iclog was being written when the dm-error switch occurred, it can race
with log unmounting as follows:
Thread 1 Thread 2
xfs_log_unmount
xfs_log_clean
xfs_log_quiesce
xlog_ioend_work
<observe error>
xlog_force_shutdown
test_and_set_bit(XLOG_IOERROR)
xfs_log_force
<log is shut down, nop>
xfs_log_umount_write
<log is shut down, nop>
xlog_dealloc_log
xlog_cil_destroy
<wait for iclogs>
spin_lock(&log->l_cilp->xc_push_lock)
<KABOOM>
Therefore, free the CIL after waiting for the iclogs to complete. I
/think/ this race has existed for quite a few years now, though I don't
remember the ~2014 era logging code well enough to know if it was a real
threat then or if the actual race was exposed only more recently.
Fixes: ac983517ec59 ("xfs: don't sleep in xlog_cil_force_lsn on shutdown")
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
2022-07-01 16:08:33 +00:00
|
|
|
/*
|
|
|
|
* Destroy the CIL after waiting for iclog IO completion because an
|
|
|
|
* iclog EIO error will try to shut down the log, which accesses the
|
|
|
|
* CIL to wake up the waiters.
|
|
|
|
*/
|
|
|
|
xlog_cil_destroy(log);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog = log->l_iclog;
|
xfs: unmount does not wait for shutdown during unmount
And interesting situation can occur if a log IO error occurs during
the unmount of a filesystem. The cases reported have the same
signature - the update of the superblock counters fails due to a log
write IO error:
XFS (dm-16): xfs_do_force_shutdown(0x2) called from line 1170 of file fs/xfs/xfs_log.c. Return address = 0xffffffffa08a44a1
XFS (dm-16): Log I/O Error Detected. Shutting down filesystem
XFS (dm-16): Unable to update superblock counters. Freespace may not be correct on next mount.
XFS (dm-16): xfs_log_force: error 5 returned.
XFS (¿-¿¿¿): Please umount the filesystem and rectify the problem(s)
It can be seen that the last line of output contains a corrupt
device name - this is because the log and xfs_mount structures have
already been freed by the time this message is printed. A kernel
oops closely follows.
The issue is that the shutdown is occurring in a separate IO
completion thread to the unmount. Once the shutdown processing has
started and all the iclogs are marked with XLOG_STATE_IOERROR, the
log shutdown code wakes anyone waiting on a log force so they can
process the shutdown error. This wakes up the unmount code that
is doing a synchronous transaction to update the superblock
counters.
The unmount path now sees all the iclogs are marked with
XLOG_STATE_IOERROR and so never waits on them again, knowing that if
it does, there will not be a wakeup trigger for it and we will hang
the unmount if we do. Hence the unmount runs through all the
remaining code and frees all the filesystem structures while the
xlog_iodone() is still processing the shutdown. When the log
shutdown processing completes, xfs_do_force_shutdown() emits the
"Please umount the filesystem and rectify the problem(s)" message,
and xlog_iodone() then aborts all the objects attached to the iclog.
An iclog that has already been freed....
The real issue here is that there is no serialisation point between
the log IO and the unmount. We have serialisations points for log
writes, log forces, reservations, etc, but we don't actually have
any code that wakes for log IO to fully complete. We do that for all
other types of object, so why not iclogbufs?
Well, it turns out that we can easily do this. We've got xfs_buf
handles, and that's what everyone else uses for IO serialisation.
i.e. bp->b_sema. So, lets hold iclogbufs locked over IO, and only
release the lock in xlog_iodone() when we are finished with the
buffer. That way before we tear down the iclog, we can lock and
unlock the buffer to ensure IO completion has finished completely
before we tear it down.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Tested-by: Mike Snitzer <snitzer@redhat.com>
Tested-by: Bob Mastors <bob.mastors@solidfire.com>
Reviewed-by: Brian Foster <bfoster@redhat.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2014-04-16 22:15:26 +00:00
|
|
|
for (i = 0; i < log->l_iclog_bufs; i++) {
|
2005-04-16 22:20:36 +00:00
|
|
|
next_iclog = iclog->ic_next;
|
2019-06-29 02:27:25 +00:00
|
|
|
kmem_free(iclog->ic_data);
|
2008-05-19 06:31:57 +00:00
|
|
|
kmem_free(iclog);
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog = next_iclog;
|
|
|
|
}
|
|
|
|
|
|
|
|
log->l_mp->m_log = NULL;
|
2019-06-29 02:27:25 +00:00
|
|
|
destroy_workqueue(log->l_ioend_workqueue);
|
2008-05-19 06:31:57 +00:00
|
|
|
kmem_free(log);
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Update counters atomically now that memcpy is done.
|
|
|
|
*/
|
|
|
|
static inline void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_state_finish_copy(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
int record_cnt,
|
|
|
|
int copy_bytes)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-10-14 17:36:41 +00:00
|
|
|
lockdep_assert_held(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-02-13 23:03:29 +00:00
|
|
|
be32_add_cpu(&iclog->ic_header.h_num_logops, record_cnt);
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog->ic_offset += copy_bytes;
|
2019-10-14 17:36:41 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-02 06:42:05 +00:00
|
|
|
/*
|
|
|
|
* print out info relating to regions written which consume
|
|
|
|
* the reservation
|
|
|
|
*/
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
void
|
|
|
|
xlog_print_tic_res(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
struct xlog_ticket *ticket)
|
2005-09-02 06:42:05 +00:00
|
|
|
{
|
2017-06-15 04:29:48 +00:00
|
|
|
xfs_warn(mp, "ticket reservation summary:");
|
xfs: log ticket region debug is largely useless
xlog_tic_add_region() is used to trace the regions being added to a
log ticket to provide information in the situation where a ticket
reservation overrun occurs. The information gathered is stored int
the ticket, and dumped if xlog_print_tic_res() is called.
For a front end struct xfs_trans overrun, the ticket only contains
reservation tracking information - the ticket is never handed to the
log so has no regions attached to it. The overrun debug information in this
case comes from xlog_print_trans(), which walks the items attached
to the transaction and dumps their attached formatted log vectors
directly. It also dumps the ticket state, but that only contains
reservation accounting and nothing else. Hence xlog_print_tic_res()
never dumps region or overrun information from this path.
xlog_tic_add_region() is actually called from xlog_write(), which
means it is being used to track the regions seen in a
CIL checkpoint log vector chain. In looking at CIL behaviour
recently, I've seen 32MB checkpoints regularly exceed 250,000
regions in the LV chain. The log ticket debug code can track *15*
regions. IOWs, if there is a ticket overrun in the CIL code, the
ticket region tracking code is going to be completely useless for
determining what went wrong. The only thing it can tell us is how
much of an overrun occurred, and we really don't need extra debug
information in the log ticket to tell us that.
Indeed, the main place we call xlog_tic_add_region() is also adding
up the number of regions and the space used so that xlog_write()
knows how much will be written to the log. This is exactly the same
information that log ticket is storing once we take away the useless
region tracking array. Hence xlog_tic_add_region() is not useful,
but can be called 250,000 times a CIL push...
Just strip all that debug "information" out of the of the log ticket
and only have it report reservation space information when an
overrun occurs. This also reduces the size of a log ticket down by
about 150 bytes...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Chandan Babu R <chandan.babu@oracle.com>
Signed-off-by: Dave Chinner <david@fromorbit.com>
2022-04-21 00:35:09 +00:00
|
|
|
xfs_warn(mp, " unit res = %d bytes", ticket->t_unit_res);
|
|
|
|
xfs_warn(mp, " current res = %d bytes", ticket->t_curr_res);
|
|
|
|
xfs_warn(mp, " original count = %d", ticket->t_ocnt);
|
|
|
|
xfs_warn(mp, " remaining count = %d", ticket->t_cnt);
|
2005-09-02 06:42:05 +00:00
|
|
|
}
|
|
|
|
|
2017-06-15 04:29:50 +00:00
|
|
|
/*
|
|
|
|
* Print a summary of the transaction.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xlog_print_trans(
|
2018-05-09 14:49:37 +00:00
|
|
|
struct xfs_trans *tp)
|
2017-06-15 04:29:50 +00:00
|
|
|
{
|
2018-05-09 14:49:37 +00:00
|
|
|
struct xfs_mount *mp = tp->t_mountp;
|
|
|
|
struct xfs_log_item *lip;
|
2017-06-15 04:29:50 +00:00
|
|
|
|
|
|
|
/* dump core transaction and ticket info */
|
|
|
|
xfs_warn(mp, "transaction summary:");
|
2018-01-08 18:41:35 +00:00
|
|
|
xfs_warn(mp, " log res = %d", tp->t_log_res);
|
|
|
|
xfs_warn(mp, " log count = %d", tp->t_log_count);
|
|
|
|
xfs_warn(mp, " flags = 0x%x", tp->t_flags);
|
2017-06-15 04:29:50 +00:00
|
|
|
|
|
|
|
xlog_print_tic_res(mp, tp->t_ticket);
|
|
|
|
|
|
|
|
/* dump each log item */
|
2018-05-09 14:49:37 +00:00
|
|
|
list_for_each_entry(lip, &tp->t_items, li_trans) {
|
2017-06-15 04:29:50 +00:00
|
|
|
struct xfs_log_vec *lv = lip->li_lv;
|
|
|
|
struct xfs_log_iovec *vec;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
xfs_warn(mp, "log item: ");
|
|
|
|
xfs_warn(mp, " type = 0x%x", lip->li_type);
|
2018-05-09 14:47:34 +00:00
|
|
|
xfs_warn(mp, " flags = 0x%lx", lip->li_flags);
|
2017-06-15 04:29:50 +00:00
|
|
|
if (!lv)
|
|
|
|
continue;
|
|
|
|
xfs_warn(mp, " niovecs = %d", lv->lv_niovecs);
|
|
|
|
xfs_warn(mp, " size = %d", lv->lv_size);
|
|
|
|
xfs_warn(mp, " bytes = %d", lv->lv_bytes);
|
|
|
|
xfs_warn(mp, " buf len = %d", lv->lv_buf_len);
|
|
|
|
|
|
|
|
/* dump each iovec for the log item */
|
|
|
|
vec = lv->lv_iovecp;
|
|
|
|
for (i = 0; i < lv->lv_niovecs; i++) {
|
|
|
|
int dumplen = min(vec->i_len, 32);
|
|
|
|
|
|
|
|
xfs_warn(mp, " iovec[%d]", i);
|
|
|
|
xfs_warn(mp, " type = 0x%x", vec->i_type);
|
|
|
|
xfs_warn(mp, " len = %d", vec->i_len);
|
|
|
|
xfs_warn(mp, " first %d bytes of iovec[%d]:", dumplen, i);
|
2017-06-26 15:54:16 +00:00
|
|
|
xfs_hex_dump(vec->i_addr, dumplen);
|
2017-06-15 04:29:50 +00:00
|
|
|
|
|
|
|
vec++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-21 00:36:05 +00:00
|
|
|
static inline void
|
|
|
|
xlog_write_iovec(
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
uint32_t *log_offset,
|
|
|
|
void *data,
|
|
|
|
uint32_t write_len,
|
|
|
|
int *bytes_left,
|
|
|
|
uint32_t *record_cnt,
|
|
|
|
uint32_t *data_cnt)
|
|
|
|
{
|
2022-04-21 00:36:27 +00:00
|
|
|
ASSERT(*log_offset < iclog->ic_log->l_iclog_size);
|
2022-04-21 00:36:05 +00:00
|
|
|
ASSERT(*log_offset % sizeof(int32_t) == 0);
|
|
|
|
ASSERT(write_len % sizeof(int32_t) == 0);
|
|
|
|
|
|
|
|
memcpy(iclog->ic_datap + *log_offset, data, write_len);
|
|
|
|
*log_offset += write_len;
|
|
|
|
*bytes_left -= write_len;
|
|
|
|
(*record_cnt)++;
|
|
|
|
*data_cnt += write_len;
|
|
|
|
}
|
|
|
|
|
2010-03-23 00:29:44 +00:00
|
|
|
/*
|
2022-04-21 00:36:05 +00:00
|
|
|
* Write log vectors into a single iclog which is guaranteed by the caller
|
|
|
|
* to have enough space to write the entire log vector into.
|
2010-03-23 00:29:44 +00:00
|
|
|
*/
|
2022-04-21 00:36:05 +00:00
|
|
|
static void
|
|
|
|
xlog_write_full(
|
|
|
|
struct xfs_log_vec *lv,
|
2010-03-23 00:29:44 +00:00
|
|
|
struct xlog_ticket *ticket,
|
2022-04-21 00:36:05 +00:00
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
uint32_t *log_offset,
|
|
|
|
uint32_t *len,
|
|
|
|
uint32_t *record_cnt,
|
|
|
|
uint32_t *data_cnt)
|
2010-03-23 00:29:44 +00:00
|
|
|
{
|
2022-04-21 00:36:05 +00:00
|
|
|
int index;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ASSERT(*log_offset + *len <= iclog->ic_size ||
|
|
|
|
iclog->ic_state == XLOG_STATE_WANT_SYNC);
|
2021-06-18 15:21:50 +00:00
|
|
|
|
2022-04-21 00:36:05 +00:00
|
|
|
/*
|
|
|
|
* Ordered log vectors have no regions to write so this
|
|
|
|
* loop will naturally skip them.
|
|
|
|
*/
|
|
|
|
for (index = 0; index < lv->lv_niovecs; index++) {
|
|
|
|
struct xfs_log_iovec *reg = &lv->lv_iovecp[index];
|
|
|
|
struct xlog_op_header *ophdr = reg->i_addr;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:05 +00:00
|
|
|
ophdr->oh_tid = cpu_to_be32(ticket->t_tid);
|
|
|
|
xlog_write_iovec(iclog, log_offset, reg->i_addr,
|
|
|
|
reg->i_len, len, record_cnt, data_cnt);
|
2010-03-23 00:29:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
static int
|
|
|
|
xlog_write_get_more_iclog_space(
|
2010-03-23 00:29:44 +00:00
|
|
|
struct xlog_ticket *ticket,
|
2022-04-21 00:36:15 +00:00
|
|
|
struct xlog_in_core **iclogp,
|
|
|
|
uint32_t *log_offset,
|
|
|
|
uint32_t len,
|
|
|
|
uint32_t *record_cnt,
|
2022-04-21 00:36:37 +00:00
|
|
|
uint32_t *data_cnt)
|
2010-03-23 00:29:44 +00:00
|
|
|
{
|
2022-04-21 00:36:15 +00:00
|
|
|
struct xlog_in_core *iclog = *iclogp;
|
|
|
|
struct xlog *log = iclog->ic_log;
|
|
|
|
int error;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
|
|
|
ASSERT(iclog->ic_state == XLOG_STATE_WANT_SYNC);
|
|
|
|
xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt);
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
error = xlog_state_release_iclog(log, iclog, ticket);
|
2022-04-21 00:36:15 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:37 +00:00
|
|
|
error = xlog_state_get_iclog_space(log, len, &iclog, ticket,
|
|
|
|
log_offset);
|
2022-04-21 00:36:15 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
*record_cnt = 0;
|
|
|
|
*data_cnt = 0;
|
|
|
|
*iclogp = iclog;
|
|
|
|
return 0;
|
2010-03-23 00:29:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-04-21 00:36:15 +00:00
|
|
|
* Write log vectors into a single iclog which is smaller than the current chain
|
|
|
|
* length. We write until we cannot fit a full record into the remaining space
|
|
|
|
* and then stop. We return the log vector that is to be written that cannot
|
|
|
|
* wholly fit in the iclog.
|
2010-03-23 00:29:44 +00:00
|
|
|
*/
|
|
|
|
static int
|
2022-04-21 00:36:15 +00:00
|
|
|
xlog_write_partial(
|
|
|
|
struct xfs_log_vec *lv,
|
2010-03-23 00:29:44 +00:00
|
|
|
struct xlog_ticket *ticket,
|
2022-04-21 00:36:15 +00:00
|
|
|
struct xlog_in_core **iclogp,
|
|
|
|
uint32_t *log_offset,
|
|
|
|
uint32_t *len,
|
|
|
|
uint32_t *record_cnt,
|
2022-04-21 00:36:37 +00:00
|
|
|
uint32_t *data_cnt)
|
2022-04-21 00:36:15 +00:00
|
|
|
{
|
|
|
|
struct xlog_in_core *iclog = *iclogp;
|
|
|
|
struct xlog_op_header *ophdr;
|
|
|
|
int index = 0;
|
|
|
|
uint32_t rlen;
|
|
|
|
int error;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/* walk the logvec, copying until we run out of space in the iclog */
|
|
|
|
for (index = 0; index < lv->lv_niovecs; index++) {
|
|
|
|
struct xfs_log_iovec *reg = &lv->lv_iovecp[index];
|
|
|
|
uint32_t reg_offset = 0;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/*
|
|
|
|
* The first region of a continuation must have a non-zero
|
|
|
|
* length otherwise log recovery will just skip over it and
|
|
|
|
* start recovering from the next opheader it finds. Because we
|
|
|
|
* mark the next opheader as a continuation, recovery will then
|
|
|
|
* incorrectly add the continuation to the previous region and
|
|
|
|
* that breaks stuff.
|
|
|
|
*
|
|
|
|
* Hence if there isn't space for region data after the
|
|
|
|
* opheader, then we need to start afresh with a new iclog.
|
|
|
|
*/
|
|
|
|
if (iclog->ic_size - *log_offset <=
|
|
|
|
sizeof(struct xlog_op_header)) {
|
|
|
|
error = xlog_write_get_more_iclog_space(ticket,
|
|
|
|
&iclog, log_offset, *len, record_cnt,
|
2022-04-21 00:36:37 +00:00
|
|
|
data_cnt);
|
2022-04-21 00:36:15 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
}
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ophdr = reg->i_addr;
|
|
|
|
rlen = min_t(uint32_t, reg->i_len, iclog->ic_size - *log_offset);
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ophdr->oh_tid = cpu_to_be32(ticket->t_tid);
|
|
|
|
ophdr->oh_len = cpu_to_be32(rlen - sizeof(struct xlog_op_header));
|
|
|
|
if (rlen != reg->i_len)
|
|
|
|
ophdr->oh_flags |= XLOG_CONTINUE_TRANS;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
xlog_write_iovec(iclog, log_offset, reg->i_addr,
|
|
|
|
rlen, len, record_cnt, data_cnt);
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/* If we wrote the whole region, move to the next. */
|
|
|
|
if (rlen == reg->i_len)
|
|
|
|
continue;
|
2019-10-14 17:36:41 +00:00
|
|
|
|
2010-03-23 00:29:44 +00:00
|
|
|
/*
|
2022-04-21 00:36:15 +00:00
|
|
|
* We now have a partially written iovec, but it can span
|
|
|
|
* multiple iclogs so we loop here. First we release the iclog
|
|
|
|
* we currently have, then we get a new iclog and add a new
|
|
|
|
* opheader. Then we continue copying from where we were until
|
|
|
|
* we either complete the iovec or fill the iclog. If we
|
|
|
|
* complete the iovec, then we increment the index and go right
|
|
|
|
* back to the top of the outer loop. if we fill the iclog, we
|
|
|
|
* run the inner loop again.
|
|
|
|
*
|
|
|
|
* This is complicated by the tail of a region using all the
|
|
|
|
* space in an iclog and hence requiring us to release the iclog
|
|
|
|
* and get a new one before returning to the outer loop. We must
|
|
|
|
* always guarantee that we exit this inner loop with at least
|
|
|
|
* space for log transaction opheaders left in the current
|
|
|
|
* iclog, hence we cannot just terminate the loop at the end
|
|
|
|
* of the of the continuation. So we loop while there is no
|
|
|
|
* space left in the current iclog, and check for the end of the
|
|
|
|
* continuation after getting a new iclog.
|
2010-03-23 00:29:44 +00:00
|
|
|
*/
|
2022-04-21 00:36:15 +00:00
|
|
|
do {
|
|
|
|
/*
|
|
|
|
* Ensure we include the continuation opheader in the
|
|
|
|
* space we need in the new iclog by adding that size
|
|
|
|
* to the length we require. This continuation opheader
|
|
|
|
* needs to be accounted to the ticket as the space it
|
|
|
|
* consumes hasn't been accounted to the lv we are
|
|
|
|
* writing.
|
|
|
|
*/
|
|
|
|
error = xlog_write_get_more_iclog_space(ticket,
|
|
|
|
&iclog, log_offset,
|
|
|
|
*len + sizeof(struct xlog_op_header),
|
2022-04-21 00:36:37 +00:00
|
|
|
record_cnt, data_cnt);
|
2022-04-21 00:36:15 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ophdr = iclog->ic_datap + *log_offset;
|
|
|
|
ophdr->oh_tid = cpu_to_be32(ticket->t_tid);
|
|
|
|
ophdr->oh_clientid = XFS_TRANSACTION;
|
|
|
|
ophdr->oh_res2 = 0;
|
|
|
|
ophdr->oh_flags = XLOG_WAS_CONT_TRANS;
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ticket->t_curr_res -= sizeof(struct xlog_op_header);
|
|
|
|
*log_offset += sizeof(struct xlog_op_header);
|
|
|
|
*data_cnt += sizeof(struct xlog_op_header);
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/*
|
|
|
|
* If rlen fits in the iclog, then end the region
|
|
|
|
* continuation. Otherwise we're going around again.
|
|
|
|
*/
|
|
|
|
reg_offset += rlen;
|
|
|
|
rlen = reg->i_len - reg_offset;
|
|
|
|
if (rlen <= iclog->ic_size - *log_offset)
|
|
|
|
ophdr->oh_flags |= XLOG_END_TRANS;
|
|
|
|
else
|
|
|
|
ophdr->oh_flags |= XLOG_CONTINUE_TRANS;
|
2019-10-14 17:36:41 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
rlen = min_t(uint32_t, rlen, iclog->ic_size - *log_offset);
|
|
|
|
ophdr->oh_len = cpu_to_be32(rlen);
|
2019-10-14 17:36:41 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
xlog_write_iovec(iclog, log_offset,
|
|
|
|
reg->i_addr + reg_offset,
|
|
|
|
rlen, len, record_cnt, data_cnt);
|
|
|
|
|
|
|
|
} while (ophdr->oh_flags & XLOG_CONTINUE_TRANS);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* No more iovecs remain in this logvec so return the next log vec to
|
|
|
|
* the caller so it can go back to fast path copying.
|
|
|
|
*/
|
|
|
|
*iclogp = iclog;
|
|
|
|
return 0;
|
2010-03-23 00:29:44 +00:00
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Write some region out to in-core log
|
|
|
|
*
|
|
|
|
* This will be called when writing externally provided regions or when
|
|
|
|
* writing out a commit record for a given transaction.
|
|
|
|
*
|
|
|
|
* General algorithm:
|
|
|
|
* 1. Find total length of this write. This may include adding to the
|
|
|
|
* lengths passed in.
|
|
|
|
* 2. Check whether we violate the tickets reservation.
|
|
|
|
* 3. While writing to this iclog
|
|
|
|
* A. Reserve as much space in this iclog as can get
|
|
|
|
* B. If this is first write, save away start lsn
|
|
|
|
* C. While writing this region:
|
|
|
|
* 1. If first write of transaction, write start record
|
|
|
|
* 2. Write log operation header (header per region)
|
|
|
|
* 3. Find out if we can fit entire region into this iclog
|
|
|
|
* 4. Potentially, verify destination memcpy ptr
|
|
|
|
* 5. Memcpy (partial) region
|
|
|
|
* 6. If partial copy, release iclog; otherwise, continue
|
|
|
|
* copying more regions into current iclog
|
|
|
|
* 4. Mark want sync bit (in simulation mode)
|
|
|
|
* 5. Release iclog for potential flush to on-disk log.
|
|
|
|
*
|
|
|
|
* ERRORS:
|
|
|
|
* 1. Panic if reservation is overrun. This should never happen since
|
|
|
|
* reservation amounts are generated internal to the filesystem.
|
|
|
|
* NOTES:
|
|
|
|
* 1. Tickets are single threaded data structures.
|
|
|
|
* 2. The XLOG_END_TRANS & XLOG_CONTINUE_TRANS flags are passed down to the
|
|
|
|
* syncing routine. When a single log_write region needs to span
|
|
|
|
* multiple in-core logs, the XLOG_CONTINUE_TRANS bit should be set
|
|
|
|
* on all log operation writes which don't contain the end of the
|
|
|
|
* region. The XLOG_END_TRANS bit is used for the in-core log
|
|
|
|
* operation which contains the end of the continued log_write region.
|
|
|
|
* 3. When xlog_state_get_iclog_space() grabs the rest of the current iclog,
|
|
|
|
* we don't really know exactly how much space will be used. As a result,
|
|
|
|
* we don't update ic_offset until the end when we know exactly how many
|
|
|
|
* bytes have been written out.
|
|
|
|
*/
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
int
|
2010-02-15 23:34:54 +00:00
|
|
|
xlog_write(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log,
|
2021-08-11 01:00:42 +00:00
|
|
|
struct xfs_cil_ctx *ctx,
|
2022-07-07 08:55:59 +00:00
|
|
|
struct list_head *lv_chain,
|
2010-02-15 23:34:54 +00:00
|
|
|
struct xlog_ticket *ticket,
|
2022-04-21 00:35:19 +00:00
|
|
|
uint32_t len)
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-03-23 00:35:45 +00:00
|
|
|
struct xlog_in_core *iclog = NULL;
|
2022-07-07 08:55:59 +00:00
|
|
|
struct xfs_log_vec *lv;
|
2022-04-21 00:36:05 +00:00
|
|
|
uint32_t record_cnt = 0;
|
|
|
|
uint32_t data_cnt = 0;
|
2019-10-14 17:36:41 +00:00
|
|
|
int error = 0;
|
2022-04-21 00:36:15 +00:00
|
|
|
int log_offset;
|
2010-03-23 00:35:45 +00:00
|
|
|
|
2017-06-15 04:29:48 +00:00
|
|
|
if (ticket->t_curr_res < 0) {
|
|
|
|
xfs_alert_tag(log->l_mp, XFS_PTAG_LOGRES,
|
|
|
|
"ctx ticket reservation ran out. Need to up reservation");
|
2010-03-23 00:43:17 +00:00
|
|
|
xlog_print_tic_res(log->l_mp, ticket);
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR);
|
2017-06-15 04:29:48 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
error = xlog_state_get_iclog_space(log, len, &iclog, ticket,
|
2022-04-21 00:36:37 +00:00
|
|
|
&log_offset);
|
2022-04-21 00:36:15 +00:00
|
|
|
if (error)
|
|
|
|
return error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
ASSERT(log_offset <= iclog->ic_size - 1);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/*
|
|
|
|
* If we have a context pointer, pass it the first iclog we are
|
|
|
|
* writing to so it can record state needed for iclog write
|
|
|
|
* ordering.
|
|
|
|
*/
|
|
|
|
if (ctx)
|
|
|
|
xlog_cil_set_ctx_write_state(ctx, iclog);
|
2010-03-23 00:29:44 +00:00
|
|
|
|
2022-07-07 08:55:59 +00:00
|
|
|
list_for_each_entry(lv, lv_chain, lv_list) {
|
2010-03-23 00:35:45 +00:00
|
|
|
/*
|
2022-04-21 00:36:15 +00:00
|
|
|
* If the entire log vec does not fit in the iclog, punt it to
|
|
|
|
* the partial copy loop which can handle this case.
|
2010-03-23 00:35:45 +00:00
|
|
|
*/
|
2022-04-21 00:36:15 +00:00
|
|
|
if (lv->lv_niovecs &&
|
|
|
|
lv->lv_bytes > iclog->ic_size - log_offset) {
|
|
|
|
error = xlog_write_partial(lv, ticket, &iclog,
|
|
|
|
&log_offset, &len, &record_cnt,
|
2022-04-21 00:36:37 +00:00
|
|
|
&data_cnt);
|
2022-04-21 00:36:15 +00:00
|
|
|
if (error) {
|
|
|
|
/*
|
|
|
|
* We have no iclog to release, so just return
|
|
|
|
* the error immediately.
|
|
|
|
*/
|
2010-03-23 00:35:45 +00:00
|
|
|
return error;
|
|
|
|
}
|
2022-04-21 00:36:15 +00:00
|
|
|
} else {
|
|
|
|
xlog_write_full(lv, ticket, iclog, &log_offset,
|
|
|
|
&len, &record_cnt, &data_cnt);
|
2010-03-23 00:35:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ASSERT(len == 0);
|
|
|
|
|
2022-04-21 00:36:15 +00:00
|
|
|
/*
|
|
|
|
* We've already been guaranteed that the last writes will fit inside
|
|
|
|
* the current iclog, and hence it will already have the space used by
|
|
|
|
* those writes accounted to it. Hence we do not need to update the
|
|
|
|
* iclog with the number of bytes written here.
|
|
|
|
*/
|
2019-10-14 17:36:41 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2022-04-21 00:36:15 +00:00
|
|
|
xlog_state_finish_copy(log, iclog, record_cnt, 0);
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
error = xlog_state_release_iclog(log, iclog, ticket);
|
2019-10-14 17:36:41 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2019-10-14 17:36:41 +00:00
|
|
|
return error;
|
2010-03-23 00:35:45 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
static void
|
|
|
|
xlog_state_activate_iclog(
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
int *iclogs_changed)
|
|
|
|
{
|
|
|
|
ASSERT(list_empty_careful(&iclog->ic_callbacks));
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_activate(iclog, _RET_IP_);
|
2020-03-20 15:49:20 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the number of ops in this iclog indicate it just contains the
|
|
|
|
* dummy transaction, we can change state into IDLE (the second time
|
|
|
|
* around). Otherwise we should change the state into NEED a dummy.
|
|
|
|
* We don't need to cover the dummy.
|
|
|
|
*/
|
|
|
|
if (*iclogs_changed == 0 &&
|
|
|
|
iclog->ic_header.h_num_logops == cpu_to_be32(XLOG_COVER_OPS)) {
|
|
|
|
*iclogs_changed = 1;
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We have two dirty iclogs so start over. This could also be
|
|
|
|
* num of ops indicating this is not the dummy going out.
|
|
|
|
*/
|
|
|
|
*iclogs_changed = 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
iclog->ic_state = XLOG_STATE_ACTIVE;
|
|
|
|
iclog->ic_offset = 0;
|
|
|
|
iclog->ic_header.h_num_logops = 0;
|
|
|
|
memset(iclog->ic_header.h_cycle_data, 0,
|
|
|
|
sizeof(iclog->ic_header.h_cycle_data));
|
|
|
|
iclog->ic_header.h_lsn = 0;
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
iclog->ic_header.h_tail_lsn = 0;
|
2020-03-20 15:49:20 +00:00
|
|
|
}
|
|
|
|
|
2019-09-06 00:32:52 +00:00
|
|
|
/*
|
2020-03-20 15:49:20 +00:00
|
|
|
* Loop through all iclogs and mark all iclogs currently marked DIRTY as
|
|
|
|
* ACTIVE after iclog I/O has completed.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2020-03-20 15:49:20 +00:00
|
|
|
static void
|
|
|
|
xlog_state_activate_iclogs(
|
2019-09-06 00:32:52 +00:00
|
|
|
struct xlog *log,
|
2020-03-20 15:49:20 +00:00
|
|
|
int *iclogs_changed)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2020-03-20 15:49:20 +00:00
|
|
|
struct xlog_in_core *iclog = log->l_iclog;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
do {
|
2020-03-20 15:49:20 +00:00
|
|
|
if (iclog->ic_state == XLOG_STATE_DIRTY)
|
|
|
|
xlog_state_activate_iclog(iclog, iclogs_changed);
|
|
|
|
/*
|
|
|
|
* The ordering of marking iclogs ACTIVE must be maintained, so
|
|
|
|
* an iclog doesn't become ACTIVE beyond one that is SYNCING.
|
|
|
|
*/
|
|
|
|
else if (iclog->ic_state != XLOG_STATE_ACTIVE)
|
|
|
|
break;
|
|
|
|
} while ((iclog = iclog->ic_next) != log->l_iclog);
|
|
|
|
}
|
2019-09-06 00:32:52 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
static int
|
|
|
|
xlog_covered_state(
|
|
|
|
int prev_state,
|
|
|
|
int iclogs_changed)
|
|
|
|
{
|
2019-09-06 00:32:52 +00:00
|
|
|
/*
|
xfs: don't reset log idle state on covering checkpoints
Now that log covering occurs on quiesce, we'd like to reuse the
underlying superblock sync for final superblock updates. This
includes things like lazy superblock counter updates, log feature
incompat bits in the future, etc. One quirk to this approach is that
once the log is in the IDLE (i.e. already covered) state, any
subsequent log write resets the state back to NEED. This means that
a final superblock sync to an already covered log requires two more
sb syncs to return the log back to IDLE again.
For example, if a lazy superblock enabled filesystem is mount cycled
without any modifications, the unmount path syncs the superblock
once and writes an unmount record. With the desired log quiesce
covering behavior, we sync the superblock three times at unmount
time: once for the lazy superblock counter update and twice more to
cover the log. By contrast, if the log is active or only partially
covered at unmount time, a final superblock sync would doubly serve
as the one or two remaining syncs required to cover the log.
This duplicate covering sequence is unnecessary because the
filesystem remains consistent if a crash occurs at any point. The
superblock will either be recovered in the event of a crash or
written back before the log is quiesced and potentially cleaned with
an unmount record.
Update the log covering state machine to remain in the IDLE state if
additional covering checkpoints pass through the log. This
facilitates final superblock updates (such as lazy superblock
counters) via a single sb sync without losing covered status. This
provides some consistency with the active and partially covered
cases and also avoids harmless, but spurious checkpoints when
quiescing the log.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
* We go to NEED for any non-covering writes. We go to NEED2 if we just
|
|
|
|
* wrote the first covering record (DONE). We go to IDLE if we just
|
|
|
|
* wrote the second covering record (DONE2) and remain in IDLE until a
|
|
|
|
* non-covering write occurs.
|
2019-09-06 00:32:52 +00:00
|
|
|
*/
|
2020-03-20 15:49:20 +00:00
|
|
|
switch (prev_state) {
|
|
|
|
case XLOG_STATE_COVER_IDLE:
|
xfs: don't reset log idle state on covering checkpoints
Now that log covering occurs on quiesce, we'd like to reuse the
underlying superblock sync for final superblock updates. This
includes things like lazy superblock counter updates, log feature
incompat bits in the future, etc. One quirk to this approach is that
once the log is in the IDLE (i.e. already covered) state, any
subsequent log write resets the state back to NEED. This means that
a final superblock sync to an already covered log requires two more
sb syncs to return the log back to IDLE again.
For example, if a lazy superblock enabled filesystem is mount cycled
without any modifications, the unmount path syncs the superblock
once and writes an unmount record. With the desired log quiesce
covering behavior, we sync the superblock three times at unmount
time: once for the lazy superblock counter update and twice more to
cover the log. By contrast, if the log is active or only partially
covered at unmount time, a final superblock sync would doubly serve
as the one or two remaining syncs required to cover the log.
This duplicate covering sequence is unnecessary because the
filesystem remains consistent if a crash occurs at any point. The
superblock will either be recovered in the event of a crash or
written back before the log is quiesced and potentially cleaned with
an unmount record.
Update the log covering state machine to remain in the IDLE state if
additional covering checkpoints pass through the log. This
facilitates final superblock updates (such as lazy superblock
counters) via a single sb sync without losing covered status. This
provides some consistency with the active and partially covered
cases and also avoids harmless, but spurious checkpoints when
quiescing the log.
Signed-off-by: Brian Foster <bfoster@redhat.com>
Reviewed-by: Darrick J. Wong <darrick.wong@oracle.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
2021-01-23 00:48:22 +00:00
|
|
|
if (iclogs_changed == 1)
|
|
|
|
return XLOG_STATE_COVER_IDLE;
|
2021-04-20 22:54:36 +00:00
|
|
|
fallthrough;
|
2020-03-20 15:49:20 +00:00
|
|
|
case XLOG_STATE_COVER_NEED:
|
|
|
|
case XLOG_STATE_COVER_NEED2:
|
|
|
|
break;
|
|
|
|
case XLOG_STATE_COVER_DONE:
|
|
|
|
if (iclogs_changed == 1)
|
|
|
|
return XLOG_STATE_COVER_NEED2;
|
|
|
|
break;
|
|
|
|
case XLOG_STATE_COVER_DONE2:
|
|
|
|
if (iclogs_changed == 1)
|
|
|
|
return XLOG_STATE_COVER_IDLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ASSERT(0);
|
|
|
|
}
|
2019-09-06 00:32:52 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
return XLOG_STATE_COVER_NEED;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_state_clean_iclog(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *dirty_iclog)
|
|
|
|
{
|
|
|
|
int iclogs_changed = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_clean(dirty_iclog, _RET_IP_);
|
|
|
|
|
2020-03-20 15:49:21 +00:00
|
|
|
dirty_iclog->ic_state = XLOG_STATE_DIRTY;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-20 15:49:20 +00:00
|
|
|
xlog_state_activate_iclogs(log, &iclogs_changed);
|
|
|
|
wake_up_all(&dirty_iclog->ic_force_wait);
|
|
|
|
|
|
|
|
if (iclogs_changed) {
|
|
|
|
log->l_covered_state = xlog_covered_state(log->l_covered_state,
|
|
|
|
iclogs_changed);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2019-09-06 00:32:52 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
STATIC xfs_lsn_t
|
|
|
|
xlog_get_lowest_lsn(
|
2019-06-29 02:27:20 +00:00
|
|
|
struct xlog *log)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-06-29 02:27:20 +00:00
|
|
|
struct xlog_in_core *iclog = log->l_iclog;
|
|
|
|
xfs_lsn_t lowest_lsn = 0, lsn;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
do {
|
2019-10-14 17:36:43 +00:00
|
|
|
if (iclog->ic_state == XLOG_STATE_ACTIVE ||
|
|
|
|
iclog->ic_state == XLOG_STATE_DIRTY)
|
2019-06-29 02:27:20 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
lsn = be64_to_cpu(iclog->ic_header.h_lsn);
|
|
|
|
if ((lsn && !lowest_lsn) || XFS_LSN_CMP(lsn, lowest_lsn) < 0)
|
2005-04-16 22:20:36 +00:00
|
|
|
lowest_lsn = lsn;
|
2019-06-29 02:27:20 +00:00
|
|
|
} while ((iclog = iclog->ic_next) != log->l_iclog);
|
|
|
|
|
2006-01-15 01:37:08 +00:00
|
|
|
return lowest_lsn;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2019-09-06 00:32:52 +00:00
|
|
|
/*
|
|
|
|
* Completion of a iclog IO does not imply that a transaction has completed, as
|
|
|
|
* transactions can be large enough to span many iclogs. We cannot change the
|
|
|
|
* tail of the log half way through a transaction as this may be the only
|
|
|
|
* transaction in the log and moving the tail to point to the middle of it
|
|
|
|
* will prevent recovery from finding the start of the transaction. Hence we
|
|
|
|
* should only update the last_sync_lsn if this iclog contains transaction
|
|
|
|
* completion callbacks on it.
|
|
|
|
*
|
|
|
|
* We have to do this before we drop the icloglock to ensure we are the only one
|
|
|
|
* that can update it.
|
|
|
|
*
|
|
|
|
* If we are moving the last_sync_lsn forwards, we also need to ensure we kick
|
|
|
|
* the reservation grant head pushing. This is due to the fact that the push
|
|
|
|
* target is bound by the current last_sync_lsn value. Hence if we have a large
|
|
|
|
* amount of log space bound up in this committing transaction then the
|
|
|
|
* last_sync_lsn value may be the limiting factor preventing tail pushing from
|
|
|
|
* freeing space in the log. Hence once we've updated the last_sync_lsn we
|
|
|
|
* should push the AIL to ensure the push target (and hence the grant head) is
|
|
|
|
* no longer bound by the old log head location and can move forwards and make
|
|
|
|
* progress again.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xlog_state_set_callback(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
xfs_lsn_t header_lsn)
|
|
|
|
{
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_callback(iclog, _RET_IP_);
|
2019-09-06 00:32:52 +00:00
|
|
|
iclog->ic_state = XLOG_STATE_CALLBACK;
|
|
|
|
|
|
|
|
ASSERT(XFS_LSN_CMP(atomic64_read(&log->l_last_sync_lsn),
|
|
|
|
header_lsn) <= 0);
|
|
|
|
|
|
|
|
if (list_empty_careful(&iclog->ic_callbacks))
|
|
|
|
return;
|
|
|
|
|
|
|
|
atomic64_set(&log->l_last_sync_lsn, header_lsn);
|
|
|
|
xlog_grant_push_ail(log, 0);
|
|
|
|
}
|
|
|
|
|
2019-09-06 00:32:51 +00:00
|
|
|
/*
|
|
|
|
* Return true if we need to stop processing, false to continue to the next
|
|
|
|
* iclog. The caller will need to run callbacks if the iclog is returned in the
|
|
|
|
* XLOG_STATE_CALLBACK state.
|
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
xlog_state_iodone_process_iclog(
|
|
|
|
struct xlog *log,
|
2021-08-11 00:59:01 +00:00
|
|
|
struct xlog_in_core *iclog)
|
2019-09-06 00:32:51 +00:00
|
|
|
{
|
|
|
|
xfs_lsn_t lowest_lsn;
|
2019-09-06 00:32:52 +00:00
|
|
|
xfs_lsn_t header_lsn;
|
2019-09-06 00:32:51 +00:00
|
|
|
|
2019-10-14 17:36:43 +00:00
|
|
|
switch (iclog->ic_state) {
|
|
|
|
case XLOG_STATE_ACTIVE:
|
|
|
|
case XLOG_STATE_DIRTY:
|
|
|
|
/*
|
|
|
|
* Skip all iclogs in the ACTIVE & DIRTY states:
|
|
|
|
*/
|
2019-09-06 00:32:51 +00:00
|
|
|
return false;
|
2019-10-14 17:36:43 +00:00
|
|
|
case XLOG_STATE_DONE_SYNC:
|
|
|
|
/*
|
2019-10-14 17:36:43 +00:00
|
|
|
* Now that we have an iclog that is in the DONE_SYNC state, do
|
|
|
|
* one more check here to see if we have chased our tail around.
|
|
|
|
* If this is not the lowest lsn iclog, then we will leave it
|
|
|
|
* for another completion to process.
|
2019-10-14 17:36:43 +00:00
|
|
|
*/
|
|
|
|
header_lsn = be64_to_cpu(iclog->ic_header.h_lsn);
|
|
|
|
lowest_lsn = xlog_get_lowest_lsn(log);
|
|
|
|
if (lowest_lsn && XFS_LSN_CMP(lowest_lsn, header_lsn) < 0)
|
|
|
|
return false;
|
|
|
|
xlog_state_set_callback(log, iclog, header_lsn);
|
|
|
|
return false;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Can only perform callbacks in order. Since this iclog is not
|
2019-10-14 17:36:43 +00:00
|
|
|
* in the DONE_SYNC state, we skip the rest and just try to
|
|
|
|
* clean up.
|
2019-10-14 17:36:43 +00:00
|
|
|
*/
|
2019-09-06 00:32:51 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-11 01:00:40 +00:00
|
|
|
/*
|
|
|
|
* Loop over all the iclogs, running attached callbacks on them. Return true if
|
2021-08-11 01:00:40 +00:00
|
|
|
* we ran any callbacks, indicating that we dropped the icloglock. We don't need
|
|
|
|
* to handle transient shutdown state here at all because
|
|
|
|
* xlog_state_shutdown_callbacks() will be run to do the necessary shutdown
|
|
|
|
* cleanup of the callbacks.
|
2021-08-11 01:00:40 +00:00
|
|
|
*/
|
|
|
|
static bool
|
|
|
|
xlog_state_do_iclog_callbacks(
|
|
|
|
struct xlog *log)
|
|
|
|
__releases(&log->l_icloglock)
|
|
|
|
__acquires(&log->l_icloglock)
|
|
|
|
{
|
|
|
|
struct xlog_in_core *first_iclog = log->l_iclog;
|
|
|
|
struct xlog_in_core *iclog = first_iclog;
|
|
|
|
bool ran_callback = false;
|
|
|
|
|
|
|
|
do {
|
|
|
|
LIST_HEAD(cb_list);
|
|
|
|
|
2021-08-11 01:00:40 +00:00
|
|
|
if (xlog_state_iodone_process_iclog(log, iclog))
|
|
|
|
break;
|
|
|
|
if (iclog->ic_state != XLOG_STATE_CALLBACK) {
|
|
|
|
iclog = iclog->ic_next;
|
|
|
|
continue;
|
2021-08-11 01:00:40 +00:00
|
|
|
}
|
|
|
|
list_splice_init(&iclog->ic_callbacks, &cb_list);
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
|
|
|
|
trace_xlog_iclog_callbacks_start(iclog, _RET_IP_);
|
|
|
|
xlog_cil_process_committed(&cb_list);
|
|
|
|
trace_xlog_iclog_callbacks_done(iclog, _RET_IP_);
|
|
|
|
ran_callback = true;
|
|
|
|
|
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 01:00:40 +00:00
|
|
|
xlog_state_clean_iclog(log, iclog);
|
2021-08-11 01:00:40 +00:00
|
|
|
iclog = iclog->ic_next;
|
|
|
|
} while (iclog != first_iclog);
|
|
|
|
|
|
|
|
return ran_callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Loop running iclog completion callbacks until there are no more iclogs in a
|
|
|
|
* state that can run callbacks.
|
|
|
|
*/
|
2005-04-16 22:20:36 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_state_do_callback(
|
2020-03-20 15:49:20 +00:00
|
|
|
struct xlog *log)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-09-06 00:32:51 +00:00
|
|
|
int flushcnt = 0;
|
|
|
|
int repeats = 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 01:00:40 +00:00
|
|
|
while (xlog_state_do_iclog_callbacks(log)) {
|
|
|
|
if (xlog_is_shutdown(log))
|
|
|
|
break;
|
2006-09-28 01:02:14 +00:00
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
if (++repeats > 5000) {
|
2006-09-28 01:02:14 +00:00
|
|
|
flushcnt += repeats;
|
|
|
|
repeats = 0;
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(log->l_mp,
|
2006-09-28 01:02:14 +00:00
|
|
|
"%s: possible infinite loop (%d iterations)",
|
2008-04-10 02:19:21 +00:00
|
|
|
__func__, flushcnt);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2021-08-11 01:00:40 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-11 01:00:40 +00:00
|
|
|
if (log->l_iclog->ic_state == XLOG_STATE_ACTIVE)
|
2010-12-21 01:09:01 +00:00
|
|
|
wake_up_all(&log->l_flush_wait);
|
2019-09-06 00:32:48 +00:00
|
|
|
|
|
|
|
spin_unlock(&log->l_icloglock);
|
2008-05-19 06:34:27 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Finish transitioning this iclog to the dirty state.
|
|
|
|
*
|
|
|
|
* Callbacks could take time, so they are done outside the scope of the
|
2008-08-13 06:34:31 +00:00
|
|
|
* global state machine log lock.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-11-23 05:28:09 +00:00
|
|
|
STATIC void
|
2005-04-16 22:20:36 +00:00
|
|
|
xlog_state_done_syncing(
|
2020-03-20 15:49:20 +00:00
|
|
|
struct xlog_in_core *iclog)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2019-06-29 02:27:30 +00:00
|
|
|
struct xlog *log = iclog->ic_log;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2008-03-06 02:44:14 +00:00
|
|
|
ASSERT(atomic_read(&iclog->ic_refcnt) == 0);
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_sync_done(iclog, _RET_IP_);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we got an error, either on the first buffer, or in the case of
|
2020-03-20 15:49:20 +00:00
|
|
|
* split log writes, on the second, we shut down the file system and
|
|
|
|
* no iclogs should ever be attempted to be written to disk again.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2021-08-11 00:59:01 +00:00
|
|
|
if (!xlog_is_shutdown(log)) {
|
2020-03-20 15:49:20 +00:00
|
|
|
ASSERT(iclog->ic_state == XLOG_STATE_SYNCING);
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog->ic_state = XLOG_STATE_DONE_SYNC;
|
2020-03-20 15:49:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Someone could be sleeping prior to writing out the next
|
|
|
|
* iclog buffer, we wake them all, one will get to do the
|
|
|
|
* I/O, the others get to wait for the result.
|
|
|
|
*/
|
2010-12-21 01:09:01 +00:00
|
|
|
wake_up_all(&iclog->ic_write_wait);
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2020-03-26 01:18:24 +00:00
|
|
|
xlog_state_do_callback(log);
|
2020-03-20 15:49:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the head of the in-core log ring is not (ACTIVE or DIRTY), then we must
|
2008-08-13 06:34:31 +00:00
|
|
|
* sleep. We wait on the flush queue on the head iclog as that should be
|
|
|
|
* the first iclog to complete flushing. Hence if all iclogs are syncing,
|
|
|
|
* we will wait here and all new writes will sleep until a sync completes.
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* The in-core logs are used in a circular fashion. They are not used
|
|
|
|
* out-of-order even when an iclog past the head is free.
|
|
|
|
*
|
|
|
|
* return:
|
|
|
|
* * log_offset where xlog_write() can start writing into the in-core
|
|
|
|
* log's data space.
|
|
|
|
* * in-core log pointer to which xlog_write() should write.
|
|
|
|
* * boolean indicating this is a continued write to an in-core log.
|
|
|
|
* If this is the last write, then the in-core log's offset field
|
|
|
|
* needs to be incremented, depending on the amount of data which
|
|
|
|
* is copied.
|
|
|
|
*/
|
2007-11-23 05:28:09 +00:00
|
|
|
STATIC int
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_state_get_iclog_space(
|
|
|
|
struct xlog *log,
|
|
|
|
int len,
|
|
|
|
struct xlog_in_core **iclogp,
|
|
|
|
struct xlog_ticket *ticket,
|
|
|
|
int *logoffsetp)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
int log_offset;
|
|
|
|
xlog_rec_header_t *head;
|
|
|
|
xlog_in_core_t *iclog;
|
|
|
|
|
|
|
|
restart:
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log)) {
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2014-06-25 04:58:08 +00:00
|
|
|
return -EIO;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
iclog = log->l_iclog;
|
2008-05-19 06:34:27 +00:00
|
|
|
if (iclog->ic_state != XLOG_STATE_ACTIVE) {
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(log->l_mp, xs_log_noiclogs);
|
2008-05-19 06:34:27 +00:00
|
|
|
|
|
|
|
/* Wait for log writes to have flushed */
|
2010-12-21 01:09:01 +00:00
|
|
|
xlog_wait(&log->l_flush_wait, &log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
goto restart;
|
|
|
|
}
|
2008-05-19 06:34:27 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
head = &iclog->ic_header;
|
|
|
|
|
2008-03-06 02:44:14 +00:00
|
|
|
atomic_inc(&iclog->ic_refcnt); /* prevents sync */
|
2005-04-16 22:20:36 +00:00
|
|
|
log_offset = iclog->ic_offset;
|
|
|
|
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_get_space(iclog, _RET_IP_);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* On the 1st write to an iclog, figure out lsn. This works
|
|
|
|
* if iclogs marked XLOG_STATE_WANT_SYNC always write out what they are
|
|
|
|
* committing to. If the offset is set, that's how many blocks
|
|
|
|
* must be written.
|
|
|
|
*/
|
|
|
|
if (log_offset == 0) {
|
|
|
|
ticket->t_curr_res -= log->l_iclog_hsize;
|
2007-10-12 00:59:34 +00:00
|
|
|
head->h_cycle = cpu_to_be32(log->l_curr_cycle);
|
|
|
|
head->h_lsn = cpu_to_be64(
|
2007-10-12 00:58:05 +00:00
|
|
|
xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block));
|
2005-04-16 22:20:36 +00:00
|
|
|
ASSERT(log->l_curr_block >= 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If there is enough room to write everything, then do it. Otherwise,
|
|
|
|
* claim the rest of the region and make sure the XLOG_STATE_WANT_SYNC
|
|
|
|
* bit is on, so this will get flushed out. Don't update ic_offset
|
|
|
|
* until you know exactly how many bytes get copied. Therefore, wait
|
|
|
|
* until later to update ic_offset.
|
|
|
|
*
|
|
|
|
* xlog_write() algorithm assumes that at least 2 xlog_op_header_t's
|
|
|
|
* can fit into remaining data section.
|
|
|
|
*/
|
|
|
|
if (iclog->ic_size - iclog->ic_offset < 2*sizeof(xlog_op_header_t)) {
|
2019-10-14 17:36:41 +00:00
|
|
|
int error = 0;
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
xlog_state_switch_iclogs(log, iclog, iclog->ic_size);
|
|
|
|
|
2008-07-11 07:43:55 +00:00
|
|
|
/*
|
2019-10-14 17:36:41 +00:00
|
|
|
* If we are the only one writing to this iclog, sync it to
|
|
|
|
* disk. We need to do an atomic compare and decrement here to
|
|
|
|
* avoid racing with concurrent atomic_dec_and_lock() calls in
|
2008-07-11 07:43:55 +00:00
|
|
|
* xlog_state_release_iclog() when there is more than one
|
|
|
|
* reference to the iclog.
|
|
|
|
*/
|
2019-10-14 17:36:41 +00:00
|
|
|
if (!atomic_add_unless(&iclog->ic_refcnt, -1, 1))
|
xfs: xlog_sync() manually adjusts grant head space
When xlog_sync() rounds off the tail the iclog that is being
flushed, it manually subtracts that space from the grant heads. This
space is actually reserved by the transaction ticket that covers
the xlog_sync() call from xlog_write(), but we don't plumb the
ticket down far enough for it to account for the space consumed in
the current log ticket.
The grant heads are hot, so we really should be accounting this to
the ticket is we can, rather than adding thousands of extra grant
head updates every CIL commit.
Interestingly, this actually indicates a potential log space overrun
can occur when we force the log. By the time that xfs_log_force()
pushes out an active iclog and consumes the roundoff space, the
reservation for that roundoff space has been returned to the grant
heads and is no longer covered by a reservation. In theory the
roundoff added to log force on an already full log could push the
write head past the tail. In practice, the CIL commit that writes to
the log and needs the iclog pushed will have reserved space for
roundoff, so when it releases the ticket there will still be
physical space for the roundoff to be committed to the log, even
though it is no longer reserved. This roundoff won't be enough space
to allow a transaction to be woken if the log is full, so overruns
should not actually occur in practice.
That said, it indicates that we should not release the CIL context
log ticket until after we've released the commit iclog. It also
means that xlog_sync() still needs the direct grant head
manipulation if we don't provide it with a ticket. Log forces are
rare when we are in fast paths running 1.5 million transactions/s
that make the grant heads hot, so let's optimise the hot case and
pass CIL log tickets down to the xlog_sync() code.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-07 08:56:09 +00:00
|
|
|
error = xlog_state_release_iclog(log, iclog, ticket);
|
2019-10-14 17:36:41 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
if (error)
|
|
|
|
return error;
|
2005-04-16 22:20:36 +00:00
|
|
|
goto restart;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Do we have enough room to write the full amount in the remainder
|
|
|
|
* of this iclog? Or must we continue a write on the next iclog and
|
|
|
|
* mark this iclog as completely taken? In the case where we switch
|
|
|
|
* iclogs (to mark it taken), this particular iclog will release/sync
|
|
|
|
* to disk in xlog_write().
|
|
|
|
*/
|
2022-04-21 00:36:37 +00:00
|
|
|
if (len <= iclog->ic_size - iclog->ic_offset)
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog->ic_offset += len;
|
2022-04-21 00:36:37 +00:00
|
|
|
else
|
2005-04-16 22:20:36 +00:00
|
|
|
xlog_state_switch_iclogs(log, iclog, iclog->ic_size);
|
|
|
|
*iclogp = iclog;
|
|
|
|
|
|
|
|
ASSERT(iclog->ic_offset <= iclog->ic_size);
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
*logoffsetp = log_offset;
|
|
|
|
return 0;
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
/*
|
2020-03-26 01:18:24 +00:00
|
|
|
* The first cnt-1 times a ticket goes through here we don't need to move the
|
|
|
|
* grant write head because the permanent reservation has reserved cnt times the
|
|
|
|
* unit amount. Release part of current permanent unit reservation and reset
|
|
|
|
* current reservation to be one units worth. Also move grant reservation head
|
|
|
|
* forward.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2020-03-26 01:18:23 +00:00
|
|
|
void
|
|
|
|
xfs_log_ticket_regrant(
|
2012-06-14 14:22:16 +00:00
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_ticket *ticket)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2020-03-26 01:18:23 +00:00
|
|
|
trace_xfs_log_ticket_regrant(log, ticket);
|
2009-12-14 23:14:59 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ticket->t_cnt > 0)
|
|
|
|
ticket->t_cnt--;
|
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
xlog_grant_sub_space(log, &log->l_reserve_head.grant,
|
2010-12-21 01:08:20 +00:00
|
|
|
ticket->t_curr_res);
|
2012-02-20 02:31:25 +00:00
|
|
|
xlog_grant_sub_space(log, &log->l_write_head.grant,
|
2010-12-21 01:08:20 +00:00
|
|
|
ticket->t_curr_res);
|
2005-04-16 22:20:36 +00:00
|
|
|
ticket->t_curr_res = ticket->t_unit_res;
|
2009-12-14 23:14:59 +00:00
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
trace_xfs_log_ticket_regrant_sub(log, ticket);
|
2009-12-14 23:14:59 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* just return if we still have some of the pre-reserved space */
|
2020-03-26 01:18:23 +00:00
|
|
|
if (!ticket->t_cnt) {
|
|
|
|
xlog_grant_add_space(log, &log->l_reserve_head.grant,
|
|
|
|
ticket->t_unit_res);
|
|
|
|
trace_xfs_log_ticket_regrant_exit(log, ticket);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
ticket->t_curr_res = ticket->t_unit_res;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
xfs_log_ticket_put(ticket);
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Give back the space left from a reservation.
|
|
|
|
*
|
|
|
|
* All the information we need to make a correct determination of space left
|
|
|
|
* is present. For non-permanent reservations, things are quite easy. The
|
|
|
|
* count should have been decremented to zero. We only need to deal with the
|
|
|
|
* space remaining in the current reservation part of the ticket. If the
|
|
|
|
* ticket contains a permanent reservation, there may be left over space which
|
|
|
|
* needs to be released. A count of N means that N-1 refills of the current
|
|
|
|
* reservation can be done before we need to ask for more space. The first
|
|
|
|
* one goes to fill up the first current reservation. Once we run out of
|
|
|
|
* space, the count will stay at zero and the only space remaining will be
|
|
|
|
* in the current reservation field.
|
|
|
|
*/
|
2020-03-26 01:18:23 +00:00
|
|
|
void
|
|
|
|
xfs_log_ticket_ungrant(
|
2012-06-14 14:22:16 +00:00
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_ticket *ticket)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2020-03-26 01:18:23 +00:00
|
|
|
int bytes;
|
|
|
|
|
|
|
|
trace_xfs_log_ticket_ungrant(log, ticket);
|
2010-12-21 01:06:05 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ticket->t_cnt > 0)
|
|
|
|
ticket->t_cnt--;
|
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
trace_xfs_log_ticket_ungrant_sub(log, ticket);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-12-21 01:06:05 +00:00
|
|
|
/*
|
|
|
|
* If this is a permanent reservation ticket, we may be able to free
|
2005-04-16 22:20:36 +00:00
|
|
|
* up more space based on the remaining count.
|
|
|
|
*/
|
2010-12-21 01:06:05 +00:00
|
|
|
bytes = ticket->t_curr_res;
|
2005-04-16 22:20:36 +00:00
|
|
|
if (ticket->t_cnt > 0) {
|
|
|
|
ASSERT(ticket->t_flags & XLOG_TIC_PERM_RESERV);
|
2010-12-21 01:06:05 +00:00
|
|
|
bytes += ticket->t_unit_res*ticket->t_cnt;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
xlog_grant_sub_space(log, &log->l_reserve_head.grant, bytes);
|
|
|
|
xlog_grant_sub_space(log, &log->l_write_head.grant, bytes);
|
2010-12-21 01:06:05 +00:00
|
|
|
|
2020-03-26 01:18:23 +00:00
|
|
|
trace_xfs_log_ticket_ungrant_exit(log, ticket);
|
2009-12-14 23:14:59 +00:00
|
|
|
|
2012-02-20 02:31:23 +00:00
|
|
|
xfs_log_space_wake(log->l_mp);
|
2020-03-26 01:18:23 +00:00
|
|
|
xfs_log_ticket_put(ticket);
|
2012-02-20 02:31:20 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2020-03-26 01:18:24 +00:00
|
|
|
* This routine will mark the current iclog in the ring as WANT_SYNC and move
|
|
|
|
* the current iclog pointer to the next iclog in the ring.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
xfs: AIL needs asynchronous CIL forcing
The AIL pushing is stalling on log forces when it comes across
pinned items. This is happening on removal workloads where the AIL
is dominated by stale items that are removed from AIL when the
checkpoint that marks the items stale is committed to the journal.
This results is relatively few items in the AIL, but those that are
are often pinned as directories items are being removed from are
still being logged.
As a result, many push cycles through the CIL will first issue a
blocking log force to unpin the items. This can take some time to
complete, with tracing regularly showing push delays of half a
second and sometimes up into the range of several seconds. Sequences
like this aren't uncommon:
....
399.829437: xfsaild: last lsn 0x11002dd000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 270ms delay>
400.099622: xfsaild: target 0x11002f3600, prev 0x11002f3600, last lsn 0x0
400.099623: xfsaild: first lsn 0x11002f3600
400.099679: xfsaild: last lsn 0x1100305000 count 16 stuck 11 flushing 0 tout 50
<wanted 50ms, got 500ms delay>
400.589348: xfsaild: target 0x110032e600, prev 0x11002f3600, last lsn 0x0
400.589349: xfsaild: first lsn 0x1100305000
400.589595: xfsaild: last lsn 0x110032e600 count 156 stuck 101 flushing 30 tout 50
<wanted 50ms, got 460ms delay>
400.950341: xfsaild: target 0x1100353000, prev 0x110032e600, last lsn 0x0
400.950343: xfsaild: first lsn 0x1100317c00
400.950436: xfsaild: last lsn 0x110033d200 count 105 stuck 101 flushing 0 tout 20
<wanted 20ms, got 200ms delay>
401.142333: xfsaild: target 0x1100361600, prev 0x1100353000, last lsn 0x0
401.142334: xfsaild: first lsn 0x110032e600
401.142535: xfsaild: last lsn 0x1100353000 count 122 stuck 101 flushing 8 tout 10
<wanted 10ms, got 10ms delay>
401.154323: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x1100353000
401.154328: xfsaild: first lsn 0x1100353000
401.154389: xfsaild: last lsn 0x1100353000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 300ms delay>
401.451525: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
401.451526: xfsaild: first lsn 0x1100353000
401.451804: xfsaild: last lsn 0x1100377200 count 170 stuck 22 flushing 122 tout 50
<wanted 50ms, got 500ms delay>
401.933581: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
....
In each of these cases, every AIL pass saw 101 log items stuck on
the AIL (pinned) with very few other items being found. Each pass, a
log force was issued, and delay between last/first is the sleep time
+ the sync log force time.
Some of these 101 items pinned the tail of the log. The tail of the
log does slowly creep forward (first lsn), but the problem is that
the log is actually out of reservation space because it's been
running so many transactions that stale items that never reach the
AIL but consume log space. Hence we have a largely empty AIL, with
long term pins on items that pin the tail of the log that don't get
pushed frequently enough to keep log space available.
The problem is the hundreds of milliseconds that we block in the log
force pushing the CIL out to disk. The AIL should not be stalled
like this - it needs to run and flush items that are at the tail of
the log with minimal latency. What we really need to do is trigger a
log flush, but then not wait for it at all - we've already done our
waiting for stuff to complete when we backed off prior to the log
force being issued.
Even if we remove the XFS_LOG_SYNC from the xfs_log_force() call, we
still do a blocking flush of the CIL and that is what is causing the
issue. Hence we need a new interface for the CIL to trigger an
immediate background push of the CIL to get it moving faster but not
to wait on that to occur. While the CIL is pushing, the AIL can also
be pushing.
We already have an internal interface to do this -
xlog_cil_push_now() - but we need a wrapper for it to be used
externally. xlog_cil_force_seq() can easily be extended to do what
we need as it already implements the synchronous CIL push via
xlog_cil_push_now(). Add the necessary flags and "push current
sequence" semantics to xlog_cil_force_seq() and convert the AIL
pushing to use it.
One of the complexities here is that the CIL push does not guarantee
that the commit record for the CIL checkpoint is written to disk.
The current log force ensures this by submitting the current ACTIVE
iclog that the commit record was written to. We need the CIL to
actually write this commit record to disk for an async push to
ensure that the checkpoint actually makes it to disk and unpins the
pinned items in the checkpoint on completion. Hence we need to pass
down to the CIL push that we are doing an async flush so that it can
switch out the commit_iclog if necessary to get written to disk when
the commit iclog is finally released.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:44 +00:00
|
|
|
void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_state_switch_iclogs(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
int eventual_size)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
ASSERT(iclog->ic_state == XLOG_STATE_ACTIVE);
|
2020-03-20 15:49:21 +00:00
|
|
|
assert_spin_locked(&log->l_icloglock);
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_switch(iclog, _RET_IP_);
|
2020-03-20 15:49:21 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (!eventual_size)
|
|
|
|
eventual_size = iclog->ic_offset;
|
|
|
|
iclog->ic_state = XLOG_STATE_WANT_SYNC;
|
2007-10-12 00:59:34 +00:00
|
|
|
iclog->ic_header.h_prev_block = cpu_to_be32(log->l_prev_block);
|
2005-04-16 22:20:36 +00:00
|
|
|
log->l_prev_block = log->l_curr_block;
|
|
|
|
log->l_prev_cycle = log->l_curr_cycle;
|
|
|
|
|
|
|
|
/* roll log?: ic_offset changed later */
|
|
|
|
log->l_curr_block += BTOBB(eventual_size)+BTOBB(log->l_iclog_hsize);
|
|
|
|
|
|
|
|
/* Round up to next log-sunit */
|
2021-06-18 15:21:48 +00:00
|
|
|
if (log->l_iclog_roundoff > BBSIZE) {
|
2021-06-18 15:24:04 +00:00
|
|
|
uint32_t sunit_bb = BTOBB(log->l_iclog_roundoff);
|
2005-04-16 22:20:36 +00:00
|
|
|
log->l_curr_block = roundup(log->l_curr_block, sunit_bb);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log->l_curr_block >= log->l_logBBsize) {
|
2015-10-12 04:59:25 +00:00
|
|
|
/*
|
|
|
|
* Rewind the current block before the cycle is bumped to make
|
|
|
|
* sure that the combined LSN never transiently moves forward
|
|
|
|
* when the log wraps to the next cycle. This is to support the
|
|
|
|
* unlocked sample of these fields from xlog_valid_lsn(). Most
|
|
|
|
* other cases should acquire l_icloglock.
|
|
|
|
*/
|
|
|
|
log->l_curr_block -= log->l_logBBsize;
|
|
|
|
ASSERT(log->l_curr_block >= 0);
|
|
|
|
smp_wmb();
|
2005-04-16 22:20:36 +00:00
|
|
|
log->l_curr_cycle++;
|
|
|
|
if (log->l_curr_cycle == XLOG_HEADER_MAGIC_NUM)
|
|
|
|
log->l_curr_cycle++;
|
|
|
|
}
|
|
|
|
ASSERT(iclog == log->l_iclog);
|
|
|
|
log->l_iclog = iclog->ic_next;
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-07-27 23:23:49 +00:00
|
|
|
/*
|
|
|
|
* Force the iclog to disk and check if the iclog has been completed before
|
|
|
|
* xlog_force_iclog() returns. This can happen on synchronous (e.g.
|
|
|
|
* pmem) or fast async storage because we drop the icloglock to issue the IO.
|
|
|
|
* If completion has already occurred, tell the caller so that it can avoid an
|
|
|
|
* unnecessary wait on the iclog.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
xlog_force_and_check_iclog(
|
|
|
|
struct xlog_in_core *iclog,
|
|
|
|
bool *completed)
|
|
|
|
{
|
|
|
|
xfs_lsn_t lsn = be64_to_cpu(iclog->ic_header.h_lsn);
|
|
|
|
int error;
|
|
|
|
|
|
|
|
*completed = false;
|
|
|
|
error = xlog_force_iclog(iclog);
|
|
|
|
if (error)
|
|
|
|
return error;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the iclog has already been completed and reused the header LSN
|
|
|
|
* will have been rewritten by completion
|
|
|
|
*/
|
|
|
|
if (be64_to_cpu(iclog->ic_header.h_lsn) != lsn)
|
|
|
|
*completed = true;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
|
|
|
* Write out all data in the in-core log as of this exact moment in time.
|
|
|
|
*
|
|
|
|
* Data may be written to the in-core log during this call. However,
|
|
|
|
* we don't guarantee this data will be written out. A change from past
|
|
|
|
* implementation means this routine will *not* write out zero length LRs.
|
|
|
|
*
|
|
|
|
* Basically, we try and perform an intelligent scan of the in-core logs.
|
|
|
|
* If we determine there is no flushable data, we just return. There is no
|
|
|
|
* flushable data if:
|
|
|
|
*
|
|
|
|
* 1. the current iclog is active and has no data; the previous iclog
|
|
|
|
* is in the active or dirty state.
|
|
|
|
* 2. the current iclog is drity, and the previous iclog is in the
|
|
|
|
* active or dirty state.
|
|
|
|
*
|
2008-08-13 06:34:31 +00:00
|
|
|
* We may sleep if:
|
2005-04-16 22:20:36 +00:00
|
|
|
*
|
|
|
|
* 1. the current iclog is not in the active nor dirty state.
|
|
|
|
* 2. the current iclog dirty, and the previous iclog is not in the
|
|
|
|
* active nor dirty state.
|
|
|
|
* 3. the current iclog is active, and there is another thread writing
|
|
|
|
* to this particular iclog.
|
|
|
|
* 4. a) the current iclog is active and has no other writers
|
|
|
|
* b) when we return from flushing out this iclog, it is still
|
|
|
|
* not in the active nor dirty state.
|
|
|
|
*/
|
2010-01-19 09:56:46 +00:00
|
|
|
int
|
2018-03-14 06:15:28 +00:00
|
|
|
xfs_log_force(
|
2010-01-19 09:56:46 +00:00
|
|
|
struct xfs_mount *mp,
|
2018-03-14 06:15:28 +00:00
|
|
|
uint flags)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
2010-01-19 09:56:46 +00:00
|
|
|
struct xlog_in_core *iclog;
|
|
|
|
|
2015-10-12 07:21:22 +00:00
|
|
|
XFS_STATS_INC(mp, xs_log_force);
|
2018-03-14 06:15:28 +00:00
|
|
|
trace_xfs_log_force(mp, 0, _RET_IP_);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2011-12-06 21:58:07 +00:00
|
|
|
xlog_cil_force(log);
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2018-03-14 06:15:29 +00:00
|
|
|
goto out_error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
iclog = log->l_iclog;
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_force(iclog, _RET_IP_);
|
|
|
|
|
2018-03-14 06:15:29 +00:00
|
|
|
if (iclog->ic_state == XLOG_STATE_DIRTY ||
|
|
|
|
(iclog->ic_state == XLOG_STATE_ACTIVE &&
|
|
|
|
atomic_read(&iclog->ic_refcnt) == 0 && iclog->ic_offset == 0)) {
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2018-03-14 06:15:29 +00:00
|
|
|
* If the head is dirty or (active and empty), then we need to
|
|
|
|
* look at the previous iclog.
|
|
|
|
*
|
|
|
|
* If the previous iclog is active or dirty we are done. There
|
|
|
|
* is nothing to sync out. Otherwise, we attach ourselves to the
|
2005-04-16 22:20:36 +00:00
|
|
|
* previous iclog and go to sleep.
|
|
|
|
*/
|
2018-03-14 06:15:29 +00:00
|
|
|
iclog = iclog->ic_prev;
|
|
|
|
} else if (iclog->ic_state == XLOG_STATE_ACTIVE) {
|
|
|
|
if (atomic_read(&iclog->ic_refcnt) == 0) {
|
2021-07-27 23:23:48 +00:00
|
|
|
/* We have exclusive access to this iclog. */
|
2021-07-27 23:23:49 +00:00
|
|
|
bool completed;
|
|
|
|
|
|
|
|
if (xlog_force_and_check_iclog(iclog, &completed))
|
2019-10-14 17:36:41 +00:00
|
|
|
goto out_error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-07-27 23:23:49 +00:00
|
|
|
if (completed)
|
2018-03-14 06:15:29 +00:00
|
|
|
goto out_unlock;
|
|
|
|
} else {
|
|
|
|
/*
|
2021-07-27 23:23:49 +00:00
|
|
|
* Someone else is still writing to this iclog, so we
|
|
|
|
* need to ensure that when they release the iclog it
|
|
|
|
* gets synced immediately as we may be waiting on it.
|
2018-03-14 06:15:29 +00:00
|
|
|
*/
|
|
|
|
xlog_state_switch_iclogs(log, iclog, 0);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-14 06:15:29 +00:00
|
|
|
|
2021-07-27 23:23:49 +00:00
|
|
|
/*
|
|
|
|
* The iclog we are about to wait on may contain the checkpoint pushed
|
|
|
|
* by the above xlog_cil_force() call, but it may not have been pushed
|
|
|
|
* to disk yet. Like the ACTIVE case above, we need to make sure caches
|
|
|
|
* are flushed when this iclog is written.
|
|
|
|
*/
|
|
|
|
if (iclog->ic_state == XLOG_STATE_WANT_SYNC)
|
|
|
|
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
|
|
|
|
|
2020-03-20 15:49:18 +00:00
|
|
|
if (flags & XFS_LOG_SYNC)
|
|
|
|
return xlog_wait_on_iclog(iclog);
|
2018-03-14 06:15:29 +00:00
|
|
|
out_unlock:
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
return 0;
|
|
|
|
out_error:
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
return -EIO;
|
2010-01-19 09:56:46 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: AIL needs asynchronous CIL forcing
The AIL pushing is stalling on log forces when it comes across
pinned items. This is happening on removal workloads where the AIL
is dominated by stale items that are removed from AIL when the
checkpoint that marks the items stale is committed to the journal.
This results is relatively few items in the AIL, but those that are
are often pinned as directories items are being removed from are
still being logged.
As a result, many push cycles through the CIL will first issue a
blocking log force to unpin the items. This can take some time to
complete, with tracing regularly showing push delays of half a
second and sometimes up into the range of several seconds. Sequences
like this aren't uncommon:
....
399.829437: xfsaild: last lsn 0x11002dd000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 270ms delay>
400.099622: xfsaild: target 0x11002f3600, prev 0x11002f3600, last lsn 0x0
400.099623: xfsaild: first lsn 0x11002f3600
400.099679: xfsaild: last lsn 0x1100305000 count 16 stuck 11 flushing 0 tout 50
<wanted 50ms, got 500ms delay>
400.589348: xfsaild: target 0x110032e600, prev 0x11002f3600, last lsn 0x0
400.589349: xfsaild: first lsn 0x1100305000
400.589595: xfsaild: last lsn 0x110032e600 count 156 stuck 101 flushing 30 tout 50
<wanted 50ms, got 460ms delay>
400.950341: xfsaild: target 0x1100353000, prev 0x110032e600, last lsn 0x0
400.950343: xfsaild: first lsn 0x1100317c00
400.950436: xfsaild: last lsn 0x110033d200 count 105 stuck 101 flushing 0 tout 20
<wanted 20ms, got 200ms delay>
401.142333: xfsaild: target 0x1100361600, prev 0x1100353000, last lsn 0x0
401.142334: xfsaild: first lsn 0x110032e600
401.142535: xfsaild: last lsn 0x1100353000 count 122 stuck 101 flushing 8 tout 10
<wanted 10ms, got 10ms delay>
401.154323: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x1100353000
401.154328: xfsaild: first lsn 0x1100353000
401.154389: xfsaild: last lsn 0x1100353000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 300ms delay>
401.451525: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
401.451526: xfsaild: first lsn 0x1100353000
401.451804: xfsaild: last lsn 0x1100377200 count 170 stuck 22 flushing 122 tout 50
<wanted 50ms, got 500ms delay>
401.933581: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
....
In each of these cases, every AIL pass saw 101 log items stuck on
the AIL (pinned) with very few other items being found. Each pass, a
log force was issued, and delay between last/first is the sleep time
+ the sync log force time.
Some of these 101 items pinned the tail of the log. The tail of the
log does slowly creep forward (first lsn), but the problem is that
the log is actually out of reservation space because it's been
running so many transactions that stale items that never reach the
AIL but consume log space. Hence we have a largely empty AIL, with
long term pins on items that pin the tail of the log that don't get
pushed frequently enough to keep log space available.
The problem is the hundreds of milliseconds that we block in the log
force pushing the CIL out to disk. The AIL should not be stalled
like this - it needs to run and flush items that are at the tail of
the log with minimal latency. What we really need to do is trigger a
log flush, but then not wait for it at all - we've already done our
waiting for stuff to complete when we backed off prior to the log
force being issued.
Even if we remove the XFS_LOG_SYNC from the xfs_log_force() call, we
still do a blocking flush of the CIL and that is what is causing the
issue. Hence we need a new interface for the CIL to trigger an
immediate background push of the CIL to get it moving faster but not
to wait on that to occur. While the CIL is pushing, the AIL can also
be pushing.
We already have an internal interface to do this -
xlog_cil_push_now() - but we need a wrapper for it to be used
externally. xlog_cil_force_seq() can easily be extended to do what
we need as it already implements the synchronous CIL push via
xlog_cil_push_now(). Add the necessary flags and "push current
sequence" semantics to xlog_cil_force_seq() and convert the AIL
pushing to use it.
One of the complexities here is that the CIL push does not guarantee
that the commit record for the CIL checkpoint is written to disk.
The current log force ensures this by submitting the current ACTIVE
iclog that the commit record was written to. We need the CIL to
actually write this commit record to disk for an async push to
ensure that the checkpoint actually makes it to disk and unpins the
pinned items in the checkpoint on completion. Hence we need to pass
down to the CIL push that we are doing an async flush so that it can
switch out the commit_iclog if necessary to get written to disk when
the commit iclog is finally released.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:44 +00:00
|
|
|
/*
|
|
|
|
* Force the log to a specific LSN.
|
|
|
|
*
|
|
|
|
* If an iclog with that lsn can be found:
|
|
|
|
* If it is in the DIRTY state, just return.
|
|
|
|
* If it is in the ACTIVE state, move the in-core log into the WANT_SYNC
|
|
|
|
* state and go to sleep or return.
|
|
|
|
* If it is in any other state, go to sleep or return.
|
|
|
|
*
|
|
|
|
* Synchronous forces are implemented with a wait queue. All callers trying
|
|
|
|
* to force a given lsn to disk must wait on the queue attached to the
|
|
|
|
* specific in-core log. When given in-core log finally completes its write
|
|
|
|
* to disk, that thread will wake up all threads waiting on the queue.
|
|
|
|
*/
|
2018-03-14 06:15:30 +00:00
|
|
|
static int
|
2021-06-18 15:21:52 +00:00
|
|
|
xlog_force_lsn(
|
|
|
|
struct xlog *log,
|
2010-01-19 09:56:46 +00:00
|
|
|
xfs_lsn_t lsn,
|
|
|
|
uint flags,
|
2018-03-14 06:15:30 +00:00
|
|
|
int *log_flushed,
|
|
|
|
bool already_slept)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2010-01-19 09:56:46 +00:00
|
|
|
struct xlog_in_core *iclog;
|
2021-07-27 23:23:49 +00:00
|
|
|
bool completed;
|
xfs: Introduce delayed logging core code
The delayed logging code only changes in-memory structures and as
such can be enabled and disabled with a mount option. Add the mount
option and emit a warning that this is an experimental feature that
should not be used in production yet.
We also need infrastructure to track committed items that have not
yet been written to the log. This is what the Committed Item List
(CIL) is for.
The log item also needs to be extended to track the current log
vector, the associated memory buffer and it's location in the Commit
Item List. Extend the log item and log vector structures to enable
this tracking.
To maintain the current log format for transactions with delayed
logging, we need to introduce a checkpoint transaction and a context
for tracking each checkpoint from initiation to transaction
completion. This includes adding a log ticket for tracking space
log required/used by the context checkpoint.
To track all the changes we need an io vector array per log item,
rather than a single array for the entire transaction. Using the new
log vector structure for this requires two passes - the first to
allocate the log vector structures and chain them together, and the
second to fill them out. This log vector chain can then be passed
to the CIL for formatting, pinning and insertion into the CIL.
Formatting of the log vector chain is relatively simple - it's just
a loop over the iovecs on each log vector, but it is made slightly
more complex because we re-write the iovec after the copy to point
back at the memory buffer we just copied into.
This code also needs to pin log items. If the log item is not
already tracked in this checkpoint context, then it needs to be
pinned. Otherwise it is already pinned and we don't need to pin it
again.
The only other complexity is calculating the amount of new log space
the formatting has consumed. This needs to be accounted to the
transaction in progress, and the accounting is made more complex
becase we need also to steal space from it for log metadata in the
checkpoint transaction. Calculate all this at insert time and update
all the tickets, counters, etc correctly.
Once we've formatted all the log items in the transaction, attach
the busy extents to the checkpoint context so the busy extents live
until checkpoint completion and can be processed at that point in
time. Transactions can then be freed at this point in time.
Now we need to issue checkpoints - we are tracking the amount of log space
used by the items in the CIL, so we can trigger background checkpoints when the
space usage gets to a certain threshold. Otherwise, checkpoints need ot be
triggered when a log synchronisation point is reached - a log force event.
Because the log write code already handles chained log vectors, writing the
transaction is trivial, too. Construct a transaction header, add it
to the head of the chain and write it into the log, then issue a
commit record write. Then we can release the checkpoint log ticket
and attach the context to the log buffer so it can be called during
Io completion to complete the checkpoint.
We also need to allow for synchronising multiple in-flight
checkpoints. This is needed for two things - the first is to ensure
that checkpoint commit records appear in the log in the correct
sequence order (so they are replayed in the correct order). The
second is so that xfs_log_force_lsn() operates correctly and only
flushes and/or waits for the specific sequence it was provided with.
To do this we need a wait variable and a list tracking the
checkpoint commits in progress. We can walk this list and wait for
the checkpoints to change state or complete easily, an this provides
the necessary synchronisation for correct operation in both cases.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Alex Elder <aelder@sgi.com>
2010-05-21 04:37:18 +00:00
|
|
|
|
2010-01-19 09:56:46 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 00:59:01 +00:00
|
|
|
if (xlog_is_shutdown(log))
|
2018-03-14 06:15:29 +00:00
|
|
|
goto out_error;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2021-08-11 00:59:01 +00:00
|
|
|
iclog = log->l_iclog;
|
2018-03-14 06:15:29 +00:00
|
|
|
while (be64_to_cpu(iclog->ic_header.h_lsn) != lsn) {
|
2021-06-18 18:57:05 +00:00
|
|
|
trace_xlog_iclog_force_lsn(iclog, _RET_IP_);
|
2018-03-14 06:15:29 +00:00
|
|
|
iclog = iclog->ic_next;
|
|
|
|
if (iclog == log->l_iclog)
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
2010-01-19 09:56:46 +00:00
|
|
|
|
2021-07-27 23:23:49 +00:00
|
|
|
switch (iclog->ic_state) {
|
|
|
|
case XLOG_STATE_ACTIVE:
|
2018-03-14 06:15:29 +00:00
|
|
|
/*
|
|
|
|
* We sleep here if we haven't already slept (e.g. this is the
|
|
|
|
* first time we've looked at the correct iclog buf) and the
|
|
|
|
* buffer before us is going to be sync'ed. The reason for this
|
|
|
|
* is that if we are doing sync transactions here, by waiting
|
|
|
|
* for the previous I/O to complete, we can allow a few more
|
|
|
|
* transactions into this iclog before we close it down.
|
|
|
|
*
|
|
|
|
* Otherwise, we mark the buffer WANT_SYNC, and bump up the
|
|
|
|
* refcnt so we can release the log (which drops the ref count).
|
|
|
|
* The state switch keeps new transaction commits from using
|
|
|
|
* this buffer. When the current commits finish writing into
|
|
|
|
* the buffer, the refcount will drop to zero and the buffer
|
|
|
|
* will go out then.
|
|
|
|
*/
|
|
|
|
if (!already_slept &&
|
2019-10-14 17:36:43 +00:00
|
|
|
(iclog->ic_prev->ic_state == XLOG_STATE_WANT_SYNC ||
|
|
|
|
iclog->ic_prev->ic_state == XLOG_STATE_SYNCING)) {
|
2018-03-14 06:15:29 +00:00
|
|
|
xlog_wait(&iclog->ic_prev->ic_write_wait,
|
|
|
|
&log->l_icloglock);
|
2018-03-14 06:15:30 +00:00
|
|
|
return -EAGAIN;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2021-07-27 23:23:49 +00:00
|
|
|
if (xlog_force_and_check_iclog(iclog, &completed))
|
2019-10-14 17:36:41 +00:00
|
|
|
goto out_error;
|
2018-03-14 06:15:29 +00:00
|
|
|
if (log_flushed)
|
|
|
|
*log_flushed = 1;
|
2021-07-27 23:23:49 +00:00
|
|
|
if (completed)
|
|
|
|
goto out_unlock;
|
2021-07-27 23:23:49 +00:00
|
|
|
break;
|
|
|
|
case XLOG_STATE_WANT_SYNC:
|
|
|
|
/*
|
|
|
|
* This iclog may contain the checkpoint pushed by the
|
|
|
|
* xlog_cil_force_seq() call, but there are other writers still
|
|
|
|
* accessing it so it hasn't been pushed to disk yet. Like the
|
|
|
|
* ACTIVE case above, we need to make sure caches are flushed
|
|
|
|
* when this iclog is written.
|
|
|
|
*/
|
|
|
|
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* The entire checkpoint was written by the CIL force and is on
|
|
|
|
* its way to disk already. It will be stable when it
|
|
|
|
* completes, so we don't need to manipulate caches here at all.
|
|
|
|
* We just need to wait for completion if necessary.
|
|
|
|
*/
|
|
|
|
break;
|
2018-03-14 06:15:29 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2020-03-20 15:49:18 +00:00
|
|
|
if (flags & XFS_LOG_SYNC)
|
|
|
|
return xlog_wait_on_iclog(iclog);
|
2018-03-14 06:15:29 +00:00
|
|
|
out_unlock:
|
2010-01-19 09:56:46 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
return 0;
|
2018-03-14 06:15:29 +00:00
|
|
|
out_error:
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
return -EIO;
|
2010-01-19 09:56:46 +00:00
|
|
|
}
|
|
|
|
|
2018-03-14 06:15:30 +00:00
|
|
|
/*
|
xfs: AIL needs asynchronous CIL forcing
The AIL pushing is stalling on log forces when it comes across
pinned items. This is happening on removal workloads where the AIL
is dominated by stale items that are removed from AIL when the
checkpoint that marks the items stale is committed to the journal.
This results is relatively few items in the AIL, but those that are
are often pinned as directories items are being removed from are
still being logged.
As a result, many push cycles through the CIL will first issue a
blocking log force to unpin the items. This can take some time to
complete, with tracing regularly showing push delays of half a
second and sometimes up into the range of several seconds. Sequences
like this aren't uncommon:
....
399.829437: xfsaild: last lsn 0x11002dd000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 270ms delay>
400.099622: xfsaild: target 0x11002f3600, prev 0x11002f3600, last lsn 0x0
400.099623: xfsaild: first lsn 0x11002f3600
400.099679: xfsaild: last lsn 0x1100305000 count 16 stuck 11 flushing 0 tout 50
<wanted 50ms, got 500ms delay>
400.589348: xfsaild: target 0x110032e600, prev 0x11002f3600, last lsn 0x0
400.589349: xfsaild: first lsn 0x1100305000
400.589595: xfsaild: last lsn 0x110032e600 count 156 stuck 101 flushing 30 tout 50
<wanted 50ms, got 460ms delay>
400.950341: xfsaild: target 0x1100353000, prev 0x110032e600, last lsn 0x0
400.950343: xfsaild: first lsn 0x1100317c00
400.950436: xfsaild: last lsn 0x110033d200 count 105 stuck 101 flushing 0 tout 20
<wanted 20ms, got 200ms delay>
401.142333: xfsaild: target 0x1100361600, prev 0x1100353000, last lsn 0x0
401.142334: xfsaild: first lsn 0x110032e600
401.142535: xfsaild: last lsn 0x1100353000 count 122 stuck 101 flushing 8 tout 10
<wanted 10ms, got 10ms delay>
401.154323: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x1100353000
401.154328: xfsaild: first lsn 0x1100353000
401.154389: xfsaild: last lsn 0x1100353000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 300ms delay>
401.451525: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
401.451526: xfsaild: first lsn 0x1100353000
401.451804: xfsaild: last lsn 0x1100377200 count 170 stuck 22 flushing 122 tout 50
<wanted 50ms, got 500ms delay>
401.933581: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
....
In each of these cases, every AIL pass saw 101 log items stuck on
the AIL (pinned) with very few other items being found. Each pass, a
log force was issued, and delay between last/first is the sleep time
+ the sync log force time.
Some of these 101 items pinned the tail of the log. The tail of the
log does slowly creep forward (first lsn), but the problem is that
the log is actually out of reservation space because it's been
running so many transactions that stale items that never reach the
AIL but consume log space. Hence we have a largely empty AIL, with
long term pins on items that pin the tail of the log that don't get
pushed frequently enough to keep log space available.
The problem is the hundreds of milliseconds that we block in the log
force pushing the CIL out to disk. The AIL should not be stalled
like this - it needs to run and flush items that are at the tail of
the log with minimal latency. What we really need to do is trigger a
log flush, but then not wait for it at all - we've already done our
waiting for stuff to complete when we backed off prior to the log
force being issued.
Even if we remove the XFS_LOG_SYNC from the xfs_log_force() call, we
still do a blocking flush of the CIL and that is what is causing the
issue. Hence we need a new interface for the CIL to trigger an
immediate background push of the CIL to get it moving faster but not
to wait on that to occur. While the CIL is pushing, the AIL can also
be pushing.
We already have an internal interface to do this -
xlog_cil_push_now() - but we need a wrapper for it to be used
externally. xlog_cil_force_seq() can easily be extended to do what
we need as it already implements the synchronous CIL push via
xlog_cil_push_now(). Add the necessary flags and "push current
sequence" semantics to xlog_cil_force_seq() and convert the AIL
pushing to use it.
One of the complexities here is that the CIL push does not guarantee
that the commit record for the CIL checkpoint is written to disk.
The current log force ensures this by submitting the current ACTIVE
iclog that the commit record was written to. We need the CIL to
actually write this commit record to disk for an async push to
ensure that the checkpoint actually makes it to disk and unpins the
pinned items in the checkpoint on completion. Hence we need to pass
down to the CIL push that we are doing an async flush so that it can
switch out the commit_iclog if necessary to get written to disk when
the commit iclog is finally released.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:44 +00:00
|
|
|
* Force the log to a specific checkpoint sequence.
|
2018-03-14 06:15:30 +00:00
|
|
|
*
|
xfs: AIL needs asynchronous CIL forcing
The AIL pushing is stalling on log forces when it comes across
pinned items. This is happening on removal workloads where the AIL
is dominated by stale items that are removed from AIL when the
checkpoint that marks the items stale is committed to the journal.
This results is relatively few items in the AIL, but those that are
are often pinned as directories items are being removed from are
still being logged.
As a result, many push cycles through the CIL will first issue a
blocking log force to unpin the items. This can take some time to
complete, with tracing regularly showing push delays of half a
second and sometimes up into the range of several seconds. Sequences
like this aren't uncommon:
....
399.829437: xfsaild: last lsn 0x11002dd000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 270ms delay>
400.099622: xfsaild: target 0x11002f3600, prev 0x11002f3600, last lsn 0x0
400.099623: xfsaild: first lsn 0x11002f3600
400.099679: xfsaild: last lsn 0x1100305000 count 16 stuck 11 flushing 0 tout 50
<wanted 50ms, got 500ms delay>
400.589348: xfsaild: target 0x110032e600, prev 0x11002f3600, last lsn 0x0
400.589349: xfsaild: first lsn 0x1100305000
400.589595: xfsaild: last lsn 0x110032e600 count 156 stuck 101 flushing 30 tout 50
<wanted 50ms, got 460ms delay>
400.950341: xfsaild: target 0x1100353000, prev 0x110032e600, last lsn 0x0
400.950343: xfsaild: first lsn 0x1100317c00
400.950436: xfsaild: last lsn 0x110033d200 count 105 stuck 101 flushing 0 tout 20
<wanted 20ms, got 200ms delay>
401.142333: xfsaild: target 0x1100361600, prev 0x1100353000, last lsn 0x0
401.142334: xfsaild: first lsn 0x110032e600
401.142535: xfsaild: last lsn 0x1100353000 count 122 stuck 101 flushing 8 tout 10
<wanted 10ms, got 10ms delay>
401.154323: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x1100353000
401.154328: xfsaild: first lsn 0x1100353000
401.154389: xfsaild: last lsn 0x1100353000 count 101 stuck 101 flushing 0 tout 20
<wanted 20ms, got 300ms delay>
401.451525: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
401.451526: xfsaild: first lsn 0x1100353000
401.451804: xfsaild: last lsn 0x1100377200 count 170 stuck 22 flushing 122 tout 50
<wanted 50ms, got 500ms delay>
401.933581: xfsaild: target 0x1100361600, prev 0x1100361600, last lsn 0x0
....
In each of these cases, every AIL pass saw 101 log items stuck on
the AIL (pinned) with very few other items being found. Each pass, a
log force was issued, and delay between last/first is the sleep time
+ the sync log force time.
Some of these 101 items pinned the tail of the log. The tail of the
log does slowly creep forward (first lsn), but the problem is that
the log is actually out of reservation space because it's been
running so many transactions that stale items that never reach the
AIL but consume log space. Hence we have a largely empty AIL, with
long term pins on items that pin the tail of the log that don't get
pushed frequently enough to keep log space available.
The problem is the hundreds of milliseconds that we block in the log
force pushing the CIL out to disk. The AIL should not be stalled
like this - it needs to run and flush items that are at the tail of
the log with minimal latency. What we really need to do is trigger a
log flush, but then not wait for it at all - we've already done our
waiting for stuff to complete when we backed off prior to the log
force being issued.
Even if we remove the XFS_LOG_SYNC from the xfs_log_force() call, we
still do a blocking flush of the CIL and that is what is causing the
issue. Hence we need a new interface for the CIL to trigger an
immediate background push of the CIL to get it moving faster but not
to wait on that to occur. While the CIL is pushing, the AIL can also
be pushing.
We already have an internal interface to do this -
xlog_cil_push_now() - but we need a wrapper for it to be used
externally. xlog_cil_force_seq() can easily be extended to do what
we need as it already implements the synchronous CIL push via
xlog_cil_push_now(). Add the necessary flags and "push current
sequence" semantics to xlog_cil_force_seq() and convert the AIL
pushing to use it.
One of the complexities here is that the CIL push does not guarantee
that the commit record for the CIL checkpoint is written to disk.
The current log force ensures this by submitting the current ACTIVE
iclog that the commit record was written to. We need the CIL to
actually write this commit record to disk for an async push to
ensure that the checkpoint actually makes it to disk and unpins the
pinned items in the checkpoint on completion. Hence we need to pass
down to the CIL push that we are doing an async flush so that it can
switch out the commit_iclog if necessary to get written to disk when
the commit iclog is finally released.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Allison Henderson <allison.henderson@oracle.com>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-08-11 01:00:44 +00:00
|
|
|
* First force the CIL so that all the required changes have been flushed to the
|
|
|
|
* iclogs. If the CIL force completed it will return a commit LSN that indicates
|
|
|
|
* the iclog that needs to be flushed to stable storage. If the caller needs
|
|
|
|
* a synchronous log force, we will wait on the iclog with the LSN returned by
|
|
|
|
* xlog_cil_force_seq() to be completed.
|
2018-03-14 06:15:30 +00:00
|
|
|
*/
|
|
|
|
int
|
2021-06-18 15:21:52 +00:00
|
|
|
xfs_log_force_seq(
|
2018-03-14 06:15:30 +00:00
|
|
|
struct xfs_mount *mp,
|
2021-06-18 15:21:52 +00:00
|
|
|
xfs_csn_t seq,
|
2018-03-14 06:15:30 +00:00
|
|
|
uint flags,
|
|
|
|
int *log_flushed)
|
|
|
|
{
|
2021-06-18 15:21:52 +00:00
|
|
|
struct xlog *log = mp->m_log;
|
|
|
|
xfs_lsn_t lsn;
|
2018-03-14 06:15:30 +00:00
|
|
|
int ret;
|
2021-06-18 15:21:52 +00:00
|
|
|
ASSERT(seq != 0);
|
2018-03-14 06:15:30 +00:00
|
|
|
|
|
|
|
XFS_STATS_INC(mp, xs_log_force);
|
2021-06-18 15:21:52 +00:00
|
|
|
trace_xfs_log_force(mp, seq, _RET_IP_);
|
2018-03-14 06:15:30 +00:00
|
|
|
|
2021-06-18 15:21:52 +00:00
|
|
|
lsn = xlog_cil_force_seq(log, seq);
|
2018-03-14 06:15:30 +00:00
|
|
|
if (lsn == NULLCOMMITLSN)
|
|
|
|
return 0;
|
|
|
|
|
2021-06-18 15:21:52 +00:00
|
|
|
ret = xlog_force_lsn(log, lsn, flags, log_flushed, false);
|
|
|
|
if (ret == -EAGAIN) {
|
|
|
|
XFS_STATS_INC(mp, xs_log_force_sleep);
|
|
|
|
ret = xlog_force_lsn(log, lsn, flags, log_flushed, true);
|
|
|
|
}
|
2018-03-14 06:15:30 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2009-03-29 07:55:42 +00:00
|
|
|
* Free a used ticket when its refcount falls to zero.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2008-11-17 06:37:10 +00:00
|
|
|
void
|
|
|
|
xfs_log_ticket_put(
|
|
|
|
xlog_ticket_t *ticket)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2008-11-17 06:37:10 +00:00
|
|
|
ASSERT(atomic_read(&ticket->t_ref) > 0);
|
2010-12-21 01:09:01 +00:00
|
|
|
if (atomic_dec_and_test(&ticket->t_ref))
|
2021-10-12 18:09:23 +00:00
|
|
|
kmem_cache_free(xfs_log_ticket_cache, ticket);
|
2008-11-17 06:37:10 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2008-11-17 06:37:10 +00:00
|
|
|
xlog_ticket_t *
|
|
|
|
xfs_log_ticket_get(
|
|
|
|
xlog_ticket_t *ticket)
|
|
|
|
{
|
|
|
|
ASSERT(atomic_read(&ticket->t_ref) > 0);
|
|
|
|
atomic_inc(&ticket->t_ref);
|
|
|
|
return ticket;
|
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2013-08-12 10:50:01 +00:00
|
|
|
* Figure out the total log space unit (in bytes) that would be
|
|
|
|
* required for a log ticket.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2021-06-18 15:21:48 +00:00
|
|
|
static int
|
|
|
|
xlog_calc_unit_res(
|
|
|
|
struct xlog *log,
|
xfs: rework per-iclog header CIL reservation
For every iclog that a CIL push will use up, we need to ensure we
have space reserved for the iclog header in each iclog. It is
extremely difficult to do this accurately with a per-cpu counter
without expensive summing of the counter in every commit. However,
we know what the maximum CIL size is going to be because of the
hard space limit we have, and hence we know exactly how many iclogs
we are going to need to write out the CIL.
We are constrained by the requirement that small transactions only
have reservation space for a single iclog header built into them.
At commit time we don't know how much of the current transaction
reservation is made up of iclog header reservations as calculated by
xfs_log_calc_unit_res() when the ticket was reserved. As larger
reservations have multiple header spaces reserved, we can steal
more than one iclog header reservation at a time, but we only steal
the exact number needed for the given log vector size delta.
As a result, we don't know exactly when we are going to steal iclog
header reservations, nor do we know exactly how many we are going to
need for a given CIL.
To make things simple, start by calculating the worst case number of
iclog headers a full CIL push will require. Record this into an
atomic variable in the CIL. Then add a byte counter to the log
ticket that records exactly how much iclog header space has been
reserved in this ticket by xfs_log_calc_unit_res(). This tells us
exactly how much space we can steal from the ticket at transaction
commit time.
Now, at transaction commit time, we can check if the CIL has a full
iclog header reservation and, if not, steal the entire reservation
the current ticket holds for iclog headers. This minimises the
number of times we need to do atomic operations in the fast path,
but still guarantees we get all the reservations we need.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-01 16:12:52 +00:00
|
|
|
int unit_bytes,
|
|
|
|
int *niclogs)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2013-08-12 10:50:01 +00:00
|
|
|
int iclog_space;
|
|
|
|
uint num_headers;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Permanent reservations have up to 'cnt'-1 active log operations
|
|
|
|
* in the log. A unit in this case is the amount of space for one
|
|
|
|
* of these log operations. Normal reservations have a cnt of 1
|
|
|
|
* and their unit amount is the total amount of space required.
|
|
|
|
*
|
|
|
|
* The following lines of code account for non-transaction data
|
2005-09-02 06:41:43 +00:00
|
|
|
* which occupy space in the on-disk log.
|
|
|
|
*
|
|
|
|
* Normal form of a transaction is:
|
|
|
|
* <oph><trans-hdr><start-oph><reg1-oph><reg1><reg2-oph>...<commit-oph>
|
|
|
|
* and then there are LR hdrs, split-recs and roundoff at end of syncs.
|
|
|
|
*
|
|
|
|
* We need to account for all the leadup data and trailer data
|
|
|
|
* around the transaction data.
|
|
|
|
* And then we need to account for the worst case in terms of using
|
|
|
|
* more space.
|
|
|
|
* The worst case will happen if:
|
|
|
|
* - the placement of the transaction happens to be such that the
|
|
|
|
* roundoff is at its maximum
|
|
|
|
* - the transaction data is synced before the commit record is synced
|
|
|
|
* i.e. <transaction-data><roundoff> | <commit-rec><roundoff>
|
|
|
|
* Therefore the commit record is in its own Log Record.
|
|
|
|
* This can happen as the commit record is called with its
|
|
|
|
* own region to xlog_write().
|
|
|
|
* This then means that in the worst case, roundoff can happen for
|
|
|
|
* the commit-rec as well.
|
|
|
|
* The commit-rec is smaller than padding in this scenario and so it is
|
|
|
|
* not added separately.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
|
|
|
|
2005-09-02 06:41:43 +00:00
|
|
|
/* for trans header */
|
|
|
|
unit_bytes += sizeof(xlog_op_header_t);
|
|
|
|
unit_bytes += sizeof(xfs_trans_header_t);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* for start-rec */
|
2005-09-02 06:41:43 +00:00
|
|
|
unit_bytes += sizeof(xlog_op_header_t);
|
|
|
|
|
2010-03-23 00:21:11 +00:00
|
|
|
/*
|
|
|
|
* for LR headers - the space for data in an iclog is the size minus
|
|
|
|
* the space used for the headers. If we use the iclog size, then we
|
|
|
|
* undercalculate the number of headers required.
|
|
|
|
*
|
|
|
|
* Furthermore - the addition of op headers for split-recs might
|
|
|
|
* increase the space required enough to require more log and op
|
|
|
|
* headers, so take that into account too.
|
|
|
|
*
|
|
|
|
* IMPORTANT: This reservation makes the assumption that if this
|
|
|
|
* transaction is the first in an iclog and hence has the LR headers
|
|
|
|
* accounted to it, then the remaining space in the iclog is
|
|
|
|
* exclusively for this transaction. i.e. if the transaction is larger
|
|
|
|
* than the iclog, it will be the only thing in that iclog.
|
|
|
|
* Fundamentally, this means we must pass the entire log vector to
|
|
|
|
* xlog_write to guarantee this.
|
|
|
|
*/
|
|
|
|
iclog_space = log->l_iclog_size - log->l_iclog_hsize;
|
|
|
|
num_headers = howmany(unit_bytes, iclog_space);
|
|
|
|
|
|
|
|
/* for split-recs - ophdrs added when data split over LRs */
|
|
|
|
unit_bytes += sizeof(xlog_op_header_t) * num_headers;
|
|
|
|
|
|
|
|
/* add extra header reservations if we overrun */
|
|
|
|
while (!num_headers ||
|
|
|
|
howmany(unit_bytes, iclog_space) > num_headers) {
|
|
|
|
unit_bytes += sizeof(xlog_op_header_t);
|
|
|
|
num_headers++;
|
|
|
|
}
|
2005-09-02 06:41:43 +00:00
|
|
|
unit_bytes += log->l_iclog_hsize * num_headers;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-09-02 06:41:43 +00:00
|
|
|
/* for commit-rec LR header - note: padding will subsume the ophdr */
|
|
|
|
unit_bytes += log->l_iclog_hsize;
|
|
|
|
|
2021-06-18 15:21:48 +00:00
|
|
|
/* roundoff padding for transaction data and one for commit record */
|
|
|
|
unit_bytes += 2 * log->l_iclog_roundoff;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: rework per-iclog header CIL reservation
For every iclog that a CIL push will use up, we need to ensure we
have space reserved for the iclog header in each iclog. It is
extremely difficult to do this accurately with a per-cpu counter
without expensive summing of the counter in every commit. However,
we know what the maximum CIL size is going to be because of the
hard space limit we have, and hence we know exactly how many iclogs
we are going to need to write out the CIL.
We are constrained by the requirement that small transactions only
have reservation space for a single iclog header built into them.
At commit time we don't know how much of the current transaction
reservation is made up of iclog header reservations as calculated by
xfs_log_calc_unit_res() when the ticket was reserved. As larger
reservations have multiple header spaces reserved, we can steal
more than one iclog header reservation at a time, but we only steal
the exact number needed for the given log vector size delta.
As a result, we don't know exactly when we are going to steal iclog
header reservations, nor do we know exactly how many we are going to
need for a given CIL.
To make things simple, start by calculating the worst case number of
iclog headers a full CIL push will require. Record this into an
atomic variable in the CIL. Then add a byte counter to the log
ticket that records exactly how much iclog header space has been
reserved in this ticket by xfs_log_calc_unit_res(). This tells us
exactly how much space we can steal from the ticket at transaction
commit time.
Now, at transaction commit time, we can check if the CIL has a full
iclog header reservation and, if not, steal the entire reservation
the current ticket holds for iclog headers. This minimises the
number of times we need to do atomic operations in the fast path,
but still guarantees we get all the reservations we need.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-01 16:12:52 +00:00
|
|
|
if (niclogs)
|
|
|
|
*niclogs = num_headers;
|
2013-08-12 10:50:01 +00:00
|
|
|
return unit_bytes;
|
|
|
|
}
|
|
|
|
|
2021-06-18 15:21:48 +00:00
|
|
|
int
|
|
|
|
xfs_log_calc_unit_res(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
int unit_bytes)
|
|
|
|
{
|
xfs: rework per-iclog header CIL reservation
For every iclog that a CIL push will use up, we need to ensure we
have space reserved for the iclog header in each iclog. It is
extremely difficult to do this accurately with a per-cpu counter
without expensive summing of the counter in every commit. However,
we know what the maximum CIL size is going to be because of the
hard space limit we have, and hence we know exactly how many iclogs
we are going to need to write out the CIL.
We are constrained by the requirement that small transactions only
have reservation space for a single iclog header built into them.
At commit time we don't know how much of the current transaction
reservation is made up of iclog header reservations as calculated by
xfs_log_calc_unit_res() when the ticket was reserved. As larger
reservations have multiple header spaces reserved, we can steal
more than one iclog header reservation at a time, but we only steal
the exact number needed for the given log vector size delta.
As a result, we don't know exactly when we are going to steal iclog
header reservations, nor do we know exactly how many we are going to
need for a given CIL.
To make things simple, start by calculating the worst case number of
iclog headers a full CIL push will require. Record this into an
atomic variable in the CIL. Then add a byte counter to the log
ticket that records exactly how much iclog header space has been
reserved in this ticket by xfs_log_calc_unit_res(). This tells us
exactly how much space we can steal from the ticket at transaction
commit time.
Now, at transaction commit time, we can check if the CIL has a full
iclog header reservation and, if not, steal the entire reservation
the current ticket holds for iclog headers. This minimises the
number of times we need to do atomic operations in the fast path,
but still guarantees we get all the reservations we need.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-01 16:12:52 +00:00
|
|
|
return xlog_calc_unit_res(mp->m_log, unit_bytes, NULL);
|
2021-06-18 15:21:48 +00:00
|
|
|
}
|
|
|
|
|
2013-08-12 10:50:01 +00:00
|
|
|
/*
|
|
|
|
* Allocate and initialise a new log ticket.
|
|
|
|
*/
|
|
|
|
struct xlog_ticket *
|
|
|
|
xlog_ticket_alloc(
|
|
|
|
struct xlog *log,
|
|
|
|
int unit_bytes,
|
|
|
|
int cnt,
|
2020-07-22 16:23:17 +00:00
|
|
|
bool permanent)
|
2013-08-12 10:50:01 +00:00
|
|
|
{
|
|
|
|
struct xlog_ticket *tic;
|
|
|
|
int unit_res;
|
|
|
|
|
2021-10-12 18:09:23 +00:00
|
|
|
tic = kmem_cache_zalloc(xfs_log_ticket_cache, GFP_NOFS | __GFP_NOFAIL);
|
2013-08-12 10:50:01 +00:00
|
|
|
|
xfs: rework per-iclog header CIL reservation
For every iclog that a CIL push will use up, we need to ensure we
have space reserved for the iclog header in each iclog. It is
extremely difficult to do this accurately with a per-cpu counter
without expensive summing of the counter in every commit. However,
we know what the maximum CIL size is going to be because of the
hard space limit we have, and hence we know exactly how many iclogs
we are going to need to write out the CIL.
We are constrained by the requirement that small transactions only
have reservation space for a single iclog header built into them.
At commit time we don't know how much of the current transaction
reservation is made up of iclog header reservations as calculated by
xfs_log_calc_unit_res() when the ticket was reserved. As larger
reservations have multiple header spaces reserved, we can steal
more than one iclog header reservation at a time, but we only steal
the exact number needed for the given log vector size delta.
As a result, we don't know exactly when we are going to steal iclog
header reservations, nor do we know exactly how many we are going to
need for a given CIL.
To make things simple, start by calculating the worst case number of
iclog headers a full CIL push will require. Record this into an
atomic variable in the CIL. Then add a byte counter to the log
ticket that records exactly how much iclog header space has been
reserved in this ticket by xfs_log_calc_unit_res(). This tells us
exactly how much space we can steal from the ticket at transaction
commit time.
Now, at transaction commit time, we can check if the CIL has a full
iclog header reservation and, if not, steal the entire reservation
the current ticket holds for iclog headers. This minimises the
number of times we need to do atomic operations in the fast path,
but still guarantees we get all the reservations we need.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
2022-07-01 16:12:52 +00:00
|
|
|
unit_res = xlog_calc_unit_res(log, unit_bytes, &tic->t_iclog_hdrs);
|
2013-08-12 10:50:01 +00:00
|
|
|
|
2008-11-17 06:37:10 +00:00
|
|
|
atomic_set(&tic->t_ref, 1);
|
2012-02-20 02:31:24 +00:00
|
|
|
tic->t_task = current;
|
2010-12-21 01:02:25 +00:00
|
|
|
INIT_LIST_HEAD(&tic->t_queue);
|
2013-08-12 10:50:01 +00:00
|
|
|
tic->t_unit_res = unit_res;
|
|
|
|
tic->t_curr_res = unit_res;
|
2005-04-16 22:20:36 +00:00
|
|
|
tic->t_cnt = cnt;
|
|
|
|
tic->t_ocnt = cnt;
|
2013-03-04 12:58:20 +00:00
|
|
|
tic->t_tid = prandom_u32();
|
2012-02-20 02:31:31 +00:00
|
|
|
if (permanent)
|
2005-04-16 22:20:36 +00:00
|
|
|
tic->t_flags |= XLOG_TIC_PERM_RESERV;
|
|
|
|
|
|
|
|
return tic;
|
2008-11-17 06:37:10 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2005-11-02 04:12:04 +00:00
|
|
|
#if defined(DEBUG)
|
2011-04-08 02:45:07 +00:00
|
|
|
/*
|
|
|
|
* Check to make sure the grant write head didn't just over lap the tail. If
|
|
|
|
* the cycles are the same, we can't be overlapping. Otherwise, make sure that
|
|
|
|
* the cycles differ by exactly one and check the byte count.
|
|
|
|
*
|
|
|
|
* This check is run unlocked, so can give false positives. Rather than assert
|
|
|
|
* on failures, use a warn-once flag and a panic tag to allow the admin to
|
|
|
|
* determine if they want to panic the machine when such an error occurs. For
|
|
|
|
* debug kernels this will have the same effect as using an assert but, unlinke
|
|
|
|
* an assert, it can be turned off at runtime.
|
|
|
|
*/
|
2010-12-21 01:02:52 +00:00
|
|
|
STATIC void
|
|
|
|
xlog_verify_grant_tail(
|
2012-06-14 14:22:15 +00:00
|
|
|
struct xlog *log)
|
2010-12-21 01:02:52 +00:00
|
|
|
{
|
2010-12-21 01:28:39 +00:00
|
|
|
int tail_cycle, tail_blocks;
|
2010-12-21 01:08:20 +00:00
|
|
|
int cycle, space;
|
2010-12-21 01:02:52 +00:00
|
|
|
|
2012-02-20 02:31:25 +00:00
|
|
|
xlog_crack_grant_head(&log->l_write_head.grant, &cycle, &space);
|
2010-12-21 01:28:39 +00:00
|
|
|
xlog_crack_atomic_lsn(&log->l_tail_lsn, &tail_cycle, &tail_blocks);
|
|
|
|
if (tail_cycle != cycle) {
|
2011-04-08 02:45:07 +00:00
|
|
|
if (cycle - 1 != tail_cycle &&
|
2021-08-11 00:59:02 +00:00
|
|
|
!test_and_set_bit(XLOG_TAIL_WARN, &log->l_opstate)) {
|
2011-04-08 02:45:07 +00:00
|
|
|
xfs_alert_tag(log->l_mp, XFS_PTAG_LOGRES,
|
|
|
|
"%s: cycle - 1 != tail_cycle", __func__);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (space > BBTOB(tail_blocks) &&
|
2021-08-11 00:59:02 +00:00
|
|
|
!test_and_set_bit(XLOG_TAIL_WARN, &log->l_opstate)) {
|
2011-04-08 02:45:07 +00:00
|
|
|
xfs_alert_tag(log->l_mp, XFS_PTAG_LOGRES,
|
|
|
|
"%s: space > BBTOB(tail_blocks)", __func__);
|
|
|
|
}
|
2010-12-21 01:02:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/* check if it will fit */
|
|
|
|
STATIC void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_verify_tail_lsn(
|
|
|
|
struct xlog *log,
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
struct xlog_in_core *iclog)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
xfs: limit iclog tail updates
From the department of "generic/482 keeps on giving", we bring you
another tail update race condition:
iclog:
S1 C1
+-----------------------+-----------------------+
S2 EOIC
Two checkpoints in a single iclog. One is complete, the other just
contains the start record and overruns into a new iclog.
Timeline:
Before S1: Cache flush, log tail = X
At S1: Metadata stable, write start record and checkpoint
At C1: Write commit record, set NEED_FUA
Single iclog checkpoint, so no need for NEED_FLUSH
Log tail still = X, so no need for NEED_FLUSH
After C1,
Before S2: Cache flush, log tail = X
At S2: Metadata stable, write start record and checkpoint
After S2: Log tail moves to X+1
At EOIC: End of iclog, more journal data to write
Releases iclog
Not a commit iclog, so no need for NEED_FLUSH
Writes log tail X+1 into iclog.
At this point, the iclog has tail X+1 and NEED_FUA set. There has
been no cache flush for the metadata between X and X+1, and the
iclog writes the new tail permanently to the log. THis is sufficient
to violate on disk metadata/journal ordering.
We have two options here. The first is to detect this case in some
manner and ensure that the partial checkpoint write sets NEED_FLUSH
when the iclog is already marked NEED_FUA and the log tail changes.
This seems somewhat fragile and quite complex to get right, and it
doesn't actually make it obvious what underlying problem it is
actually addressing from reading the code.
The second option seems much cleaner to me, because it is derived
directly from the requirements of the C1 commit record in the iclog.
That is, when we write this commit record to the iclog, we've
guaranteed that the metadata/data ordering is correct for tail
update purposes. Hence if we only write the log tail into the iclog
for the *first* commit record rather than the log tail at the last
release, we guarantee that the log tail does not move past where the
the first commit record in the log expects it to be.
IOWs, taking the first option means that replay of C1 becomes
dependent on future operations doing the right thing, not just the
C1 checkpoint itself doing the right thing. This makes log recovery
almost impossible to reason about because now we have to take into
account what might or might not have happened in the future when
looking at checkpoints in the log rather than just having to
reconstruct the past...
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2021-07-29 00:14:11 +00:00
|
|
|
xfs_lsn_t tail_lsn = be64_to_cpu(iclog->ic_header.h_tail_lsn);
|
|
|
|
int blocks;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
if (CYCLE_LSN(tail_lsn) == log->l_prev_cycle) {
|
|
|
|
blocks =
|
|
|
|
log->l_logBBsize - (log->l_prev_block - BLOCK_LSN(tail_lsn));
|
|
|
|
if (blocks < BTOBB(iclog->ic_offset)+BTOBB(log->l_iclog_hsize))
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: ran out of log space", __func__);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
|
|
|
ASSERT(CYCLE_LSN(tail_lsn)+1 == log->l_prev_cycle);
|
|
|
|
|
|
|
|
if (BLOCK_LSN(tail_lsn) == log->l_prev_block)
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: tail wrapped", __func__);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
blocks = BLOCK_LSN(tail_lsn) - log->l_prev_block;
|
|
|
|
if (blocks < BTOBB(iclog->ic_offset) + 1)
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: ran out of log space", __func__);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Perform a number of checks on the iclog before writing to disk.
|
|
|
|
*
|
|
|
|
* 1. Make sure the iclogs are still circular
|
|
|
|
* 2. Make sure we have a good magic number
|
|
|
|
* 3. Make sure we don't have magic numbers in the data
|
|
|
|
* 4. Check fields of each log operation header for:
|
|
|
|
* A. Valid client identifier
|
|
|
|
* B. tid ptr value falls in valid ptr space (user space code)
|
|
|
|
* C. Length in log record header is correct according to the
|
|
|
|
* individual operation headers within record.
|
|
|
|
* 5. When a bwrite will occur within 5 blocks of the front of the physical
|
|
|
|
* log, check the preceding blocks of the physical log to make sure all
|
|
|
|
* the cycle numbers agree with the current cycle number.
|
|
|
|
*/
|
|
|
|
STATIC void
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_verify_iclog(
|
|
|
|
struct xlog *log,
|
|
|
|
struct xlog_in_core *iclog,
|
2019-06-29 02:27:24 +00:00
|
|
|
int count)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
xlog_op_header_t *ophead;
|
|
|
|
xlog_in_core_t *icptr;
|
|
|
|
xlog_in_core_2_t *xhdr;
|
2015-06-21 23:44:47 +00:00
|
|
|
void *base_ptr, *ptr, *p;
|
2015-06-21 23:43:32 +00:00
|
|
|
ptrdiff_t field_offset;
|
2017-06-16 18:00:05 +00:00
|
|
|
uint8_t clientid;
|
2005-04-16 22:20:36 +00:00
|
|
|
int len, i, j, k, op_len;
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
/* check validity of iclog pointers */
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
icptr = log->l_iclog;
|
2013-10-30 21:01:00 +00:00
|
|
|
for (i = 0; i < log->l_iclog_bufs; i++, icptr = icptr->ic_next)
|
|
|
|
ASSERT(icptr);
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
if (icptr != log->l_iclog)
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: corrupt iclog ring", __func__);
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* check log magic numbers */
|
2011-07-08 12:36:05 +00:00
|
|
|
if (iclog->ic_header.h_magicno != cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: invalid magic num", __func__);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2015-06-21 23:44:47 +00:00
|
|
|
base_ptr = ptr = &iclog->ic_header;
|
|
|
|
p = &iclog->ic_header;
|
|
|
|
for (ptr += BBSIZE; ptr < base_ptr + count; ptr += BBSIZE) {
|
2011-07-08 12:36:05 +00:00
|
|
|
if (*(__be32 *)ptr == cpu_to_be32(XLOG_HEADER_MAGIC_NUM))
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_emerg(log->l_mp, "%s: unexpected magic num",
|
|
|
|
__func__);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* check fields */
|
2007-10-12 00:59:34 +00:00
|
|
|
len = be32_to_cpu(iclog->ic_header.h_num_logops);
|
2015-06-21 23:44:47 +00:00
|
|
|
base_ptr = ptr = iclog->ic_datap;
|
|
|
|
ophead = ptr;
|
2008-11-28 03:23:38 +00:00
|
|
|
xhdr = iclog->ic_data;
|
2005-04-16 22:20:36 +00:00
|
|
|
for (i = 0; i < len; i++) {
|
2015-06-21 23:44:47 +00:00
|
|
|
ophead = ptr;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* clientid is only 1 byte */
|
2015-06-21 23:44:47 +00:00
|
|
|
p = &ophead->oh_clientid;
|
|
|
|
field_offset = p - base_ptr;
|
2019-06-29 02:27:24 +00:00
|
|
|
if (field_offset & 0x1ff) {
|
2005-04-16 22:20:36 +00:00
|
|
|
clientid = ophead->oh_clientid;
|
|
|
|
} else {
|
2022-04-21 00:35:53 +00:00
|
|
|
idx = BTOBBT((void *)&ophead->oh_clientid - iclog->ic_datap);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
|
|
|
|
j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
|
|
|
k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
2007-10-12 00:58:05 +00:00
|
|
|
clientid = xlog_get_client_id(
|
|
|
|
xhdr[j].hic_xheader.xh_cycle_data[k]);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2007-10-12 00:58:05 +00:00
|
|
|
clientid = xlog_get_client_id(
|
|
|
|
iclog->ic_header.h_cycle_data[idx]);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-21 00:36:15 +00:00
|
|
|
if (clientid != XFS_TRANSACTION && clientid != XFS_LOG) {
|
2011-03-06 23:01:35 +00:00
|
|
|
xfs_warn(log->l_mp,
|
2022-04-21 00:36:15 +00:00
|
|
|
"%s: op %d invalid clientid %d op "PTR_FMT" offset 0x%lx",
|
|
|
|
__func__, i, clientid, ophead,
|
2011-03-06 23:01:35 +00:00
|
|
|
(unsigned long)field_offset);
|
2022-04-21 00:36:15 +00:00
|
|
|
}
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/* check length */
|
2015-06-21 23:44:47 +00:00
|
|
|
p = &ophead->oh_len;
|
|
|
|
field_offset = p - base_ptr;
|
2019-06-29 02:27:24 +00:00
|
|
|
if (field_offset & 0x1ff) {
|
2007-10-12 00:58:59 +00:00
|
|
|
op_len = be32_to_cpu(ophead->oh_len);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2022-04-21 00:35:53 +00:00
|
|
|
idx = BTOBBT((void *)&ophead->oh_len - iclog->ic_datap);
|
2005-04-16 22:20:36 +00:00
|
|
|
if (idx >= (XLOG_HEADER_CYCLE_SIZE / BBSIZE)) {
|
|
|
|
j = idx / (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
|
|
|
k = idx % (XLOG_HEADER_CYCLE_SIZE / BBSIZE);
|
2007-10-12 00:59:34 +00:00
|
|
|
op_len = be32_to_cpu(xhdr[j].hic_xheader.xh_cycle_data[k]);
|
2005-04-16 22:20:36 +00:00
|
|
|
} else {
|
2007-10-12 00:59:34 +00:00
|
|
|
op_len = be32_to_cpu(iclog->ic_header.h_cycle_data[idx]);
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ptr += sizeof(xlog_op_header_t) + op_len;
|
|
|
|
}
|
2020-03-26 01:18:24 +00:00
|
|
|
}
|
2005-11-02 04:12:04 +00:00
|
|
|
#endif
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
* Perform a forced shutdown on the log.
|
|
|
|
*
|
|
|
|
* This can be called from low level log code to trigger a shutdown, or from the
|
|
|
|
* high level mount shutdown code when the mount shuts down.
|
2010-05-17 05:51:59 +00:00
|
|
|
*
|
2021-08-11 01:00:39 +00:00
|
|
|
* Our main objectives here are to make sure that:
|
|
|
|
* a. if the shutdown was not due to a log IO error, flush the logs to
|
|
|
|
* disk. Anything modified after this is ignored.
|
|
|
|
* b. the log gets atomically marked 'XLOG_IO_ERROR' for all interested
|
|
|
|
* parties to find out. Nothing new gets queued after this is done.
|
|
|
|
* c. Tasks sleeping on log reservations, pinned objects and
|
|
|
|
* other resources get woken up.
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
* d. The mount is also marked as shut down so that log triggered shutdowns
|
|
|
|
* still behave the same as if they called xfs_forced_shutdown().
|
2021-08-11 00:59:01 +00:00
|
|
|
*
|
2021-08-11 01:00:39 +00:00
|
|
|
* Return true if the shutdown cause was a log IO error and we actually shut the
|
|
|
|
* log down.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2021-08-11 01:00:39 +00:00
|
|
|
bool
|
|
|
|
xlog_force_shutdown(
|
|
|
|
struct xlog *log,
|
2022-04-21 00:47:38 +00:00
|
|
|
uint32_t shutdown_flags)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
2021-08-11 01:00:39 +00:00
|
|
|
bool log_error = (shutdown_flags & SHUTDOWN_LOG_IO_ERROR);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-03-30 01:22:02 +00:00
|
|
|
if (!log)
|
2021-08-11 01:00:39 +00:00
|
|
|
return false;
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2010-05-17 05:51:59 +00:00
|
|
|
/*
|
2014-10-01 23:02:28 +00:00
|
|
|
* Flush all the completed transactions to disk before marking the log
|
2021-08-11 01:00:39 +00:00
|
|
|
* being shut down. We need to do this first as shutting down the log
|
|
|
|
* before the force will prevent the log force from flushing the iclogs
|
|
|
|
* to disk.
|
|
|
|
*
|
2022-03-30 01:22:02 +00:00
|
|
|
* When we are in recovery, there are no transactions to flush, and
|
|
|
|
* we don't want to touch the log because we don't want to perturb the
|
|
|
|
* current head/tail for future recovery attempts. Hence we need to
|
|
|
|
* avoid a log force in this case.
|
|
|
|
*
|
|
|
|
* If we are shutting down due to a log IO error, then we must avoid
|
|
|
|
* trying to write the log as that may just result in more IO errors and
|
|
|
|
* an endless shutdown/force loop.
|
2010-05-17 05:51:59 +00:00
|
|
|
*/
|
2022-03-30 01:22:02 +00:00
|
|
|
if (!log_error && !xlog_in_recovery(log))
|
2021-08-11 01:00:39 +00:00
|
|
|
xfs_log_force(log->l_mp, XFS_LOG_SYNC);
|
2010-05-17 05:51:59 +00:00
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2021-08-11 01:00:39 +00:00
|
|
|
* Atomically set the shutdown state. If the shutdown state is already
|
|
|
|
* set, there someone else is performing the shutdown and so we are done
|
|
|
|
* here. This should never happen because we should only ever get called
|
|
|
|
* once by the first shutdown caller.
|
|
|
|
*
|
|
|
|
* Much of the log state machine transitions assume that shutdown state
|
|
|
|
* cannot change once they hold the log->l_icloglock. Hence we need to
|
|
|
|
* hold that lock here, even though we use the atomic test_and_set_bit()
|
|
|
|
* operation to set the shutdown state.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 01:00:39 +00:00
|
|
|
if (test_and_set_bit(XLOG_IO_ERROR, &log->l_opstate)) {
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
return false;
|
|
|
|
}
|
2007-10-11 07:37:10 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
xfs: log shutdown triggers should only shut down the log
We've got a mess on our hands.
1. xfs_trans_commit() cannot cancel transactions because the mount is
shut down - that causes dirty, aborted, unlogged log items to sit
unpinned in memory and potentially get written to disk before the
log is shut down. Hence xfs_trans_commit() can only abort
transactions when xlog_is_shutdown() is true.
2. xfs_force_shutdown() is used in places to cause the current
modification to be aborted via xfs_trans_commit() because it may be
impractical or impossible to cancel the transaction directly, and
hence xfs_trans_commit() must cancel transactions when
xfs_is_shutdown() is true in this situation. But we can't do that
because of #1.
3. Log IO errors cause log shutdowns by calling xfs_force_shutdown()
to shut down the mount and then the log from log IO completion.
4. xfs_force_shutdown() can result in a log force being issued,
which has to wait for log IO completion before it will mark the log
as shut down. If #3 races with some other shutdown trigger that runs
a log force, we rely on xfs_force_shutdown() silently ignoring #3
and avoiding shutting down the log until the failed log force
completes.
5. To ensure #2 always works, we have to ensure that
xfs_force_shutdown() does not return until the the log is shut down.
But in the case of #4, this will result in a deadlock because the
log Io completion will block waiting for a log force to complete
which is blocked waiting for log IO to complete....
So the very first thing we have to do here to untangle this mess is
dissociate log shutdown triggers from mount shutdowns. We already
have xlog_forced_shutdown, which will atomically transistion to the
log a shutdown state. Due to internal asserts it cannot be called
multiple times, but was done simply because the only place that
could call it was xfs_do_force_shutdown() (i.e. the mount shutdown!)
and that could only call it once and once only. So the first thing
we do is remove the asserts.
We then convert all the internal log shutdown triggers to call
xlog_force_shutdown() directly instead of xfs_force_shutdown(). This
allows the log shutdown triggers to shut down the log without
needing to care about mount based shutdown constraints. This means
we shut down the log independently of the mount and the mount may
not notice this until it's next attempt to read or modify metadata.
At that point (e.g. xfs_trans_commit()) it will see that the log is
shutdown, error out and shutdown the mount.
To ensure that all the unmount behaviours and asserts track
correctly as a result of a log shutdown, propagate the shutdown up
to the mount if it is not already set. This keeps the mount and log
state in sync, and saves a huge amount of hassle where code fails
because of a log shutdown but only checks for mount shutdowns and
hence ends up doing the wrong thing. Cleaning up that mess is
an exercise for another day.
This enables us to address the other problems noted above in
followup patches.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Signed-off-by: Darrick J. Wong <djwong@kernel.org>
2022-03-30 01:22:01 +00:00
|
|
|
/*
|
|
|
|
* If this log shutdown also sets the mount shutdown state, issue a
|
|
|
|
* shutdown warning message.
|
|
|
|
*/
|
|
|
|
if (!test_and_set_bit(XFS_OPSTATE_SHUTDOWN, &log->l_mp->m_opstate)) {
|
|
|
|
xfs_alert_tag(log->l_mp, XFS_PTAG_SHUTDOWN_LOGERROR,
|
|
|
|
"Filesystem has been shut down due to log error (0x%x).",
|
|
|
|
shutdown_flags);
|
|
|
|
xfs_alert(log->l_mp,
|
|
|
|
"Please unmount the filesystem and rectify the problem(s).");
|
|
|
|
if (xfs_error_level >= XFS_ERRLEVEL_HIGH)
|
|
|
|
xfs_stack_trace();
|
|
|
|
}
|
|
|
|
|
2005-04-16 22:20:36 +00:00
|
|
|
/*
|
2010-12-21 01:02:25 +00:00
|
|
|
* We don't want anybody waiting for log reservations after this. That
|
|
|
|
* means we have to wake up everybody queued up on reserveq as well as
|
|
|
|
* writeq. In addition, we make sure in xlog_{re}grant_log_space that
|
|
|
|
* we don't enqueue anything once the SHUTDOWN flag is set, and this
|
2010-12-21 01:29:01 +00:00
|
|
|
* action is protected by the grant locks.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2012-02-20 02:31:27 +00:00
|
|
|
xlog_grant_head_wake_all(&log->l_reserve_head);
|
|
|
|
xlog_grant_head_wake_all(&log->l_write_head);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
|
|
|
/*
|
2014-05-06 22:05:50 +00:00
|
|
|
* Wake up everybody waiting on xfs_log_force. Wake the CIL push first
|
|
|
|
* as if the log writes were completed. The abort handling in the log
|
|
|
|
* item committed callback functions will do this again under lock to
|
|
|
|
* avoid races.
|
2005-04-16 22:20:36 +00:00
|
|
|
*/
|
2019-09-06 00:32:48 +00:00
|
|
|
spin_lock(&log->l_cilp->xc_push_lock);
|
2021-08-11 01:00:44 +00:00
|
|
|
wake_up_all(&log->l_cilp->xc_start_wait);
|
2014-05-06 22:05:50 +00:00
|
|
|
wake_up_all(&log->l_cilp->xc_commit_wait);
|
2019-09-06 00:32:48 +00:00
|
|
|
spin_unlock(&log->l_cilp->xc_push_lock);
|
2022-03-30 01:22:00 +00:00
|
|
|
|
|
|
|
spin_lock(&log->l_icloglock);
|
2021-08-11 01:00:40 +00:00
|
|
|
xlog_state_shutdown_callbacks(log);
|
2022-03-30 01:22:00 +00:00
|
|
|
spin_unlock(&log->l_icloglock);
|
2005-04-16 22:20:36 +00:00
|
|
|
|
2022-03-30 01:22:01 +00:00
|
|
|
wake_up_var(&log->l_opstate);
|
2021-08-11 01:00:39 +00:00
|
|
|
return log_error;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
|
|
|
|
2005-06-21 05:36:52 +00:00
|
|
|
STATIC int
|
2012-06-14 14:22:16 +00:00
|
|
|
xlog_iclogs_empty(
|
|
|
|
struct xlog *log)
|
2005-04-16 22:20:36 +00:00
|
|
|
{
|
|
|
|
xlog_in_core_t *iclog;
|
|
|
|
|
|
|
|
iclog = log->l_iclog;
|
|
|
|
do {
|
|
|
|
/* endianness does not matter here, zero is zero in
|
|
|
|
* any language.
|
|
|
|
*/
|
|
|
|
if (iclog->ic_header.h_num_logops)
|
2006-01-15 01:37:08 +00:00
|
|
|
return 0;
|
2005-04-16 22:20:36 +00:00
|
|
|
iclog = iclog->ic_next;
|
|
|
|
} while (iclog != log->l_iclog);
|
2006-01-15 01:37:08 +00:00
|
|
|
return 1;
|
2005-04-16 22:20:36 +00:00
|
|
|
}
|
2012-10-08 10:56:02 +00:00
|
|
|
|
2015-10-12 04:59:25 +00:00
|
|
|
/*
|
|
|
|
* Verify that an LSN stamped into a piece of metadata is valid. This is
|
|
|
|
* intended for use in read verifiers on v5 superblocks.
|
|
|
|
*/
|
|
|
|
bool
|
|
|
|
xfs_log_check_lsn(
|
|
|
|
struct xfs_mount *mp,
|
|
|
|
xfs_lsn_t lsn)
|
|
|
|
{
|
|
|
|
struct xlog *log = mp->m_log;
|
|
|
|
bool valid;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* norecovery mode skips mount-time log processing and unconditionally
|
|
|
|
* resets the in-core LSN. We can't validate in this mode, but
|
|
|
|
* modifications are not allowed anyways so just return true.
|
|
|
|
*/
|
2021-08-19 01:46:52 +00:00
|
|
|
if (xfs_has_norecovery(mp))
|
2015-10-12 04:59:25 +00:00
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some metadata LSNs are initialized to NULL (e.g., the agfl). This is
|
|
|
|
* handled by recovery and thus safe to ignore here.
|
|
|
|
*/
|
|
|
|
if (lsn == NULLCOMMITLSN)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
valid = xlog_valid_lsn(mp->m_log, lsn);
|
|
|
|
|
|
|
|
/* warn the user about what's gone wrong before verifier failure */
|
|
|
|
if (!valid) {
|
|
|
|
spin_lock(&log->l_icloglock);
|
|
|
|
xfs_warn(mp,
|
|
|
|
"Corruption warning: Metadata has LSN (%d:%d) ahead of current LSN (%d:%d). "
|
|
|
|
"Please unmount and run xfs_repair (>= v4.3) to resolve.",
|
|
|
|
CYCLE_LSN(lsn), BLOCK_LSN(lsn),
|
|
|
|
log->l_curr_cycle, log->l_curr_block);
|
|
|
|
spin_unlock(&log->l_icloglock);
|
|
|
|
}
|
|
|
|
|
|
|
|
return valid;
|
|
|
|
}
|
2018-08-01 14:40:48 +00:00
|
|
|
|
2021-08-08 15:27:12 +00:00
|
|
|
/*
|
|
|
|
* Notify the log that we're about to start using a feature that is protected
|
|
|
|
* by a log incompat feature flag. This will prevent log covering from
|
|
|
|
* clearing those flags.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
xlog_use_incompat_feat(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
down_read(&log->l_incompat_users);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Notify the log that we've finished using log incompat features. */
|
|
|
|
void
|
|
|
|
xlog_drop_incompat_feat(
|
|
|
|
struct xlog *log)
|
|
|
|
{
|
|
|
|
up_read(&log->l_incompat_users);
|
|
|
|
}
|