Commit 9d7fef74 authored by Dave Chinner's avatar Dave Chinner Committed by Christoph Hellwig

xfs: inform the xfsaild of the push target before sleeping

When trying to reserve log space, we find the amount of space
we need, then go to sleep waiting for space. When we are
woken, we try to push the tail of the log forward to make
sure we have space available.

Unfortunately, this means that if there is not space available, and
everyone who needs space goes to sleep there is no-one left to push
the tail of the log to make space available. Once we have a thread
waiting for space to become available, the others queue up behind
it in a FIFO, and none of them push the tail of the log.

This can result in everyone going to sleep in xlog_grant_log_space()
if the first sleeper races with the last I/O that moves the tail
of the log forward. With no further I/O tomove the tail of the log,
there is nothing to wake the sleepers and hence all transactions
just stop.

Fix this by making sure the xfsaild will create enough space for the
transaction that is about to sleep by moving the push target far
enough forwards to ensure that that the curent proceeees will have
enough space available when it is woken. That is, we push the
AIL before we go to sleep.

Because we've inserted the log ticket into the queue before we've
pushed and gone to sleep, subsequent transactions will wait behind
this one. Hence we are guaranteed to have space available when we
are woken.
Signed-off-by: default avatarDave Chinner <david@fromorbit.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
parent c626d174
...@@ -2560,18 +2560,19 @@ redo: ...@@ -2560,18 +2560,19 @@ redo:
xlog_ins_ticketq(&log->l_reserve_headq, tic); xlog_ins_ticketq(&log->l_reserve_headq, tic);
xlog_trace_loggrant(log, tic, xlog_trace_loggrant(log, tic,
"xlog_grant_log_space: sleep 2"); "xlog_grant_log_space: sleep 2");
spin_unlock(&log->l_grant_lock);
xlog_grant_push_ail(log->l_mp, need_bytes);
spin_lock(&log->l_grant_lock);
XFS_STATS_INC(xs_sleep_logspace); XFS_STATS_INC(xs_sleep_logspace);
sv_wait(&tic->t_wait, PINOD|PLTWAIT, &log->l_grant_lock, s); sv_wait(&tic->t_wait, PINOD|PLTWAIT, &log->l_grant_lock, s);
if (XLOG_FORCED_SHUTDOWN(log)) {
spin_lock(&log->l_grant_lock); spin_lock(&log->l_grant_lock);
if (XLOG_FORCED_SHUTDOWN(log))
goto error_return; goto error_return;
}
xlog_trace_loggrant(log, tic, xlog_trace_loggrant(log, tic,
"xlog_grant_log_space: wake 2"); "xlog_grant_log_space: wake 2");
xlog_grant_push_ail(log->l_mp, need_bytes);
spin_lock(&log->l_grant_lock);
goto redo; goto redo;
} else if (tic->t_flags & XLOG_TIC_IN_Q) } else if (tic->t_flags & XLOG_TIC_IN_Q)
xlog_del_ticketq(&log->l_reserve_headq, tic); xlog_del_ticketq(&log->l_reserve_headq, tic);
...@@ -2650,7 +2651,7 @@ xlog_regrant_write_log_space(xlog_t *log, ...@@ -2650,7 +2651,7 @@ xlog_regrant_write_log_space(xlog_t *log,
* for more free space, otherwise try to get some space for * for more free space, otherwise try to get some space for
* this transaction. * this transaction.
*/ */
need_bytes = tic->t_unit_res;
if ((ntic = log->l_write_headq)) { if ((ntic = log->l_write_headq)) {
free_bytes = xlog_space_left(log, log->l_grant_write_cycle, free_bytes = xlog_space_left(log, log->l_grant_write_cycle,
log->l_grant_write_bytes); log->l_grant_write_bytes);
...@@ -2670,26 +2671,25 @@ xlog_regrant_write_log_space(xlog_t *log, ...@@ -2670,26 +2671,25 @@ xlog_regrant_write_log_space(xlog_t *log,
xlog_trace_loggrant(log, tic, xlog_trace_loggrant(log, tic,
"xlog_regrant_write_log_space: sleep 1"); "xlog_regrant_write_log_space: sleep 1");
spin_unlock(&log->l_grant_lock);
xlog_grant_push_ail(log->l_mp, need_bytes);
spin_lock(&log->l_grant_lock);
XFS_STATS_INC(xs_sleep_logspace); XFS_STATS_INC(xs_sleep_logspace);
sv_wait(&tic->t_wait, PINOD|PLTWAIT, sv_wait(&tic->t_wait, PINOD|PLTWAIT,
&log->l_grant_lock, s); &log->l_grant_lock, s);
/* If we're shutting down, this tic is already /* If we're shutting down, this tic is already
* off the queue */ * off the queue */
if (XLOG_FORCED_SHUTDOWN(log)) {
spin_lock(&log->l_grant_lock); spin_lock(&log->l_grant_lock);
if (XLOG_FORCED_SHUTDOWN(log))
goto error_return; goto error_return;
}
xlog_trace_loggrant(log, tic, xlog_trace_loggrant(log, tic,
"xlog_regrant_write_log_space: wake 1"); "xlog_regrant_write_log_space: wake 1");
xlog_grant_push_ail(log->l_mp, tic->t_unit_res);
spin_lock(&log->l_grant_lock);
} }
} }
need_bytes = tic->t_unit_res;
redo: redo:
if (XLOG_FORCED_SHUTDOWN(log)) if (XLOG_FORCED_SHUTDOWN(log))
goto error_return; goto error_return;
...@@ -2699,19 +2699,20 @@ redo: ...@@ -2699,19 +2699,20 @@ redo:
if (free_bytes < need_bytes) { if (free_bytes < need_bytes) {
if ((tic->t_flags & XLOG_TIC_IN_Q) == 0) if ((tic->t_flags & XLOG_TIC_IN_Q) == 0)
xlog_ins_ticketq(&log->l_write_headq, tic); xlog_ins_ticketq(&log->l_write_headq, tic);
spin_unlock(&log->l_grant_lock);
xlog_grant_push_ail(log->l_mp, need_bytes);
spin_lock(&log->l_grant_lock);
XFS_STATS_INC(xs_sleep_logspace); XFS_STATS_INC(xs_sleep_logspace);
sv_wait(&tic->t_wait, PINOD|PLTWAIT, &log->l_grant_lock, s); sv_wait(&tic->t_wait, PINOD|PLTWAIT, &log->l_grant_lock, s);
/* If we're shutting down, this tic is already off the queue */ /* If we're shutting down, this tic is already off the queue */
if (XLOG_FORCED_SHUTDOWN(log)) {
spin_lock(&log->l_grant_lock); spin_lock(&log->l_grant_lock);
if (XLOG_FORCED_SHUTDOWN(log))
goto error_return; goto error_return;
}
xlog_trace_loggrant(log, tic, xlog_trace_loggrant(log, tic,
"xlog_regrant_write_log_space: wake 2"); "xlog_regrant_write_log_space: wake 2");
xlog_grant_push_ail(log->l_mp, need_bytes);
spin_lock(&log->l_grant_lock);
goto redo; goto redo;
} else if (tic->t_flags & XLOG_TIC_IN_Q) } else if (tic->t_flags & XLOG_TIC_IN_Q)
xlog_del_ticketq(&log->l_write_headq, tic); xlog_del_ticketq(&log->l_write_headq, tic);
......
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