Commit d9f68777 authored by Dave Chinner's avatar Dave Chinner Committed by Dave Chinner

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: default avatarDave Chinner <dchinner@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <djwong@kernel.org>
parent 1ccb0745
...@@ -57,7 +57,8 @@ xlog_grant_push_ail( ...@@ -57,7 +57,8 @@ xlog_grant_push_ail(
STATIC void STATIC void
xlog_sync( xlog_sync(
struct xlog *log, struct xlog *log,
struct xlog_in_core *iclog); struct xlog_in_core *iclog,
struct xlog_ticket *ticket);
#if defined(DEBUG) #if defined(DEBUG)
STATIC void STATIC void
xlog_verify_grant_tail( xlog_verify_grant_tail(
...@@ -567,7 +568,8 @@ xlog_state_shutdown_callbacks( ...@@ -567,7 +568,8 @@ xlog_state_shutdown_callbacks(
int int
xlog_state_release_iclog( xlog_state_release_iclog(
struct xlog *log, struct xlog *log,
struct xlog_in_core *iclog) struct xlog_in_core *iclog,
struct xlog_ticket *ticket)
{ {
xfs_lsn_t tail_lsn; xfs_lsn_t tail_lsn;
bool last_ref; bool last_ref;
...@@ -614,7 +616,7 @@ xlog_state_release_iclog( ...@@ -614,7 +616,7 @@ xlog_state_release_iclog(
trace_xlog_iclog_syncing(iclog, _RET_IP_); trace_xlog_iclog_syncing(iclog, _RET_IP_);
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
xlog_sync(log, iclog); xlog_sync(log, iclog, ticket);
spin_lock(&log->l_icloglock); spin_lock(&log->l_icloglock);
return 0; return 0;
} }
...@@ -881,7 +883,7 @@ xlog_force_iclog( ...@@ -881,7 +883,7 @@ xlog_force_iclog(
iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA; iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA;
if (iclog->ic_state == XLOG_STATE_ACTIVE) if (iclog->ic_state == XLOG_STATE_ACTIVE)
xlog_state_switch_iclogs(iclog->ic_log, iclog, 0); xlog_state_switch_iclogs(iclog->ic_log, iclog, 0);
return xlog_state_release_iclog(iclog->ic_log, iclog); return xlog_state_release_iclog(iclog->ic_log, iclog, NULL);
} }
/* /*
...@@ -2027,7 +2029,8 @@ xlog_calc_iclog_size( ...@@ -2027,7 +2029,8 @@ xlog_calc_iclog_size(
STATIC void STATIC void
xlog_sync( xlog_sync(
struct xlog *log, struct xlog *log,
struct xlog_in_core *iclog) struct xlog_in_core *iclog,
struct xlog_ticket *ticket)
{ {
unsigned int count; /* byte count of bwrite */ unsigned int count; /* byte count of bwrite */
unsigned int roundoff; /* roundoff to BB or stripe */ unsigned int roundoff; /* roundoff to BB or stripe */
...@@ -2039,9 +2042,17 @@ xlog_sync( ...@@ -2039,9 +2042,17 @@ xlog_sync(
count = xlog_calc_iclog_size(log, iclog, &roundoff); count = xlog_calc_iclog_size(log, iclog, &roundoff);
/* move grant heads by roundoff in sync */ /*
* 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_reserve_head.grant, roundoff);
xlog_grant_add_space(log, &log->l_write_head.grant, roundoff); xlog_grant_add_space(log, &log->l_write_head.grant, roundoff);
}
/* put cycle number in every block */ /* put cycle number in every block */
xlog_pack_data(log, iclog, roundoff); xlog_pack_data(log, iclog, roundoff);
...@@ -2277,7 +2288,7 @@ xlog_write_get_more_iclog_space( ...@@ -2277,7 +2288,7 @@ xlog_write_get_more_iclog_space(
spin_lock(&log->l_icloglock); spin_lock(&log->l_icloglock);
ASSERT(iclog->ic_state == XLOG_STATE_WANT_SYNC); ASSERT(iclog->ic_state == XLOG_STATE_WANT_SYNC);
xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt); xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt);
error = xlog_state_release_iclog(log, iclog); error = xlog_state_release_iclog(log, iclog, ticket);
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
if (error) if (error)
return error; return error;
...@@ -2539,7 +2550,7 @@ xlog_write( ...@@ -2539,7 +2550,7 @@ xlog_write(
*/ */
spin_lock(&log->l_icloglock); spin_lock(&log->l_icloglock);
xlog_state_finish_copy(log, iclog, record_cnt, 0); xlog_state_finish_copy(log, iclog, record_cnt, 0);
error = xlog_state_release_iclog(log, iclog); error = xlog_state_release_iclog(log, iclog, ticket);
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
return error; return error;
...@@ -2959,7 +2970,7 @@ xlog_state_get_iclog_space( ...@@ -2959,7 +2970,7 @@ xlog_state_get_iclog_space(
* reference to the iclog. * reference to the iclog.
*/ */
if (!atomic_add_unless(&iclog->ic_refcnt, -1, 1)) if (!atomic_add_unless(&iclog->ic_refcnt, -1, 1))
error = xlog_state_release_iclog(log, iclog); error = xlog_state_release_iclog(log, iclog, ticket);
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
if (error) if (error)
return error; return error;
......
...@@ -1189,6 +1189,7 @@ xlog_cil_push_work( ...@@ -1189,6 +1189,7 @@ xlog_cil_push_work(
xfs_csn_t push_seq; xfs_csn_t push_seq;
bool push_commit_stable; bool push_commit_stable;
LIST_HEAD (whiteouts); LIST_HEAD (whiteouts);
struct xlog_ticket *ticket;
new_ctx = xlog_cil_ctx_alloc(); new_ctx = xlog_cil_ctx_alloc();
new_ctx->ticket = xlog_cil_ticket_alloc(log); new_ctx->ticket = xlog_cil_ticket_alloc(log);
...@@ -1323,7 +1324,14 @@ xlog_cil_push_work( ...@@ -1323,7 +1324,14 @@ xlog_cil_push_work(
if (error) if (error)
goto out_abort_free_ticket; goto out_abort_free_ticket;
xfs_log_ticket_ungrant(log, ctx->ticket); /*
* Grab the ticket from the ctx so we can ungrant it after releasing the
* commit_iclog. The ctx may be freed by the time we return from
* releasing the commit_iclog (i.e. checkpoint has been completed and
* callback run) so we can't reference the ctx after the call to
* xlog_state_release_iclog().
*/
ticket = ctx->ticket;
/* /*
* If the checkpoint spans multiple iclogs, wait for all previous iclogs * If the checkpoint spans multiple iclogs, wait for all previous iclogs
...@@ -1373,12 +1381,14 @@ xlog_cil_push_work( ...@@ -1373,12 +1381,14 @@ xlog_cil_push_work(
if (push_commit_stable && if (push_commit_stable &&
ctx->commit_iclog->ic_state == XLOG_STATE_ACTIVE) ctx->commit_iclog->ic_state == XLOG_STATE_ACTIVE)
xlog_state_switch_iclogs(log, ctx->commit_iclog, 0); xlog_state_switch_iclogs(log, ctx->commit_iclog, 0);
xlog_state_release_iclog(log, ctx->commit_iclog); ticket = ctx->ticket;
xlog_state_release_iclog(log, ctx->commit_iclog, ticket);
/* Not safe to reference ctx now! */ /* Not safe to reference ctx now! */
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
xlog_cil_cleanup_whiteouts(&whiteouts); xlog_cil_cleanup_whiteouts(&whiteouts);
xfs_log_ticket_ungrant(log, ticket);
return; return;
out_skip: out_skip:
...@@ -1388,17 +1398,19 @@ xlog_cil_push_work( ...@@ -1388,17 +1398,19 @@ xlog_cil_push_work(
return; return;
out_abort_free_ticket: out_abort_free_ticket:
xfs_log_ticket_ungrant(log, ctx->ticket);
ASSERT(xlog_is_shutdown(log)); ASSERT(xlog_is_shutdown(log));
xlog_cil_cleanup_whiteouts(&whiteouts); xlog_cil_cleanup_whiteouts(&whiteouts);
if (!ctx->commit_iclog) { if (!ctx->commit_iclog) {
xfs_log_ticket_ungrant(log, ctx->ticket);
xlog_cil_committed(ctx); xlog_cil_committed(ctx);
return; return;
} }
spin_lock(&log->l_icloglock); spin_lock(&log->l_icloglock);
xlog_state_release_iclog(log, ctx->commit_iclog); ticket = ctx->ticket;
xlog_state_release_iclog(log, ctx->commit_iclog, ticket);
/* Not safe to reference ctx now! */ /* Not safe to reference ctx now! */
spin_unlock(&log->l_icloglock); spin_unlock(&log->l_icloglock);
xfs_log_ticket_ungrant(log, ticket);
} }
/* /*
......
...@@ -515,7 +515,8 @@ void xfs_log_ticket_regrant(struct xlog *log, struct xlog_ticket *ticket); ...@@ -515,7 +515,8 @@ void xfs_log_ticket_regrant(struct xlog *log, struct xlog_ticket *ticket);
void xlog_state_switch_iclogs(struct xlog *log, struct xlog_in_core *iclog, void xlog_state_switch_iclogs(struct xlog *log, struct xlog_in_core *iclog,
int eventual_size); int eventual_size);
int xlog_state_release_iclog(struct xlog *log, struct xlog_in_core *iclog); int xlog_state_release_iclog(struct xlog *log, struct xlog_in_core *iclog,
struct xlog_ticket *ticket);
/* /*
* When we crack an atomic LSN, we sample it first so that the value will not * When we crack an atomic LSN, we sample it first so that the value will not
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment