On Fri, Sep 25, 2020 at 10:58 AM Shakeel Butt <shakeelb@xxxxxxxxxx> wrote: > [snip] > > I don't think you can ignore the flushing. The __free_once() in > ___cache_free() assumes there is a space available. > > BTW do_drain() also have the same issue. > > Why not move slabs_destroy() after we update ac->avail and memmove()? Ming, can you please try the following patch? From: Shakeel Butt <shakeelb@xxxxxxxxxx> [PATCH] mm: slab: fix potential infinite recursion in ___cache_free With the commit 10befea91b61 ("mm: memcg/slab: use a single set of kmem_caches for all allocations"), it becomes possible to call kfree() from the slabs_destroy(). However if slabs_destroy() is being called for the array_cache of the local CPU then this opens the potential scenario of infinite recursion because kfree() called from slabs_destroy() can call slabs_destroy() with the same array_cache of the local CPU. Since the array_cache of the local CPU is not updated before calling slabs_destroy(), it will try to free the same pages. To fix the issue, simply update the cache before calling slabs_destroy(). Signed-off-by: Shakeel Butt <shakeelb@xxxxxxxxxx> --- mm/slab.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/mm/slab.c b/mm/slab.c index 3160dff6fd76..f658e86ec8ce 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1632,6 +1632,10 @@ static void slab_destroy(struct kmem_cache *cachep, struct page *page) kmem_cache_free(cachep->freelist_cache, freelist); } +/* + * Update the size of the caches before calling slabs_destroy as it may + * recursively call kfree. + */ static void slabs_destroy(struct kmem_cache *cachep, struct list_head *list) { struct page *page, *n; @@ -2153,8 +2157,8 @@ static void do_drain(void *arg) spin_lock(&n->list_lock); free_block(cachep, ac->entry, ac->avail, node, &list); spin_unlock(&n->list_lock); - slabs_destroy(cachep, &list); ac->avail = 0; + slabs_destroy(cachep, &list); } static void drain_cpu_caches(struct kmem_cache *cachep) @@ -3402,9 +3406,9 @@ static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) } #endif spin_unlock(&n->list_lock); - slabs_destroy(cachep, &list); ac->avail -= batchcount; memmove(ac->entry, &(ac->entry[batchcount]), sizeof(void *)*ac->avail); + slabs_destroy(cachep, &list); } /* -- 2.28.0.681.g6f77f65b4e-goog