mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
slab: Introduce kmalloc_nolock() and kfree_nolock().
kmalloc_nolock() relies on ability of local_trylock_t to detect
the situation when per-cpu kmem_cache is locked.
In !PREEMPT_RT local_(try)lock_irqsave(&s->cpu_slab->lock, flags)
disables IRQs and marks s->cpu_slab->lock as acquired.
local_lock_is_locked(&s->cpu_slab->lock) returns true when
slab is in the middle of manipulating per-cpu cache
of that specific kmem_cache.
kmalloc_nolock() can be called from any context and can re-enter
into ___slab_alloc():
kmalloc() -> ___slab_alloc(cache_A) -> irqsave -> NMI -> bpf ->
kmalloc_nolock() -> ___slab_alloc(cache_B)
or
kmalloc() -> ___slab_alloc(cache_A) -> irqsave -> tracepoint/kprobe -> bpf ->
kmalloc_nolock() -> ___slab_alloc(cache_B)
Hence the caller of ___slab_alloc() checks if &s->cpu_slab->lock
can be acquired without a deadlock before invoking the function.
If that specific per-cpu kmem_cache is busy the kmalloc_nolock()
retries in a different kmalloc bucket. The second attempt will
likely succeed, since this cpu locked different kmem_cache.
Similarly, in PREEMPT_RT local_lock_is_locked() returns true when
per-cpu rt_spin_lock is locked by current _task_. In this case
re-entrance into the same kmalloc bucket is unsafe, and
kmalloc_nolock() tries a different bucket that is most likely is
not locked by the current task. Though it may be locked by a
different task it's safe to rt_spin_lock() and sleep on it.
Similar to alloc_pages_nolock() the kmalloc_nolock() returns NULL
immediately if called from hard irq or NMI in PREEMPT_RT.
kfree_nolock() defers freeing to irq_work when local_lock_is_locked()
and (in_nmi() or in PREEMPT_RT).
SLUB_TINY config doesn't use local_lock_is_locked() and relies on
spin_trylock_irqsave(&n->list_lock) to allocate,
while kfree_nolock() always defers to irq_work.
Note, kfree_nolock() must be called _only_ for objects allocated
with kmalloc_nolock(). Debug checks (like kmemleak and kfence)
were skipped on allocation, hence obj = kmalloc(); kfree_nolock(obj);
will miss kmemleak/kfence book keeping and will cause false positives.
large_kmalloc is not supported by either kmalloc_nolock()
or kfree_nolock().
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Reviewed-by: Harry Yoo <harry.yoo@oracle.com>
Signed-off-by: Vlastimil Babka <vbabka@suse.cz>
This commit is contained in:
committed by
Vlastimil Babka
parent
7612833192
commit
af92793e52
@@ -200,7 +200,7 @@ static __always_inline bool kasan_slab_pre_free(struct kmem_cache *s,
|
||||
}
|
||||
|
||||
bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init,
|
||||
bool still_accessible);
|
||||
bool still_accessible, bool no_quarantine);
|
||||
/**
|
||||
* kasan_slab_free - Poison, initialize, and quarantine a slab object.
|
||||
* @object: Object to be freed.
|
||||
@@ -226,11 +226,13 @@ bool __kasan_slab_free(struct kmem_cache *s, void *object, bool init,
|
||||
* @Return true if KASAN took ownership of the object; false otherwise.
|
||||
*/
|
||||
static __always_inline bool kasan_slab_free(struct kmem_cache *s,
|
||||
void *object, bool init,
|
||||
bool still_accessible)
|
||||
void *object, bool init,
|
||||
bool still_accessible,
|
||||
bool no_quarantine)
|
||||
{
|
||||
if (kasan_enabled())
|
||||
return __kasan_slab_free(s, object, init, still_accessible);
|
||||
return __kasan_slab_free(s, object, init, still_accessible,
|
||||
no_quarantine);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -427,7 +429,8 @@ static inline bool kasan_slab_pre_free(struct kmem_cache *s, void *object)
|
||||
}
|
||||
|
||||
static inline bool kasan_slab_free(struct kmem_cache *s, void *object,
|
||||
bool init, bool still_accessible)
|
||||
bool init, bool still_accessible,
|
||||
bool no_quarantine)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -358,6 +358,8 @@ enum objext_flags {
|
||||
* MEMCG_DATA_OBJEXTS.
|
||||
*/
|
||||
OBJEXTS_ALLOC_FAIL = __OBJEXTS_ALLOC_FAIL,
|
||||
/* slabobj_ext vector allocated with kmalloc_nolock() */
|
||||
OBJEXTS_NOSPIN_ALLOC = __FIRST_OBJEXT_FLAG,
|
||||
/* the next bit after the last actual flag */
|
||||
__NR_OBJEXTS_FLAGS = (__FIRST_OBJEXT_FLAG << 1),
|
||||
};
|
||||
|
||||
@@ -501,6 +501,7 @@ void * __must_check krealloc_noprof(const void *objp, size_t new_size,
|
||||
#define krealloc(...) alloc_hooks(krealloc_noprof(__VA_ARGS__))
|
||||
|
||||
void kfree(const void *objp);
|
||||
void kfree_nolock(const void *objp);
|
||||
void kfree_sensitive(const void *objp);
|
||||
size_t __ksize(const void *objp);
|
||||
|
||||
@@ -957,6 +958,9 @@ static __always_inline __alloc_size(1) void *kmalloc_noprof(size_t size, gfp_t f
|
||||
}
|
||||
#define kmalloc(...) alloc_hooks(kmalloc_noprof(__VA_ARGS__))
|
||||
|
||||
void *kmalloc_nolock_noprof(size_t size, gfp_t gfp_flags, int node);
|
||||
#define kmalloc_nolock(...) alloc_hooks(kmalloc_nolock_noprof(__VA_ARGS__))
|
||||
|
||||
#define kmem_buckets_alloc(_b, _size, _flags) \
|
||||
alloc_hooks(__kmalloc_node_noprof(PASS_BUCKET_PARAMS(_size, _b), _flags, NUMA_NO_NODE))
|
||||
|
||||
|
||||
@@ -194,6 +194,7 @@ menu "Slab allocator options"
|
||||
|
||||
config SLUB
|
||||
def_bool y
|
||||
select IRQ_WORK
|
||||
|
||||
config KVFREE_RCU_BATCHED
|
||||
def_bool y
|
||||
|
||||
+4
-1
@@ -252,7 +252,7 @@ bool __kasan_slab_pre_free(struct kmem_cache *cache, void *object,
|
||||
}
|
||||
|
||||
bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
|
||||
bool still_accessible)
|
||||
bool still_accessible, bool no_quarantine)
|
||||
{
|
||||
if (!kasan_arch_is_ready() || is_kfence_address(object))
|
||||
return false;
|
||||
@@ -274,6 +274,9 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object, bool init,
|
||||
|
||||
poison_slab_object(cache, object, init);
|
||||
|
||||
if (no_quarantine)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the object is put into quarantine, do not let slab put the object
|
||||
* onto the freelist for now. The object's metadata is kept until the
|
||||
|
||||
@@ -57,6 +57,10 @@ struct slab {
|
||||
struct {
|
||||
union {
|
||||
struct list_head slab_list;
|
||||
struct { /* For deferred deactivate_slab() */
|
||||
struct llist_node llnode;
|
||||
void *flush_freelist;
|
||||
};
|
||||
#ifdef CONFIG_SLUB_CPU_PARTIAL
|
||||
struct {
|
||||
struct slab *next;
|
||||
@@ -662,6 +666,8 @@ void __kmem_obj_info(struct kmem_obj_info *kpp, void *object, struct slab *slab)
|
||||
void __check_heap_object(const void *ptr, unsigned long n,
|
||||
const struct slab *slab, bool to_user);
|
||||
|
||||
void defer_free_barrier(void);
|
||||
|
||||
static inline bool slub_debug_orig_size(struct kmem_cache *s)
|
||||
{
|
||||
return (kmem_cache_debug_flags(s, SLAB_STORE_USER) &&
|
||||
|
||||
@@ -510,6 +510,9 @@ void kmem_cache_destroy(struct kmem_cache *s)
|
||||
rcu_barrier();
|
||||
}
|
||||
|
||||
/* Wait for deferred work from kmalloc/kfree_nolock() */
|
||||
defer_free_barrier();
|
||||
|
||||
cpus_read_lock();
|
||||
mutex_lock(&slab_mutex);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user