At the moment, the slab objects are charged to the memcg at the allocation time. However there are cases where slab objects are allocated at the time where the right target memcg to charge it to is not known. One such case is the network sockets for the incoming connection which are allocated in the softirq context. Couple hundred thousand connections are very normal on large loaded server and almost all of those sockets underlying those connections get allocated in the softirq context and thus not charged to any memcg. However later at the accept() time we know the right target memcg to charge. Let's add new API to charge already allocated objects, so we can have better accounting of the memory usage. Signed-off-by: Shakeel Butt <shakeel.butt@xxxxxxxxx> --- This is RFC to get early comments and I still have to measure the performance impact of this charging. Particularly I am planning to test neper's tcp_crr with this patch. include/linux/slab.h | 1 + mm/slub.c | 39 +++++++++++++++++++++++++++++++++ net/ipv4/inet_connection_sock.c | 1 + 3 files changed, 41 insertions(+) diff --git a/include/linux/slab.h b/include/linux/slab.h index 512e7c844b7f..a8b09b0ca066 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -547,6 +547,7 @@ void *kmem_cache_alloc_lru_noprof(struct kmem_cache *s, struct list_lru *lru, gfp_t gfpflags) __assume_slab_alignment __malloc; #define kmem_cache_alloc_lru(...) alloc_hooks(kmem_cache_alloc_lru_noprof(__VA_ARGS__)) +bool kmem_cache_post_charge(void *objp, gfp_t gfpflags); void kmem_cache_free(struct kmem_cache *s, void *objp); kmem_buckets *kmem_buckets_create(const char *name, slab_flags_t flags, diff --git a/mm/slub.c b/mm/slub.c index b6b947596e26..574122ad89b8 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2189,6 +2189,16 @@ void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p, __memcg_slab_free_hook(s, slab, p, objects, obj_exts); } + +static __fastpath_inline +bool memcg_slab_post_charge(struct kmem_cache *s, void *p, gfp_t flags) +{ + if (likely(!memcg_kmem_online())) + return true; + + return __memcg_slab_post_alloc_hook(s, NULL, flags, 1, &p); +} + #else /* CONFIG_MEMCG */ static inline bool memcg_slab_post_alloc_hook(struct kmem_cache *s, struct list_lru *lru, @@ -2202,6 +2212,13 @@ static inline void memcg_slab_free_hook(struct kmem_cache *s, struct slab *slab, void **p, int objects) { } + +static inline bool memcg_slab_post_charge(struct kmem_cache *s, + void *p, + gfp_t flags) +{ + return true; +} #endif /* CONFIG_MEMCG */ #ifdef CONFIG_SLUB_RCU_DEBUG @@ -4110,6 +4127,28 @@ void *kmem_cache_alloc_lru_noprof(struct kmem_cache *s, struct list_lru *lru, } EXPORT_SYMBOL(kmem_cache_alloc_lru_noprof); +bool kmem_cache_post_charge(void *objp, gfp_t gfpflags) +{ + struct folio *folio; + struct slab *slab; + struct kmem_cache *s; + + folio = virt_to_folio(objp); + if (unlikely(!folio_test_slab(folio))) + return false; + + slab = folio_slab(folio); + s = slab->slab_cache; + + /* Ignore KMALLOC_NORMAL cache */ + if (s->flags & SLAB_KMALLOC && + !(s->flags & (SLAB_CACHE_DMA|SLAB_ACCOUNT|SLAB_RECLAIM_ACCOUNT))) + return true; + + return memcg_slab_post_charge(s, objp, gfpflags); +} +EXPORT_SYMBOL(kmem_cache_post_charge); + /** * kmem_cache_alloc_node - Allocate an object on the specified node * @s: The cache to allocate from. diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 64d07b842e73..f707bb76e24d 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -733,6 +733,7 @@ struct sock *inet_csk_accept(struct sock *sk, struct proto_accept_arg *arg) if (amt) mem_cgroup_charge_skmem(newsk->sk_memcg, amt, GFP_KERNEL | __GFP_NOFAIL); + kmem_cache_post_charge(newsk, GFP_KERNEL | __GFP_NOFAIL); release_sock(newsk); } -- 2.43.5