mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-25 02:49:32 +03:00
Prioritize "metadata" in arc_get_data_buf
When the arc is at it's size limit and a new buffer is added, data will be evicted (or recycled) from the arc to make room for this new buffer. As far as I can tell, this is to try and keep the arc from over stepping it's bounds (i.e. keep it below the size limitation placed on it). This makes sense conceptually, but there appears to be a subtle flaw in its current implementation, resulting in metadata buffers being throttled. When it evicts from the arc's lists, it also passes in a "type" so as to remove a buffer of the same type that it is adding. The problem with this is that once the size limit is hit, the ratio of "metadata" to "data" contained in the arc essentially becomes fixed. For example, consider the following scenario: * the size of the arc is capped at 10G * the meta_limit is capped at 4G * 9G of the arc contains "data" * 1G of the arc contains "metadata" Now, every time a new "metadata" buffer is created and added to the arc, an older "metadata" buffer(s) will be removed from the arc; preserving the 9G "data" to 1G "metadata" ratio that was in-place when the size limit was reached. This occurs even though the amount of "metadata" is far below the "metadata" limit. This can result in the arc behaving pathologically for certain workloads. To fix this, the arc_get_data_buf function was modified to evict "data" from the arc even when adding a "metadata" buffer; unless it's at the "metadata" limit. In addition, arc_evict now more closely resembles arc_evict_ghost; such that when evicting "data" from the arc, it may make a second pass over the arc lists and evict "metadata" if it cannot meet the eviction size the first time around. Signed-off-by: Prakash Surya <surya1@llnl.gov> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Issue #2110
This commit is contained in:
parent
77765b540b
commit
da8ccd0ee0
@ -1890,6 +1890,7 @@ arc_evict(arc_state_t *state, uint64_t spa, int64_t bytes, boolean_t recycle,
|
||||
|
||||
evicted_state = (state == arc_mru) ? arc_mru_ghost : arc_mfu_ghost;
|
||||
|
||||
top:
|
||||
mutex_enter(&state->arcs_mtx);
|
||||
mutex_enter(&evicted_state->arcs_mtx);
|
||||
|
||||
@ -2005,6 +2006,15 @@ arc_evict(arc_state_t *state, uint64_t spa, int64_t bytes, boolean_t recycle,
|
||||
mutex_exit(&evicted_state->arcs_mtx);
|
||||
mutex_exit(&state->arcs_mtx);
|
||||
|
||||
if (list == &state->arcs_list[ARC_BUFC_DATA] &&
|
||||
(bytes < 0 || bytes_evicted < bytes)) {
|
||||
/* Prevent second pass from recycling metadata into data */
|
||||
recycle = FALSE;
|
||||
type = ARC_BUFC_METADATA;
|
||||
list = &state->arcs_list[type];
|
||||
goto top;
|
||||
}
|
||||
|
||||
if (bytes_evicted < bytes)
|
||||
dprintf("only evicted %lld bytes from %x\n",
|
||||
(longlong_t)bytes_evicted, state);
|
||||
@ -2146,16 +2156,9 @@ arc_adjust(void)
|
||||
adjustment = MIN((int64_t)(arc_size - arc_c),
|
||||
(int64_t)(arc_anon->arcs_size + arc_mru->arcs_size - arc_p));
|
||||
|
||||
if (adjustment > 0 && arc_mru->arcs_lsize[ARC_BUFC_DATA] > 0) {
|
||||
delta = MIN(arc_mru->arcs_lsize[ARC_BUFC_DATA], adjustment);
|
||||
if (adjustment > 0 && arc_mru->arcs_size > 0) {
|
||||
delta = MIN(arc_mru->arcs_size, adjustment);
|
||||
(void) arc_evict(arc_mru, 0, delta, FALSE, ARC_BUFC_DATA);
|
||||
adjustment -= delta;
|
||||
}
|
||||
|
||||
if (adjustment > 0 && arc_mru->arcs_lsize[ARC_BUFC_METADATA] > 0) {
|
||||
delta = MIN(arc_mru->arcs_lsize[ARC_BUFC_METADATA], adjustment);
|
||||
(void) arc_evict(arc_mru, 0, delta, FALSE,
|
||||
ARC_BUFC_METADATA);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2164,17 +2167,9 @@ arc_adjust(void)
|
||||
|
||||
adjustment = arc_size - arc_c;
|
||||
|
||||
if (adjustment > 0 && arc_mfu->arcs_lsize[ARC_BUFC_DATA] > 0) {
|
||||
delta = MIN(adjustment, arc_mfu->arcs_lsize[ARC_BUFC_DATA]);
|
||||
if (adjustment > 0 && arc_mfu->arcs_size > 0) {
|
||||
delta = MIN(arc_mfu->arcs_size, adjustment);
|
||||
(void) arc_evict(arc_mfu, 0, delta, FALSE, ARC_BUFC_DATA);
|
||||
adjustment -= delta;
|
||||
}
|
||||
|
||||
if (adjustment > 0 && arc_mfu->arcs_lsize[ARC_BUFC_METADATA] > 0) {
|
||||
int64_t delta = MIN(adjustment,
|
||||
arc_mfu->arcs_lsize[ARC_BUFC_METADATA]);
|
||||
(void) arc_evict(arc_mfu, 0, delta, FALSE,
|
||||
ARC_BUFC_METADATA);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2752,6 +2747,8 @@ arc_get_data_buf(arc_buf_t *buf)
|
||||
arc_state_t *state = buf->b_hdr->b_state;
|
||||
uint64_t size = buf->b_hdr->b_size;
|
||||
arc_buf_contents_t type = buf->b_hdr->b_type;
|
||||
arc_buf_contents_t evict = ARC_BUFC_DATA;
|
||||
boolean_t recycle = TRUE;
|
||||
|
||||
arc_adapt(size, state);
|
||||
|
||||
@ -2792,7 +2789,24 @@ arc_get_data_buf(arc_buf_t *buf)
|
||||
mfu_space > arc_mfu->arcs_size) ? arc_mru : arc_mfu;
|
||||
}
|
||||
|
||||
if ((buf->b_data = arc_evict(state, 0, size, TRUE, type)) == NULL) {
|
||||
/*
|
||||
* Evict data buffers prior to metadata buffers, unless we're
|
||||
* over the metadata limit and adding a metadata buffer.
|
||||
*/
|
||||
if (type == ARC_BUFC_METADATA) {
|
||||
if (arc_meta_used >= arc_meta_limit)
|
||||
evict = ARC_BUFC_METADATA;
|
||||
else
|
||||
/*
|
||||
* In this case, we're evicting data while
|
||||
* adding metadata. Thus, to prevent recycling a
|
||||
* data buffer into a metadata buffer, recycling
|
||||
* is disabled in the following arc_evict call.
|
||||
*/
|
||||
recycle = FALSE;
|
||||
}
|
||||
|
||||
if ((buf->b_data = arc_evict(state, 0, size, recycle, evict)) == NULL) {
|
||||
if (type == ARC_BUFC_METADATA) {
|
||||
buf->b_data = zio_buf_alloc(size);
|
||||
arc_space_consume(size, ARC_SPACE_DATA);
|
||||
@ -2803,7 +2817,9 @@ arc_get_data_buf(arc_buf_t *buf)
|
||||
* via the prune callback to drop references. The
|
||||
* prune callback in run in the context of the reclaim
|
||||
* thread to avoid deadlocking on the hash_lock.
|
||||
* Of course, only do this when recycle is true.
|
||||
*/
|
||||
if (recycle)
|
||||
cv_signal(&arc_reclaim_thr_cv);
|
||||
} else {
|
||||
ASSERT(type == ARC_BUFC_DATA);
|
||||
@ -2812,6 +2828,8 @@ arc_get_data_buf(arc_buf_t *buf)
|
||||
atomic_add_64(&arc_size, size);
|
||||
}
|
||||
|
||||
/* Only bump this if we tried to recycle and failed */
|
||||
if (recycle)
|
||||
ARCSTAT_BUMP(arcstat_recycle_miss);
|
||||
}
|
||||
ASSERT(buf->b_data != NULL);
|
||||
|
Loading…
Reference in New Issue
Block a user