Skip to content

Commit

Permalink
Introduce limit on size of L2ARC headers
Browse files Browse the repository at this point in the history
Since L2ARC buffers are not evicted on memory pressure, too large
amount of headers on system with irrationally large L2ARC can render
it slow or even unusable.  This change limits L2ARC writes and
rebuild if unevictable L2ARC-only headers reach dangerous level.

While there, call arc_adapt() on L2ARC rebuild, so that it could
properly grow arc_c, reflecting potentially significant ARC size
increase and avoiding slow growth with hopeless eviction attempts
later when "overflow" is detected.

Reviewed-by: Ryan Moeller <[email protected]>
Reviewed-by: Brian Behlendorf <[email protected]>
Reported-by: Richard Elling <[email protected]>
Signed-off-by: Alexander Motin <[email protected]>
Closes openzfs#10765
  • Loading branch information
amotin authored Aug 25, 2020
1 parent 47a3f3f commit 523e129
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 5 deletions.
14 changes: 14 additions & 0 deletions man/man5/zfs-module-parameters.5
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,20 @@ feature.
Default value: \fB200\fR%.
.RE

.sp
.ne 2
.na
\fBl2arc_meta_percent\fR (int)
.ad
.RS 12n
Percent of ARC size allowed for L2ARC-only headers.
Since L2ARC buffers are not evicted on memory pressure, too large amount of
headers on system with irrationaly large L2ARC can render it slow or unusable.
This parameter limits L2ARC writes and rebuild to achieve it.
.sp
Default value: \fB33\fR%.
.RE

.sp
.ne 2
.na
Expand Down
27 changes: 22 additions & 5 deletions module/zfs/arc.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ unsigned long l2arc_feed_min_ms = L2ARC_FEED_MIN_MS; /* min interval msecs */
int l2arc_noprefetch = B_TRUE; /* don't cache prefetch bufs */
int l2arc_feed_again = B_TRUE; /* turbo warmup */
int l2arc_norw = B_FALSE; /* no reads during writes */
int l2arc_meta_percent = 33; /* limit on headers size */

/*
* L2ARC Internals
Expand Down Expand Up @@ -4988,9 +4989,6 @@ arc_adapt(int bytes, arc_state_t *state)
int64_t mrug_size = zfs_refcount_count(&arc_mru_ghost->arcs_size);
int64_t mfug_size = zfs_refcount_count(&arc_mfu_ghost->arcs_size);

if (state == arc_l2c_only)
return;

ASSERT(bytes > 0);
/*
* Adapt the target size of the MRU list:
Expand Down Expand Up @@ -9144,6 +9142,15 @@ l2arc_write_buffers(spa_t *spa, l2arc_dev_t *dev, uint64_t target_sz)
return (write_asize);
}

static boolean_t
l2arc_hdr_limit_reached(void)
{
int64_t s = aggsum_upper_bound(&astat_l2_hdr_size);

return (arc_reclaim_needed() || (s > arc_meta_limit * 3 / 4) ||
(s > (arc_warm ? arc_c : arc_c_max) * l2arc_meta_percent / 100));
}

/*
* This thread feeds the L2ARC at regular intervals. This is the beating
* heart of the L2ARC.
Expand Down Expand Up @@ -9211,7 +9218,7 @@ l2arc_feed_thread(void *unused)
/*
* Avoid contributing to memory pressure.
*/
if (arc_reclaim_needed()) {
if (l2arc_hdr_limit_reached()) {
ARCSTAT_BUMP(arcstat_l2_abort_lowmem);
spa_config_exit(spa, SCL_L2ARC, dev);
continue;
Expand Down Expand Up @@ -9662,7 +9669,7 @@ l2arc_rebuild(l2arc_dev_t *dev)
* online the L2ARC dev at a later time (or re-import the pool)
* to reconstruct it (when there's less memory pressure).
*/
if (arc_reclaim_needed()) {
if (l2arc_hdr_limit_reached()) {
ARCSTAT_BUMP(arcstat_l2_rebuild_abort_lowmem);
cmn_err(CE_NOTE, "System running low on memory, "
"aborting L2ARC rebuild.");
Expand Down Expand Up @@ -10002,6 +10009,13 @@ l2arc_log_blk_restore(l2arc_dev_t *dev, const l2arc_log_blk_phys_t *lb,
uint64_t size = 0, asize = 0;
uint64_t log_entries = dev->l2ad_log_entries;

/*
* Usually arc_adapt() is called only for data, not headers, but
* since we may allocate significant amount of memory here, let ARC
* grow its arc_c.
*/
arc_adapt(log_entries * HDR_L2ONLY_SIZE, arc_l2c_only);

for (int i = log_entries - 1; i >= 0; i--) {
/*
* Restore goes in the reverse temporal direction to preserve
Expand Down Expand Up @@ -10538,6 +10552,9 @@ ZFS_MODULE_PARAM(zfs_l2arc, l2arc_, feed_again, INT, ZMOD_RW,
ZFS_MODULE_PARAM(zfs_l2arc, l2arc_, norw, INT, ZMOD_RW,
"No reads during writes");

ZFS_MODULE_PARAM(zfs_l2arc, l2arc_, meta_percent, INT, ZMOD_RW,
"Percent of ARC size allowed for L2ARC-only headers");

ZFS_MODULE_PARAM(zfs_l2arc, l2arc_, rebuild_enabled, INT, ZMOD_RW,
"Rebuild the L2ARC when importing a pool");

Expand Down

0 comments on commit 523e129

Please sign in to comment.