Skip to content

Commit 284f17a

Browse files
committed
mm/slub: handle bulk and single object freeing separately
Currently we have a single function slab_free() handling both single object freeing and bulk freeing with necessary hooks, the latter case requiring slab_free_freelist_hook(). It should be however better to distinguish the two use cases for the following reasons: - code simpler to follow for the single object case - better code generation - although inlining should eliminate the slab_free_freelist_hook() for single object freeing in case no debugging options are enabled, it seems it's not perfect. When e.g. KASAN is enabled, we're imposing additional unnecessary overhead for single object freeing. - preparation to add percpu array caches in near future Therefore, simplify slab_free() for the single object case by dropping unnecessary parameters and calling only slab_free_hook() instead of slab_free_freelist_hook(). Rename the bulk variant to slab_free_bulk() and adjust callers accordingly. While at it, flip (and document) slab_free_hook() return value so that it returns true when the freeing can proceed, which matches the logic of slab_free_freelist_hook() and is not confusingly the opposite. Additionally we can simplify a bit by changing the tail parameter of do_slab_free() when freeing a single object - instead of NULL we can set it equal to head. bloat-o-meter shows small code reduction with a .config that has KASAN etc disabled: add/remove: 0/0 grow/shrink: 0/4 up/down: 0/-118 (-118) Function old new delta kmem_cache_alloc_bulk 1203 1196 -7 kmem_cache_free 861 835 -26 __kmem_cache_free 741 704 -37 kmem_cache_free_bulk 911 863 -48 Reviewed-by: Chengming Zhou <[email protected]> Signed-off-by: Vlastimil Babka <[email protected]>
1 parent 520a688 commit 284f17a

File tree

1 file changed

+35
-24
lines changed

1 file changed

+35
-24
lines changed

mm/slub.c

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,9 +2051,12 @@ void memcg_slab_alloc_error_hook(struct kmem_cache *s, int objects,
20512051
/*
20522052
* Hooks for other subsystems that check memory allocations. In a typical
20532053
* production configuration these hooks all should produce no code at all.
2054+
*
2055+
* Returns true if freeing of the object can proceed, false if its reuse
2056+
* was delayed by KASAN quarantine.
20542057
*/
2055-
static __always_inline bool slab_free_hook(struct kmem_cache *s,
2056-
void *x, bool init)
2058+
static __always_inline
2059+
bool slab_free_hook(struct kmem_cache *s, void *x, bool init)
20572060
{
20582061
kmemleak_free_recursive(x, s->flags);
20592062
kmsan_slab_free(s, x);
@@ -2086,7 +2089,7 @@ static __always_inline bool slab_free_hook(struct kmem_cache *s,
20862089
s->size - s->inuse - rsize);
20872090
}
20882091
/* KASAN might put x into memory quarantine, delaying its reuse. */
2089-
return kasan_slab_free(s, x, init);
2092+
return !kasan_slab_free(s, x, init);
20902093
}
20912094

20922095
static inline bool slab_free_freelist_hook(struct kmem_cache *s,
@@ -2096,7 +2099,7 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
20962099

20972100
void *object;
20982101
void *next = *head;
2099-
void *old_tail = *tail ? *tail : *head;
2102+
void *old_tail = *tail;
21002103

21012104
if (is_kfence_address(next)) {
21022105
slab_free_hook(s, next, false);
@@ -2112,8 +2115,8 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
21122115
next = get_freepointer(s, object);
21132116

21142117
/* If object's reuse doesn't have to be delayed */
2115-
if (likely(!slab_free_hook(s, object,
2116-
slab_want_init_on_free(s)))) {
2118+
if (likely(slab_free_hook(s, object,
2119+
slab_want_init_on_free(s)))) {
21172120
/* Move object to the new freelist */
21182121
set_freepointer(s, object, *head);
21192122
*head = object;
@@ -2128,9 +2131,6 @@ static inline bool slab_free_freelist_hook(struct kmem_cache *s,
21282131
}
21292132
} while (object != old_tail);
21302133

2131-
if (*head == *tail)
2132-
*tail = NULL;
2133-
21342134
return *head != NULL;
21352135
}
21362136

@@ -4241,7 +4241,6 @@ static __always_inline void do_slab_free(struct kmem_cache *s,
42414241
struct slab *slab, void *head, void *tail,
42424242
int cnt, unsigned long addr)
42434243
{
4244-
void *tail_obj = tail ? : head;
42454244
struct kmem_cache_cpu *c;
42464245
unsigned long tid;
42474246
void **freelist;
@@ -4260,14 +4259,14 @@ static __always_inline void do_slab_free(struct kmem_cache *s,
42604259
barrier();
42614260

42624261
if (unlikely(slab != c->slab)) {
4263-
__slab_free(s, slab, head, tail_obj, cnt, addr);
4262+
__slab_free(s, slab, head, tail, cnt, addr);
42644263
return;
42654264
}
42664265

42674266
if (USE_LOCKLESS_FAST_PATH()) {
42684267
freelist = READ_ONCE(c->freelist);
42694268

4270-
set_freepointer(s, tail_obj, freelist);
4269+
set_freepointer(s, tail, freelist);
42714270

42724271
if (unlikely(!__update_cpu_freelist_fast(s, freelist, head, tid))) {
42734272
note_cmpxchg_failure("slab_free", s, tid);
@@ -4284,7 +4283,7 @@ static __always_inline void do_slab_free(struct kmem_cache *s,
42844283
tid = c->tid;
42854284
freelist = c->freelist;
42864285

4287-
set_freepointer(s, tail_obj, freelist);
4286+
set_freepointer(s, tail, freelist);
42884287
c->freelist = head;
42894288
c->tid = next_tid(tid);
42904289

@@ -4297,15 +4296,27 @@ static void do_slab_free(struct kmem_cache *s,
42974296
struct slab *slab, void *head, void *tail,
42984297
int cnt, unsigned long addr)
42994298
{
4300-
void *tail_obj = tail ? : head;
4301-
4302-
__slab_free(s, slab, head, tail_obj, cnt, addr);
4299+
__slab_free(s, slab, head, tail, cnt, addr);
43034300
}
43044301
#endif /* CONFIG_SLUB_TINY */
43054302

4306-
static __fastpath_inline void slab_free(struct kmem_cache *s, struct slab *slab,
4307-
void *head, void *tail, void **p, int cnt,
4308-
unsigned long addr)
4303+
static __fastpath_inline
4304+
void slab_free(struct kmem_cache *s, struct slab *slab, void *object,
4305+
unsigned long addr)
4306+
{
4307+
bool init;
4308+
4309+
memcg_slab_free_hook(s, slab, &object, 1);
4310+
4311+
init = !is_kfence_address(object) && slab_want_init_on_free(s);
4312+
4313+
if (likely(slab_free_hook(s, object, init)))
4314+
do_slab_free(s, slab, object, object, 1, addr);
4315+
}
4316+
4317+
static __fastpath_inline
4318+
void slab_free_bulk(struct kmem_cache *s, struct slab *slab, void *head,
4319+
void *tail, void **p, int cnt, unsigned long addr)
43094320
{
43104321
memcg_slab_free_hook(s, slab, p, cnt);
43114322
/*
@@ -4319,7 +4330,7 @@ static __fastpath_inline void slab_free(struct kmem_cache *s, struct slab *slab,
43194330
#ifdef CONFIG_KASAN_GENERIC
43204331
void ___cache_free(struct kmem_cache *cache, void *x, unsigned long addr)
43214332
{
4322-
do_slab_free(cache, virt_to_slab(x), x, NULL, 1, addr);
4333+
do_slab_free(cache, virt_to_slab(x), x, x, 1, addr);
43234334
}
43244335
#endif
43254336

@@ -4363,7 +4374,7 @@ void kmem_cache_free(struct kmem_cache *s, void *x)
43634374
if (!s)
43644375
return;
43654376
trace_kmem_cache_free(_RET_IP_, x, s);
4366-
slab_free(s, virt_to_slab(x), x, NULL, &x, 1, _RET_IP_);
4377+
slab_free(s, virt_to_slab(x), x, _RET_IP_);
43674378
}
43684379
EXPORT_SYMBOL(kmem_cache_free);
43694380

@@ -4409,7 +4420,7 @@ void kfree(const void *object)
44094420

44104421
slab = folio_slab(folio);
44114422
s = slab->slab_cache;
4412-
slab_free(s, slab, x, NULL, &x, 1, _RET_IP_);
4423+
slab_free(s, slab, x, _RET_IP_);
44134424
}
44144425
EXPORT_SYMBOL(kfree);
44154426

@@ -4526,8 +4537,8 @@ void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
45264537
if (!df.slab)
45274538
continue;
45284539

4529-
slab_free(df.s, df.slab, df.freelist, df.tail, &p[size], df.cnt,
4530-
_RET_IP_);
4540+
slab_free_bulk(df.s, df.slab, df.freelist, df.tail, &p[size],
4541+
df.cnt, _RET_IP_);
45314542
} while (likely(size));
45324543
}
45334544
EXPORT_SYMBOL(kmem_cache_free_bulk);

0 commit comments

Comments
 (0)