From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-alma10-1.taild15c8.ts.net [100.103.45.18]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 801C5299A84 for ; Fri, 26 Jun 2026 04:22:18 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=100.103.45.18 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782447739; cv=none; b=lhxQd1YB0B8n56nVG8MXe1q1MKX51sIioKWDNEKV7x/QlKL0/amE3BqbXZX4WNmaay5LXJ996duxVau7XUDgNUtlKvi7RwMM+kcBdEjDeylw+bshoJp4Yls9qc3s3McjqlpdL5PEV8U8rZI9zzCmvRGeulC2hNovdj+R1JMsRPU= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782447739; c=relaxed/simple; bh=CaqjdSPyZE+DrbVTEzxNNqrV2wapy6GWRtQ7yDTHDhs=; h=Message-ID:Date:MIME-Version:Subject:To:Cc:References:From: In-Reply-To:Content-Type; b=jq5JrHnuSnmeZ/JV+XpeXgzYqBK6bDVyReG6lBkx1SKelRdPJO8MI/zQNwgsmaZ/JU414X1A0o1OQi7d27HhH5KHNhlc1by3//ix2KudJvtl0CQyvhf73WetuJ++IRJ22Osll6or6yuTddGvoQlNrc98kSYegmXwC9U8jGALj28= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=M0amm3Fn; arc=none smtp.client-ip=100.103.45.18 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="M0amm3Fn" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 02C8B1F000E9; Fri, 26 Jun 2026 04:22:14 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=kernel.org; s=k20260515; t=1782447738; bh=CreF09zsseqG1KqS3MGVF4hVYNf0WAFIeh6kL8u7eDw=; h=Date:Subject:To:Cc:References:From:In-Reply-To; b=M0amm3FnZCP52MGG+c868lxtvYBFok+9dsCpP+awjR4HDs8z1wJM7sN3rWJEORAT3 xYkpVw6nzqoNSpZjtEu8f1aItyXzUaULZpCWrHmKE64Q200+MKS5cgK1v3CEesp4bk PTbYeCOxY/NbiEkjqnjFh0vlD6o2BiGZXYw7P4esth8LG8ishA9mg+KAZ4beOWl1bh K/dTKHeDUCIcECDKNUiUqw1arDafWCmvXukFyEvg9FanDSk9vq/6bQIHss8OuyduPo i80kattdA4Oi+ri2KfTgpEaPlQTkerA15dFu4gRM8RgyLBmwWutj2R+R53RWsWv7YW EHeeItb1IZESg== Message-ID: <62453403-954c-4cf1-8924-6d38184b0810@kernel.org> Date: Fri, 26 Jun 2026 13:22:09 +0900 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 User-Agent: Mozilla Thunderbird Subject: Re: [PATCH] mm/slub: serve slabobj_ext array from a strictly larger kmalloc cache To: Shakeel Butt , Vlastimil Babka , Andrew Morton Cc: Roman Gushchin , Hao Li , Christoph Lameter , David Rientjes , Suren Baghdasaryan , Usama Arif , Meta kernel team , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Danielle Costantino References: <20260625230029.703750-1-shakeel.butt@linux.dev> Content-Language: en-US From: Harry Yoo In-Reply-To: <20260625230029.703750-1-shakeel.butt@linux.dev> Content-Type: multipart/signed; micalg=pgp-sha256; protocol="application/pgp-signature"; boundary="------------wL0bb3VBv8atswQ8JRXKJEY4" This is an OpenPGP/MIME signed message (RFC 4880 and 3156) --------------wL0bb3VBv8atswQ8JRXKJEY4 Content-Type: multipart/mixed; boundary="------------OFsuQRSRGmFoObXVY94Bu0ce"; protected-headers="v1" From: Harry Yoo To: Shakeel Butt , Vlastimil Babka , Andrew Morton Cc: Roman Gushchin , Hao Li , Christoph Lameter , David Rientjes , Suren Baghdasaryan , Usama Arif , Meta kernel team , linux-mm@kvack.org, linux-kernel@vger.kernel.org, Danielle Costantino Message-ID: <62453403-954c-4cf1-8924-6d38184b0810@kernel.org> Subject: Re: [PATCH] mm/slub: serve slabobj_ext array from a strictly larger kmalloc cache References: <20260625230029.703750-1-shakeel.butt@linux.dev> In-Reply-To: <20260625230029.703750-1-shakeel.butt@linux.dev> --------------OFsuQRSRGmFoObXVY94Bu0ce Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: quoted-printable Hi Shakeel, On 6/26/26 8:00 AM, Shakeel Butt wrote: > A production host in the Meta fleet (6.16 kernel, memory allocation > profiling enabled) panicked with a kernel stack overflow while a kernel= > driver was freeing a resource: >=20 > BUG: TASK stack guard page was hit > Oops: stack guard page > RIP: 0010:kfree+0x8/0x5d0 > Call Trace: > __free_slab+0x66/0xc0 > kfree+0x3f0/0x5d0 > ... ( ~125x __free_slab <-> kfree ) ... > > do_syscall_64 >=20 > The crash dump shows a 125-deep __free_slab<->kfree recursion that > overflowed the 16 KiB kernel stack. Ouch! > What happened: a KMALLOC_NORMAL slab's obj_exts array (used by allocati= on > profiling / memcg accounting) is itself kmalloc()'d from a KMALLOC_NORM= AL > cache, Usually KMALLOC_NORMAL caches don't need obj_exts array, but yes, this could happen if memory allocation profiling is enabled. > so the "slab holds another slab's obj_exts array" relation can form > cycles. With sizeof(struct slabobj_ext) =3D=3D 16 and the host's geome= try: > > - kmalloc-512 has 64 objects/slab -> array is 64*16 =3D=3D 1024 bytes= , > served from kmalloc-1k; > - kmalloc-1k has 32 objects/slab -> array is 32*16 =3D=3D 512 bytes= , > served from kmalloc-512. Right. > A kmalloc-512 slab and a kmalloc-1k slab therefore hold each other's > obj_exts array. Discarding one frees the other's array, which empties = and > discards that slab, which frees the first's array, and so on: > __free_slab() -> free_slab_obj_exts() -> kfree() -> discard_slab() -> > __free_slab() recurses along the cycle until the stack is exhausted. Right. > The > dump confirms it: the recursion's slabs strictly alternate kmalloc-512 > (obj_exts in kmalloc-1k) and kmalloc-1k (obj_exts in kmalloc-512), and > mem_alloc_profiling_key was enabled. >=20 > Commit 280ea9c3154b ("mm/slab: avoid allocating slabobj_ext array from > its own slab") is not sufficient: it bumps the allocation size only whe= n > the array would come from the *same* cache (object_size =3D=3D). At th= e > geometry above neither cache is self-referential (512 !=3D 1024 and > 1024 !=3D 512), so the bump never triggers and the kmalloc-512 <-> kmal= loc-1k > cross cycle remains. Right. > Fix it structurally by removing cycles of every shape: serve the array > from a cache strictly larger than the one it describes whenever it woul= d > otherwise come from the same or a smaller cache. Every reference edge > then points from a smaller to a larger cache (here kmalloc-1k's array > moves to kmalloc-2k), so the relation is a DAG and cannot contain a cyc= le. This will fix the problem. But this will waste memory as we need smaller obj_exts array as the size gets larger. We should probably create a new kmalloc type to avoid cycles instead? (needed only when memory profiling is enabled, though) That would also prevent recursion even further. > No slab can be self- or cross-pinned, the tear-down recursion is bounde= d > by the number of kmalloc size classes (it terminates at the large-kmall= oc > path, which carries no obj_exts), and profiling/accounting coverage is > unchanged - the array is still allocated, only relocated. >=20 > Reproduced on next-20260623 at the same geometry: churning > kmalloc-512/kmalloc-1k under vm.mem_profiling and then shrinking leaves= > kmalloc-512 with thousands of unreclaimable objects without this patch > (8056) and at baseline with it (847). >=20 > Fixes: 4b8736964640 ("mm/slab: add allocation accounting into slab allo= cation and free paths") Perhaps Cc: stable? v6.12 and v6.18 are affected. > Signed-off-by: Shakeel Butt > Reported-by: Danielle Costantino > --- > mm/slub.c | 26 ++++++++++---------------- > 1 file changed, 10 insertions(+), 16 deletions(-) >=20 > diff --git a/mm/slub.c b/mm/slub.c > index 9ec774dc7009..48e54d340865 100644 > --- a/mm/slub.c > +++ b/mm/slub.c > @@ -2124,15 +2124,14 @@ static inline void init_slab_obj_exts(struct sl= ab *slab) > } > =20 > /* > - * Calculate the allocation size for slabobj_ext array. > + * Size of the slabobj_ext array for @slab. > * > - * When memory allocation profiling is enabled, the obj_exts array > - * could be allocated from the same slab cache it's being allocated fo= r. > - * This would prevent the slab from ever being freed because it would > - * always contain at least one allocated object (its own obj_exts arra= y). > - * > - * To avoid this, increase the allocation size when we detect the arra= y > - * may come from the same cache, forcing it to use a different cache. > + * The array is itself kmalloc()'d. If it came from the same or a smal= ler > + * kmalloc cache than @s, the "slab holds another slab's array" relati= on could > + * form a cycle (self, or e.g. kmalloc-512 <-> kmalloc-1k) that pins t= he slabs > + * forever and recurses via free_slab_obj_exts() -> kfree() -> discard= _slab() > + * at teardown. Force it into a strictly larger cache to keep that rel= ation a > + * DAG (acyclic). > */ > static inline size_t obj_exts_alloc_size(struct kmem_cache *s, > struct slab *slab, gfp_t gfp) > @@ -2147,14 +2146,9 @@ static inline size_t obj_exts_alloc_size(struct = kmem_cache *s, > return sz; > =20 > obj_exts_cache =3D kmalloc_slab(sz, NULL, gfp, __kmalloc_token(0)); > - /* > - * We can't simply compare s with obj_exts_cache, because partitioned= kmalloc > - * caches have multiple caches per size, selected by caller address o= r type. > - * Since caller address or type may differ between kmalloc_slab() and= actual > - * allocation, bump size when sizes are equal. > - */ > - if (s->object_size =3D=3D obj_exts_cache->object_size) > - return obj_exts_cache->object_size + 1; > + /* compare object_size, not the cache pointer (partitioned kmalloc ca= ches) */ This comment is no longer relevant, by the way. "compare object_size instead of cache pointers because there can be multiple caches of the same size" doesn't apply anymore. > + if (obj_exts_cache->object_size <=3D s->object_size) > + return s->object_size + 1; > =20 > return sz; > } --=20 Cheers, Harry / Hyeonggon --------------OFsuQRSRGmFoObXVY94Bu0ce-- --------------wL0bb3VBv8atswQ8JRXKJEY4 Content-Type: application/pgp-signature; name="OpenPGP_signature.asc" Content-Description: OpenPGP digital signature Content-Disposition: attachment; filename="OpenPGP_signature.asc" -----BEGIN PGP SIGNATURE----- iHUEARYKAB0WIQQQ1ub6gR5ogjaKRmOGXBN6rc5S1gUCaj3+cQAKCRCGXBN6rc5S 1vFCAP408P+shdyG+3rBPdecFSLmZ0GTZWItn30MBm+FK5kioAEAvexxEMUOs+Jf wR7TnHaFUNthkLRlk7E63fdzjD6bUQ8= =MqPD -----END PGP SIGNATURE----- --------------wL0bb3VBv8atswQ8JRXKJEY4--