Skip to content

Commit ca0bf58

Browse files
Prakash Suryabehlendorf
authored andcommitted
Illumos 5497 - lock contention on arcs_mtx
Reviewed by: George Wilson <[email protected]> Reviewed by: Matthew Ahrens <[email protected]> Reviewed by: Richard Elling <[email protected]> Approved by: Dan McDonald <[email protected]> Porting notes and other significant code changes: The illumos 5368 patch (ARC should cache more metadata), which was never picked up by ZoL, is mostly reverted by this patch. Since ZoL relies on the kernel asynchronously calling the shrinker to actually reap memory, the shrinker wakes up arc_reclaim_waiters_cv every time it runs. The arc_adapt_thread() function no longer calls arc_do_user_evicts() since the newly-added arc_user_evicts_thread() calls it periodically. Notable conflicting ZoL commits which conflicted with this patch or whose effects are either duplicated or un-done by this patch: 302f753 - Integrate ARC more tightly with Linux 39e055c - Adjust arc_p based on "bytes" in arc_shrink f521ce1 - Allow "arc_p" to drop to zero or grow to "arc_c" 77765b5 - Remove "arc_meta_used" from arc_adjust calculation 94520ca - Prune metadata from ghost lists in arc_adjust_meta Trace support for multilist_insert() and multilist_remove() has been added and produces the following output: fio-12498 [077] .... 112936.448324: zfs_multilist__insert: ml { offset 240 numsublists 80 sublistidx 63 } fio-12498 [077] .... 112936.448347: zfs_multilist__remove: ml { offset 240 numsublists 80 sublistidx 29 } The following arcstats have been removed: recycle_miss - Used by arcstat.py and arc_summary.py, both of which have been updated appropriately. l2_writes_hdr_miss The following arcstats have been added: evict_not_enough - Number of times arc_evict_state() was unable to evict enough buffers to reach its target amount. evict_l2_skip - Number of times arc_evict_hdr() skipped eviction because it was being written to the l2arc. l2_writes_lock_retry - Replaces l2_writes_hdr_miss. Number of times l2arc_write_done() failed to acquire hash_lock (and re-tries). arc_meta_min - Shows the value of the zfs_arc_meta_min module parameter (see below). The "index" column of the "dbuf" kstat has been removed since it doesn't have a direct analog in the new multilist scheme. Additional multilist- related stats could be added in the future but would likely require extensions to the mulilist API. The following module parameters have been added: zfs_arc_evict_batch_limit - Number of ARC headers to free per sub-list before moving on to the next sub-list. zfs_arc_meta_min - Enforce a floor on the amount of metadata in the ARC. zfs_arc_num_sublists_per_state - Number of multilist sub-lists per ARC state. zfs_arc_overflow_shift - Controls amount by which the ARC must exceed the target size to be considered "overflowing". Ported-by: Tim Chase <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]
1 parent b9541d6 commit ca0bf58

File tree

17 files changed

+1940
-758
lines changed

17 files changed

+1940
-758
lines changed

cmd/arc_summary/arc_summary.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,10 @@ def get_arc_summary(Kstat):
191191
### ARC Misc. ###
192192
deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
193193
mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
194-
recycle_miss = Kstat["kstat.zfs.misc.arcstats.recycle_miss"]
195194

196195
### ARC Misc. ###
197196
output["arc_misc"] = {}
198197
output["arc_misc"]["deleted"] = fHits(deleted)
199-
output["arc_misc"]['recycle_miss'] = fHits(recycle_miss)
200198
output["arc_misc"]['mutex_miss'] = fHits(mutex_miss)
201199
output["arc_misc"]['evict_skips'] = fHits(mutex_miss)
202200

@@ -302,8 +300,6 @@ def _arc_summary(Kstat):
302300
### ARC Misc. ###
303301
sys.stdout.write("ARC Misc:\n")
304302
sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
305-
sys.stdout.write("\tRecycle Misses:\t\t\t\t%s\n" %
306-
arc['arc_misc']['recycle_miss'])
307303
sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
308304
arc['arc_misc']['mutex_miss'])
309305
sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" %

cmd/arcstat/arcstat.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@
8282
"mrug": [4, 1000, "MRU Ghost List hits per second"],
8383
"eskip": [5, 1000, "evict_skip per second"],
8484
"mtxmis": [6, 1000, "mutex_miss per second"],
85-
"rmis": [4, 1000, "recycle_miss per second"],
8685
"dread": [5, 1000, "Demand accesses per second"],
8786
"pread": [5, 1000, "Prefetch accesses per second"],
8887
"l2hits": [6, 1000, "L2ARC hits per second"],
@@ -406,7 +405,6 @@ def calculate():
406405
v["mrug"] = d["mru_ghost_hits"] / sint
407406
v["mfug"] = d["mfu_ghost_hits"] / sint
408407
v["eskip"] = d["evict_skip"] / sint
409-
v["rmis"] = d["recycle_miss"] / sint
410408
v["mtxmis"] = d["mutex_miss"] / sint
411409

412410
if l2exist:

include/sys/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ COMMON_H = \
3333
$(top_srcdir)/include/sys/efi_partition.h \
3434
$(top_srcdir)/include/sys/metaslab.h \
3535
$(top_srcdir)/include/sys/metaslab_impl.h \
36+
$(top_srcdir)/include/sys/multilist.h \
3637
$(top_srcdir)/include/sys/nvpair.h \
3738
$(top_srcdir)/include/sys/nvpair_impl.h \
3839
$(top_srcdir)/include/sys/range_tree.h \
@@ -53,6 +54,7 @@ COMMON_H = \
5354
$(top_srcdir)/include/sys/trace_dbuf.h \
5455
$(top_srcdir)/include/sys/trace_dmu.h \
5556
$(top_srcdir)/include/sys/trace_dnode.h \
57+
$(top_srcdir)/include/sys/trace_multilist.h \
5658
$(top_srcdir)/include/sys/trace_txg.h \
5759
$(top_srcdir)/include/sys/trace_zil.h \
5860
$(top_srcdir)/include/sys/trace_zrlock.h \

include/sys/arc.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ extern "C" {
3838
#include <sys/spa.h>
3939
#include <sys/refcount.h>
4040

41+
/*
42+
* Used by arc_flush() to inform arc_evict_state() that it should evict
43+
* all available buffers from the arc state being passed in.
44+
*/
45+
#define ARC_EVICT_ALL -1ULL
46+
4147
typedef struct arc_buf_hdr arc_buf_hdr_t;
4248
typedef struct arc_buf arc_buf_t;
4349
typedef struct arc_prune arc_prune_t;
@@ -146,7 +152,6 @@ typedef enum arc_state_type {
146152
typedef struct arc_buf_info {
147153
arc_state_type_t abi_state_type;
148154
arc_buf_contents_t abi_state_contents;
149-
uint64_t abi_state_index;
150155
uint32_t abi_flags;
151156
uint32_t abi_datacnt;
152157
uint64_t abi_size;
@@ -200,7 +205,7 @@ void arc_freed(spa_t *spa, const blkptr_t *bp);
200205
void arc_set_callback(arc_buf_t *buf, arc_evict_func_t *func, void *private);
201206
boolean_t arc_clear_callback(arc_buf_t *buf);
202207

203-
void arc_flush(spa_t *spa);
208+
void arc_flush(spa_t *spa, boolean_t retry);
204209
void arc_tempreserve_clear(uint64_t reserve);
205210
int arc_tempreserve_space(uint64_t reserve, uint64_t txg);
206211

include/sys/arc_impl.h

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,22 @@ extern "C" {
6767
*/
6868

6969
typedef struct arc_state {
70-
list_t arcs_list[ARC_BUFC_NUMTYPES]; /* list of evictable buffers */
71-
uint64_t arcs_lsize[ARC_BUFC_NUMTYPES]; /* amount of evictable data */
72-
uint64_t arcs_size; /* total amount of data in this state */
73-
kmutex_t arcs_mtx;
70+
/*
71+
* list of evictable buffers
72+
*/
73+
multilist_t arcs_list[ARC_BUFC_NUMTYPES];
74+
/*
75+
* total amount of evictable data in this state
76+
*/
77+
uint64_t arcs_lsize[ARC_BUFC_NUMTYPES];
78+
/*
79+
* total amount of data in this state; this includes: evictable,
80+
* non-evictable, ARC_BUFC_DATA, and ARC_BUFC_METADATA.
81+
*/
82+
uint64_t arcs_size;
83+
/*
84+
* supports the "dbufs" kstat
85+
*/
7486
arc_state_type_t arcs_state;
7587
} arc_state_t;
7688

@@ -136,7 +148,7 @@ typedef struct l1arc_buf_hdr {
136148

137149
/* protected by arc state mutex */
138150
arc_state_t *b_state;
139-
list_node_t b_arc_node;
151+
multilist_node_t b_arc_node;
140152

141153
/* updated atomically */
142154
clock_t b_arc_access;

include/sys/multilist.h

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* This file and its contents are supplied under the terms of the
5+
* Common Development and Distribution License ("CDDL"), version 1.0.
6+
* You may only use this file in accordance with the terms of version
7+
* 1.0 of the CDDL.
8+
*
9+
* A full copy of the text of the CDDL should have accompanied this
10+
* source. A copy of the CDDL is also available via the Internet at
11+
* http://www.illumos.org/license/CDDL.
12+
*
13+
* CDDL HEADER END
14+
*/
15+
/*
16+
* Copyright (c) 2013, 2014 by Delphix. All rights reserved.
17+
*/
18+
19+
#ifndef _SYS_MULTILIST_H
20+
#define _SYS_MULTILIST_H
21+
22+
#include <sys/zfs_context.h>
23+
24+
#ifdef __cplusplus
25+
extern "C" {
26+
#endif
27+
28+
typedef list_node_t multilist_node_t;
29+
typedef struct multilist multilist_t;
30+
typedef struct multilist_sublist multilist_sublist_t;
31+
typedef unsigned int multilist_sublist_index_func_t(multilist_t *, void *);
32+
33+
struct multilist_sublist {
34+
/*
35+
* The mutex used internally to implement thread safe insertions
36+
* and removals to this individual sublist. It can also be locked
37+
* by a consumer using multilist_sublist_{lock,unlock}, which is
38+
* useful if a consumer needs to traverse the list in a thread
39+
* safe manner.
40+
*/
41+
kmutex_t mls_lock;
42+
/*
43+
* The actual list object containing all objects in this sublist.
44+
*/
45+
list_t mls_list;
46+
/*
47+
* Pad to cache line, in an effort to try and prevent cache line
48+
* contention.
49+
*/
50+
} ____cacheline_aligned;
51+
52+
struct multilist {
53+
/*
54+
* This is used to get to the multilist_node_t structure given
55+
* the void *object contained on the list.
56+
*/
57+
size_t ml_offset;
58+
/*
59+
* The number of sublists used internally by this multilist.
60+
*/
61+
uint64_t ml_num_sublists;
62+
/*
63+
* The array of pointers to the actual sublists.
64+
*/
65+
multilist_sublist_t *ml_sublists;
66+
/*
67+
* Pointer to function which determines the sublist to use
68+
* when inserting and removing objects from this multilist.
69+
* Please see the comment above multilist_create for details.
70+
*/
71+
multilist_sublist_index_func_t *ml_index_func;
72+
};
73+
74+
void multilist_destroy(multilist_t *);
75+
void multilist_create(multilist_t *, size_t, size_t, unsigned int,
76+
multilist_sublist_index_func_t *);
77+
78+
void multilist_insert(multilist_t *, void *);
79+
void multilist_remove(multilist_t *, void *);
80+
int multilist_is_empty(multilist_t *);
81+
82+
unsigned int multilist_get_num_sublists(multilist_t *);
83+
unsigned int multilist_get_random_index(multilist_t *);
84+
85+
multilist_sublist_t *multilist_sublist_lock(multilist_t *, unsigned int);
86+
void multilist_sublist_unlock(multilist_sublist_t *);
87+
88+
void multilist_sublist_insert_head(multilist_sublist_t *, void *);
89+
void multilist_sublist_insert_tail(multilist_sublist_t *, void *);
90+
void multilist_sublist_move_forward(multilist_sublist_t *mls, void *obj);
91+
void multilist_sublist_remove(multilist_sublist_t *, void *);
92+
93+
void *multilist_sublist_head(multilist_sublist_t *);
94+
void *multilist_sublist_tail(multilist_sublist_t *);
95+
void *multilist_sublist_next(multilist_sublist_t *, void *);
96+
void *multilist_sublist_prev(multilist_sublist_t *, void *);
97+
98+
void multilist_link_init(multilist_node_t *);
99+
int multilist_link_active(multilist_node_t *);
100+
101+
#ifdef __cplusplus
102+
}
103+
#endif
104+
105+
#endif /* _SYS_MULTILIST_H */

include/sys/trace_multilist.h

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9+
* or http://www.opensolaris.org/os/licensing.
10+
* See the License for the specific language governing permissions
11+
* and limitations under the License.
12+
*
13+
* When distributing Covered Code, include this CDDL HEADER in each
14+
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15+
* If applicable, add the following below this CDDL HEADER, with the
16+
* fields enclosed by brackets "[]" replaced with your own identifying
17+
* information: Portions Copyright [yyyy] [name of copyright owner]
18+
*
19+
* CDDL HEADER END
20+
*/
21+
22+
#if defined(_KERNEL) && defined(HAVE_DECLARE_EVENT_CLASS)
23+
24+
#undef TRACE_SYSTEM
25+
#define TRACE_SYSTEM zfs
26+
27+
#if !defined(_TRACE_MULTILIST_H) || defined(TRACE_HEADER_MULTI_READ)
28+
#define _TRACE_MULTILIST_H
29+
30+
#include <linux/tracepoint.h>
31+
#include <sys/types.h>
32+
33+
/*
34+
* Generic support for three argument tracepoints of the form:
35+
*
36+
* DTRACE_PROBE3(...,
37+
* multilist_t *, ...,
38+
* unsigned int, ...,
39+
* void *, ...);
40+
*/
41+
42+
DECLARE_EVENT_CLASS(zfs_multilist_insert_remove_class,
43+
TP_PROTO(multilist_t *ml, unsigned sublist_idx, void *obj),
44+
TP_ARGS(ml, sublist_idx, obj),
45+
TP_STRUCT__entry(
46+
__field(size_t, ml_offset)
47+
__field(uint64_t, ml_num_sublists)
48+
49+
__field(unsigned int, sublist_idx)
50+
),
51+
TP_fast_assign(
52+
__entry->ml_offset = ml->ml_offset;
53+
__entry->ml_num_sublists = ml->ml_num_sublists;
54+
55+
__entry->sublist_idx = sublist_idx;
56+
),
57+
TP_printk("ml { offset %ld numsublists %llu sublistidx %u } ",
58+
__entry->ml_offset, __entry->ml_num_sublists, __entry->sublist_idx)
59+
);
60+
61+
#define DEFINE_MULTILIST_INSERT_REMOVE_EVENT(name) \
62+
DEFINE_EVENT(zfs_multilist_insert_remove_class, name, \
63+
TP_PROTO(multilist_t *ml, unsigned int sublist_idx, void *obj), \
64+
TP_ARGS(ml, sublist_idx, obj))
65+
DEFINE_MULTILIST_INSERT_REMOVE_EVENT(zfs_multilist__insert);
66+
DEFINE_MULTILIST_INSERT_REMOVE_EVENT(zfs_multilist__remove);
67+
68+
#endif /* _TRACE_MULTILIST_H */
69+
70+
#undef TRACE_INCLUDE_PATH
71+
#undef TRACE_INCLUDE_FILE
72+
#define TRACE_INCLUDE_PATH sys
73+
#define TRACE_INCLUDE_FILE trace_multilist
74+
#include <trace/define_trace.h>
75+
76+
#endif /* _KERNEL && HAVE_DECLARE_EVENT_CLASS */

include/sys/zfs_context.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ extern void delay(clock_t ticks);
609609
} while (0);
610610

611611
#define max_ncpus 64
612+
#define num_online_cpus() (sysconf(_SC_NPROCESSORS_ONLN))
612613

613614
#define minclsyspri 60
614615
#define maxclsyspri 99

lib/libzpool/Makefile.am

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ libzpool_la_SOURCES = \
5555
$(top_srcdir)/module/zfs/lzjb.c \
5656
$(top_srcdir)/module/zfs/lz4.c \
5757
$(top_srcdir)/module/zfs/metaslab.c \
58+
$(top_srcdir)/module/zfs/multilist.c \
5859
$(top_srcdir)/module/zfs/range_tree.c \
5960
$(top_srcdir)/module/zfs/refcount.c \
6061
$(top_srcdir)/module/zfs/rrwlock.c \

man/man5/zfs-module-parameters.5

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,19 @@ increased to reduce the memory footprint.
347347
Default value: \fB8192\fR.
348348
.RE
349349

350+
.sp
351+
.ne 2
352+
.na
353+
\fBzfs_arc_evict_batch_limit\fR (int)
354+
.ad
355+
.RS 12n
356+
Number ARC headers to evict per sub-list before proceding to another sub-list.
357+
This batch-style operation prevents entire sub-lists from being evicted at once
358+
but comes at a cost of additional unlocking and locking.
359+
.sp
360+
Default value: \fB10\fR.
361+
.RE
362+
350363
.sp
351364
.ne 2
352365
.na
@@ -395,6 +408,19 @@ for meta data.
395408
Default value: \fB0\fR.
396409
.RE
397410

411+
.sp
412+
.ne 2
413+
.na
414+
\fBzfs_arc_meta_min\fR (ulong)
415+
.ad
416+
.RS 12n
417+
The minimum allowed size in bytes that meta data buffers may consume in
418+
the ARC. This value defaults to 0 which disables a floor on the amount
419+
of the ARC devoted meta data.
420+
.sp
421+
Default value: \fB0\fR.
422+
.RE
423+
398424
.sp
399425
.ne 2
400426
.na
@@ -447,6 +473,40 @@ Min life of prefetch block
447473
Default value: \fB100\fR.
448474
.RE
449475

476+
.sp
477+
.ne 2
478+
.na
479+
\fBzfs_arc_num_sublists_per_state\fR (int)
480+
.ad
481+
.RS 12n
482+
To allow more fine-grained locking, each ARC state contains a series
483+
of lists for both data and meta data objects. Locking is performed at
484+
the level of these "sub-lists". This parameters controls the number of
485+
sub-lists per ARC state.
486+
.sp
487+
Default value: 1 or the number of on-online CPUs, whichever is greater
488+
.RE
489+
490+
.sp
491+
.ne 2
492+
.na
493+
\fBzfs_arc_overflow_shift\fR (int)
494+
.ad
495+
.RS 12n
496+
The ARC size is considered to be overflowing if it exceeds the current
497+
ARC target size (arc_c) by a threshold determined by this parameter.
498+
The threshold is calculated as a fraction of arc_c using the formula
499+
"arc_c >> \fBzfs_arc_overflow_shift\fR".
500+
501+
The default value of 8 causes the ARC to be considered to be overflowing
502+
if it exceeds the target size by 1/256th (0.3%) of the target size.
503+
504+
When the ARC is overflowing, new buffer allocations are stalled until
505+
the reclaim thread catches up and the overflow condition no longer exists.
506+
.sp
507+
Default value: \fB8\fR.
508+
.RE
509+
450510
.sp
451511
.ne 2
452512
.na

0 commit comments

Comments
 (0)