Skip to content

Commit 9adf402

Browse files
Dave ChinnerChandan Babu R
authored andcommitted
xfs: AIL doesn't need manual pushing
We have a mechanism that checks the amount of log space remaining available every time we make a transaction reservation. If the amount of space is below a threshold (25% free) we push on the AIL to tell it to do more work. To do this, we end up calculating the LSN that the AIL needs to push to on every reservation and updating the push target for the AIL with that new target LSN. This is silly and expensive. The AIL is perfectly capable of calculating the push target itself, and it will always be running when the AIL contains objects. What the target does is determine if the AIL needs to do any work before it goes back to sleep. If we haven't run out of reservation space or memory (or some other push all trigger), it will simply go back to sleep for a while if there is more than 25% of the journal space free without doing anything. If there are items in the AIL at a lower LSN than the target, it will try to push up to the target or to the point of getting stuck before going back to sleep and trying again soon after.` Hence we can modify the AIL to calculate it's own 25% push target before it starts a push using the same reserve grant head based calculation as is currently used, and remove all the places where we ask the AIL to push to a new 25% free target. We can also drop the minimum free space size of 256BBs from the calculation because the 25% of a minimum sized log is *always going to be larger than 256BBs. This does still require a manual push in certain circumstances. These circumstances arise when the AIL is not full, but the reservation grants consume the entire of the free space in the log. In this case, we still need to push on the AIL to free up space, so when we hit this condition (i.e. reservation going to sleep to wait on log space) we do a single push to tell the AIL it should empty itself. This will keep the AIL moving as new reservations come in and want more space, rather than keep queuing them and having to push the AIL repeatedly. The reason for using the "push all" when grant space runs out is that we can run out of grant space when there is more than 25% of the log free. Small logs are notorious for this, and we have a hack in the log callback code (xlog_state_set_callback()) where we push the AIL because the *head* moved) to ensure that we kick the AIL when we consume space in it because that can push us over the "less than 25% available" available that starts tail pushing back up again. Hence when we run out of grant space and are going to sleep, we have to consider that the grant space may be consuming almost all the log space and there is almost nothing in the AIL. In this situation, the AIL pins the tail and moving the tail forwards is the only way the grant space will come available, so we have to force the AIL to push everything to guarantee grant space will eventually be returned. Hence triggering a "push all" just before sleeping removes all the nasty corner cases we have in other parts of the code that work around the "we didn't ask the AIL to push enough to free grant space" condition that leads to log space hangs... Signed-off-by: Dave Chinner <[email protected]> Reviewed-by: Darrick J. Wong <[email protected]> Signed-off-by: Chandan Babu R <[email protected]>
1 parent 613e2fd commit 9adf402

File tree

6 files changed

+108
-229
lines changed

6 files changed

+108
-229
lines changed

fs/xfs/libxfs/xfs_defer.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
#include "xfs_mount.h"
1313
#include "xfs_defer.h"
1414
#include "xfs_trans.h"
15+
#include "xfs_trans_priv.h"
1516
#include "xfs_buf_item.h"
1617
#include "xfs_inode.h"
1718
#include "xfs_inode_item.h"
1819
#include "xfs_trace.h"
1920
#include "xfs_icache.h"
2021
#include "xfs_log.h"
22+
#include "xfs_log_priv.h"
2123
#include "xfs_rmap.h"
2224
#include "xfs_refcount.h"
2325
#include "xfs_bmap.h"
@@ -556,7 +558,7 @@ xfs_defer_relog(
556558
* the log threshold once per call.
557559
*/
558560
if (threshold_lsn == NULLCOMMITLSN) {
559-
threshold_lsn = xlog_grant_push_threshold(log, 0);
561+
threshold_lsn = xfs_ail_push_target(log->l_ailp);
560562
if (threshold_lsn == NULLCOMMITLSN)
561563
break;
562564
}

fs/xfs/xfs_log.c

Lines changed: 5 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,6 @@ xlog_alloc_log(
3030
struct xfs_buftarg *log_target,
3131
xfs_daddr_t blk_offset,
3232
int num_bblks);
33-
STATIC int
34-
xlog_space_left(
35-
struct xlog *log,
36-
atomic64_t *head);
3733
STATIC void
3834
xlog_dealloc_log(
3935
struct xlog *log);
@@ -51,10 +47,6 @@ xlog_state_get_iclog_space(
5147
struct xlog_ticket *ticket,
5248
int *logoffsetp);
5349
STATIC void
54-
xlog_grant_push_ail(
55-
struct xlog *log,
56-
int need_bytes);
57-
STATIC void
5850
xlog_sync(
5951
struct xlog *log,
6052
struct xlog_in_core *iclog,
@@ -242,42 +234,15 @@ xlog_grant_head_wake(
242234
{
243235
struct xlog_ticket *tic;
244236
int need_bytes;
245-
bool woken_task = false;
246237

247238
list_for_each_entry(tic, &head->waiters, t_queue) {
248-
249-
/*
250-
* There is a chance that the size of the CIL checkpoints in
251-
* progress at the last AIL push target calculation resulted in
252-
* limiting the target to the log head (l_last_sync_lsn) at the
253-
* time. This may not reflect where the log head is now as the
254-
* CIL checkpoints may have completed.
255-
*
256-
* Hence when we are woken here, it may be that the head of the
257-
* log that has moved rather than the tail. As the tail didn't
258-
* move, there still won't be space available for the
259-
* reservation we require. However, if the AIL has already
260-
* pushed to the target defined by the old log head location, we
261-
* will hang here waiting for something else to update the AIL
262-
* push target.
263-
*
264-
* Therefore, if there isn't space to wake the first waiter on
265-
* the grant head, we need to push the AIL again to ensure the
266-
* target reflects both the current log tail and log head
267-
* position before we wait for the tail to move again.
268-
*/
269-
270239
need_bytes = xlog_ticket_reservation(log, head, tic);
271-
if (*free_bytes < need_bytes) {
272-
if (!woken_task)
273-
xlog_grant_push_ail(log, need_bytes);
240+
if (*free_bytes < need_bytes)
274241
return false;
275-
}
276242

277243
*free_bytes -= need_bytes;
278244
trace_xfs_log_grant_wake_up(log, tic);
279245
wake_up_process(tic->t_task);
280-
woken_task = true;
281246
}
282247

283248
return true;
@@ -296,13 +261,15 @@ xlog_grant_head_wait(
296261
do {
297262
if (xlog_is_shutdown(log))
298263
goto shutdown;
299-
xlog_grant_push_ail(log, need_bytes);
300264

301265
__set_current_state(TASK_UNINTERRUPTIBLE);
302266
spin_unlock(&head->lock);
303267

304268
XFS_STATS_INC(log->l_mp, xs_sleep_logspace);
305269

270+
/* Push on the AIL to free up all the log space. */
271+
xfs_ail_push_all(log->l_ailp);
272+
306273
trace_xfs_log_grant_sleep(log, tic);
307274
schedule();
308275
trace_xfs_log_grant_wake(log, tic);
@@ -418,9 +385,6 @@ xfs_log_regrant(
418385
* of rolling transactions in the log easily.
419386
*/
420387
tic->t_tid++;
421-
422-
xlog_grant_push_ail(log, tic->t_unit_res);
423-
424388
tic->t_curr_res = tic->t_unit_res;
425389
if (tic->t_cnt > 0)
426390
return 0;
@@ -477,12 +441,7 @@ xfs_log_reserve(
477441
ASSERT(*ticp == NULL);
478442
tic = xlog_ticket_alloc(log, unit_bytes, cnt, permanent);
479443
*ticp = tic;
480-
481-
xlog_grant_push_ail(log, tic->t_cnt ? tic->t_unit_res * tic->t_cnt
482-
: tic->t_unit_res);
483-
484444
trace_xfs_log_reserve(log, tic);
485-
486445
error = xlog_grant_head_check(log, &log->l_reserve_head, tic,
487446
&need_bytes);
488447
if (error)
@@ -1330,7 +1289,7 @@ xlog_assign_tail_lsn(
13301289
* shortcut invalidity asserts in this case so that we don't trigger them
13311290
* falsely.
13321291
*/
1333-
STATIC int
1292+
int
13341293
xlog_space_left(
13351294
struct xlog *log,
13361295
atomic64_t *head)
@@ -1667,89 +1626,6 @@ xlog_alloc_log(
16671626
return ERR_PTR(error);
16681627
} /* xlog_alloc_log */
16691628

1670-
/*
1671-
* Compute the LSN that we'd need to push the log tail towards in order to have
1672-
* (a) enough on-disk log space to log the number of bytes specified, (b) at
1673-
* least 25% of the log space free, and (c) at least 256 blocks free. If the
1674-
* log free space already meets all three thresholds, this function returns
1675-
* NULLCOMMITLSN.
1676-
*/
1677-
xfs_lsn_t
1678-
xlog_grant_push_threshold(
1679-
struct xlog *log,
1680-
int need_bytes)
1681-
{
1682-
xfs_lsn_t threshold_lsn = 0;
1683-
xfs_lsn_t last_sync_lsn;
1684-
int free_blocks;
1685-
int free_bytes;
1686-
int threshold_block;
1687-
int threshold_cycle;
1688-
int free_threshold;
1689-
1690-
ASSERT(BTOBB(need_bytes) < log->l_logBBsize);
1691-
1692-
free_bytes = xlog_space_left(log, &log->l_reserve_head.grant);
1693-
free_blocks = BTOBBT(free_bytes);
1694-
1695-
/*
1696-
* Set the threshold for the minimum number of free blocks in the
1697-
* log to the maximum of what the caller needs, one quarter of the
1698-
* log, and 256 blocks.
1699-
*/
1700-
free_threshold = BTOBB(need_bytes);
1701-
free_threshold = max(free_threshold, (log->l_logBBsize >> 2));
1702-
free_threshold = max(free_threshold, 256);
1703-
if (free_blocks >= free_threshold)
1704-
return NULLCOMMITLSN;
1705-
1706-
xlog_crack_atomic_lsn(&log->l_tail_lsn, &threshold_cycle,
1707-
&threshold_block);
1708-
threshold_block += free_threshold;
1709-
if (threshold_block >= log->l_logBBsize) {
1710-
threshold_block -= log->l_logBBsize;
1711-
threshold_cycle += 1;
1712-
}
1713-
threshold_lsn = xlog_assign_lsn(threshold_cycle,
1714-
threshold_block);
1715-
/*
1716-
* Don't pass in an lsn greater than the lsn of the last
1717-
* log record known to be on disk. Use a snapshot of the last sync lsn
1718-
* so that it doesn't change between the compare and the set.
1719-
*/
1720-
last_sync_lsn = atomic64_read(&log->l_last_sync_lsn);
1721-
if (XFS_LSN_CMP(threshold_lsn, last_sync_lsn) > 0)
1722-
threshold_lsn = last_sync_lsn;
1723-
1724-
return threshold_lsn;
1725-
}
1726-
1727-
/*
1728-
* Push the tail of the log if we need to do so to maintain the free log space
1729-
* thresholds set out by xlog_grant_push_threshold. We may need to adopt a
1730-
* policy which pushes on an lsn which is further along in the log once we
1731-
* reach the high water mark. In this manner, we would be creating a low water
1732-
* mark.
1733-
*/
1734-
STATIC void
1735-
xlog_grant_push_ail(
1736-
struct xlog *log,
1737-
int need_bytes)
1738-
{
1739-
xfs_lsn_t threshold_lsn;
1740-
1741-
threshold_lsn = xlog_grant_push_threshold(log, need_bytes);
1742-
if (threshold_lsn == NULLCOMMITLSN || xlog_is_shutdown(log))
1743-
return;
1744-
1745-
/*
1746-
* Get the transaction layer to kick the dirty buffers out to
1747-
* disk asynchronously. No point in trying to do this if
1748-
* the filesystem is shutting down.
1749-
*/
1750-
xfs_ail_push(log->l_ailp, threshold_lsn);
1751-
}
1752-
17531629
/*
17541630
* Stamp cycle number in every block
17551631
*/
@@ -2712,7 +2588,6 @@ xlog_state_set_callback(
27122588
return;
27132589

27142590
atomic64_set(&log->l_last_sync_lsn, header_lsn);
2715-
xlog_grant_push_ail(log, 0);
27162591
}
27172592

27182593
/*

fs/xfs/xfs_log.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ int xfs_log_quiesce(struct xfs_mount *mp);
156156
void xfs_log_clean(struct xfs_mount *mp);
157157
bool xfs_log_check_lsn(struct xfs_mount *, xfs_lsn_t);
158158

159-
xfs_lsn_t xlog_grant_push_threshold(struct xlog *log, int need_bytes);
160159
bool xlog_force_shutdown(struct xlog *log, uint32_t shutdown_flags);
161160

162161
int xfs_attr_use_log_assist(struct xfs_mount *mp);

fs/xfs/xfs_log_priv.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ xlog_assign_grant_head(atomic64_t *head, int cycle, int space)
575575
atomic64_set(head, xlog_assign_grant_head_val(cycle, space));
576576
}
577577

578+
int xlog_space_left(struct xlog *log, atomic64_t *head);
579+
578580
/*
579581
* Committed Item List interfaces
580582
*/

0 commit comments

Comments
 (0)