From ba00946328ba6a31657388a057d6972ad94bb511 Mon Sep 17 00:00:00 2001 From: Serapheim Dimitropoulos Date: Tue, 21 Jul 2020 17:39:58 -0700 Subject: [PATCH] Slub helpers should be aware of multi-node kmem caches On a system with more than 1 nodes per cache: ``` sdb> addr nr_node_ids | deref (unsigned int)2 ``` Before this patch: ``` sdb> spl_kmem_caches -s active_memory | head 1 | spl_kmem_caches name entry_size active_objs active_memory source total_memory util ------------------- ---------- ----------- ------------- ------------------------- ------------ ---- arc_buf_hdr_t_full 328 8956159 2.7GB arc_buf_hdr_t_full[SLUB] 1.3GB 203 ``` In the output above the SLUB helpers are only looking at the first node of the kmem cache and undercount the amount of active objects in the SLUB layer and thus reporting less total_memory. As a result the SPL that explicitly tracks the counts of those allocations shows the correct number of active objects resulting to the above inconsistency where active_memory is more than total_memory and util% is more than 100%. After this patch: ``` sdb> spl_kmem_caches -s active_memory | head 1 | spl_kmem_caches name entry_size active_objs active_memory source total_memory util ------------------- ---------- ----------- ------------- ------------------------- ------------ ---- arc_buf_hdr_t_full 328 9250638 2.8GB arc_buf_hdr_t_full[SLUB] 2.8GB 99 ``` The behavior is corrected. --- sdb/commands/linux/internal/slub_helpers.py | 30 +++++++++++++++------ 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/sdb/commands/linux/internal/slub_helpers.py b/sdb/commands/linux/internal/slub_helpers.py index 82f4629c..8903d550 100644 --- a/sdb/commands/linux/internal/slub_helpers.py +++ b/sdb/commands/linux/internal/slub_helpers.py @@ -44,9 +44,18 @@ def for_each_child_cache(root_cache: drgn.Object) -> Iterable[drgn.Object]: "memcg_params.children_node") +def for_each_node(cache: drgn.Object) -> Iterable[drgn.Object]: + assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' + node_num = sdb.get_object('nr_node_ids') + for i in range(node_num): + yield cache.node[i] + + def nr_slabs(cache: drgn.Object) -> int: assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' - nslabs: int = cache.node[0].nr_slabs.counter.value_() + nslabs = 0 + for node in for_each_node(cache): + nslabs += node.nr_slabs.counter.value_() if is_root_cache(cache): for child in for_each_child_cache(cache): nslabs += nr_slabs(child) @@ -78,7 +87,9 @@ def total_memory(cache: drgn.Object) -> int: def objs(cache: drgn.Object) -> int: assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' - count: int = cache.node[0].total_objects.counter.value_() + count = 0 + for node in for_each_node(cache): + count += node.total_objects.counter.value_() if is_root_cache(cache): for child in for_each_child_cache(cache): count += objs(child) @@ -87,10 +98,12 @@ def objs(cache: drgn.Object) -> int: def inactive_objs(cache: drgn.Object) -> int: assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' - node = cache.node[0].partial # assumption nr_node_ids == 0 free = 0 - for page in list_for_each_entry("struct page", node.address_of_(), "lru"): - free += page.objects.value_() - page.inuse.value_() + for node in for_each_node(cache): + node_partial = node.partial + for page in list_for_each_entry("struct page", + node_partial.address_of_(), "lru"): + free += page.objects.value_() - page.inuse.value_() if is_root_cache(cache): for child in for_each_child_cache(cache): free += inactive_objs(child) @@ -178,9 +191,10 @@ def for_each_freeobj_in_slab(cache: drgn.Object, def for_each_partial_slab_in_cache(cache: drgn.Object) -> Iterable[drgn.Object]: assert sdb.type_canonical_name(cache.type_) == 'struct kmem_cache *' - - node = cache.node[0].partial # assumption nr_node_ids == 0 - yield from list_for_each_entry("struct page", node.address_of_(), "lru") + for node in for_each_node(cache): + node_partial = node.partial + yield from list_for_each_entry("struct page", + node_partial.address_of_(), "lru") if is_root_cache(cache): for child in for_each_child_cache(cache):