|
|
|
@ -1065,11 +1065,15 @@ bad:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Supports checking bulk free of a constructed freelist */
|
|
|
|
|
static noinline struct kmem_cache_node *free_debug_processing(
|
|
|
|
|
struct kmem_cache *s, struct page *page, void *object,
|
|
|
|
|
struct kmem_cache *s, struct page *page,
|
|
|
|
|
void *head, void *tail, int bulk_cnt,
|
|
|
|
|
unsigned long addr, unsigned long *flags)
|
|
|
|
|
{
|
|
|
|
|
struct kmem_cache_node *n = get_node(s, page_to_nid(page));
|
|
|
|
|
void *object = head;
|
|
|
|
|
int cnt = 0;
|
|
|
|
|
|
|
|
|
|
spin_lock_irqsave(&n->list_lock, *flags);
|
|
|
|
|
slab_lock(page);
|
|
|
|
@ -1077,6 +1081,9 @@ static noinline struct kmem_cache_node *free_debug_processing(
|
|
|
|
|
if (!check_slab(s, page))
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
next_object:
|
|
|
|
|
cnt++;
|
|
|
|
|
|
|
|
|
|
if (!check_valid_pointer(s, page, object)) {
|
|
|
|
|
slab_err(s, page, "Invalid object pointer 0x%p", object);
|
|
|
|
|
goto fail;
|
|
|
|
@ -1107,8 +1114,19 @@ static noinline struct kmem_cache_node *free_debug_processing(
|
|
|
|
|
if (s->flags & SLAB_STORE_USER)
|
|
|
|
|
set_track(s, object, TRACK_FREE, addr);
|
|
|
|
|
trace(s, page, object, 0);
|
|
|
|
|
/* Freepointer not overwritten by init_object(), SLAB_POISON moved it */
|
|
|
|
|
init_object(s, object, SLUB_RED_INACTIVE);
|
|
|
|
|
|
|
|
|
|
/* Reached end of constructed freelist yet? */
|
|
|
|
|
if (object != tail) {
|
|
|
|
|
object = get_freepointer(s, object);
|
|
|
|
|
goto next_object;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
if (cnt != bulk_cnt)
|
|
|
|
|
slab_err(s, page, "Bulk freelist count(%d) invalid(%d)\n",
|
|
|
|
|
bulk_cnt, cnt);
|
|
|
|
|
|
|
|
|
|
slab_unlock(page);
|
|
|
|
|
/*
|
|
|
|
|
* Keep node_lock to preserve integrity
|
|
|
|
@ -1212,7 +1230,8 @@ static inline int alloc_debug_processing(struct kmem_cache *s,
|
|
|
|
|
struct page *page, void *object, unsigned long addr) { return 0; }
|
|
|
|
|
|
|
|
|
|
static inline struct kmem_cache_node *free_debug_processing(
|
|
|
|
|
struct kmem_cache *s, struct page *page, void *object,
|
|
|
|
|
struct kmem_cache *s, struct page *page,
|
|
|
|
|
void *head, void *tail, int bulk_cnt,
|
|
|
|
|
unsigned long addr, unsigned long *flags) { return NULL; }
|
|
|
|
|
|
|
|
|
|
static inline int slab_pad_check(struct kmem_cache *s, struct page *page)
|
|
|
|
@ -1273,14 +1292,21 @@ static inline struct kmem_cache *slab_pre_alloc_hook(struct kmem_cache *s,
|
|
|
|
|
return memcg_kmem_get_cache(s, flags);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void slab_post_alloc_hook(struct kmem_cache *s,
|
|
|
|
|
gfp_t flags, void *object)
|
|
|
|
|
static inline void slab_post_alloc_hook(struct kmem_cache *s, gfp_t flags,
|
|
|
|
|
size_t size, void **p)
|
|
|
|
|
{
|
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
|
|
flags &= gfp_allowed_mask;
|
|
|
|
|
kmemcheck_slab_alloc(s, flags, object, slab_ksize(s));
|
|
|
|
|
kmemleak_alloc_recursive(object, s->object_size, 1, s->flags, flags);
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
|
void *object = p[i];
|
|
|
|
|
|
|
|
|
|
kmemcheck_slab_alloc(s, flags, object, slab_ksize(s));
|
|
|
|
|
kmemleak_alloc_recursive(object, s->object_size, 1,
|
|
|
|
|
s->flags, flags);
|
|
|
|
|
kasan_slab_alloc(s, object);
|
|
|
|
|
}
|
|
|
|
|
memcg_kmem_put_cache(s);
|
|
|
|
|
kasan_slab_alloc(s, object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void slab_free_hook(struct kmem_cache *s, void *x)
|
|
|
|
@ -1308,6 +1334,29 @@ static inline void slab_free_hook(struct kmem_cache *s, void *x)
|
|
|
|
|
kasan_slab_free(s, x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline void slab_free_freelist_hook(struct kmem_cache *s,
|
|
|
|
|
void *head, void *tail)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Compiler cannot detect this function can be removed if slab_free_hook()
|
|
|
|
|
* evaluates to nothing. Thus, catch all relevant config debug options here.
|
|
|
|
|
*/
|
|
|
|
|
#if defined(CONFIG_KMEMCHECK) || \
|
|
|
|
|
defined(CONFIG_LOCKDEP) || \
|
|
|
|
|
defined(CONFIG_DEBUG_KMEMLEAK) || \
|
|
|
|
|
defined(CONFIG_DEBUG_OBJECTS_FREE) || \
|
|
|
|
|
defined(CONFIG_KASAN)
|
|
|
|
|
|
|
|
|
|
void *object = head;
|
|
|
|
|
void *tail_obj = tail ? : head;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
slab_free_hook(s, object);
|
|
|
|
|
} while ((object != tail_obj) &&
|
|
|
|
|
(object = get_freepointer(s, object)));
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void setup_object(struct kmem_cache *s, struct page *page,
|
|
|
|
|
void *object)
|
|
|
|
|
{
|
|
|
|
@ -2433,7 +2482,7 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
|
|
|
|
|
static __always_inline void *slab_alloc_node(struct kmem_cache *s,
|
|
|
|
|
gfp_t gfpflags, int node, unsigned long addr)
|
|
|
|
|
{
|
|
|
|
|
void **object;
|
|
|
|
|
void *object;
|
|
|
|
|
struct kmem_cache_cpu *c;
|
|
|
|
|
struct page *page;
|
|
|
|
|
unsigned long tid;
|
|
|
|
@ -2512,7 +2561,7 @@ redo:
|
|
|
|
|
if (unlikely(gfpflags & __GFP_ZERO) && object)
|
|
|
|
|
memset(object, 0, s->object_size);
|
|
|
|
|
|
|
|
|
|
slab_post_alloc_hook(s, gfpflags, object);
|
|
|
|
|
slab_post_alloc_hook(s, gfpflags, 1, &object);
|
|
|
|
|
|
|
|
|
|
return object;
|
|
|
|
|
}
|
|
|
|
@ -2583,10 +2632,11 @@ EXPORT_SYMBOL(kmem_cache_alloc_node_trace);
|
|
|
|
|
* handling required then we can return immediately.
|
|
|
|
|
*/
|
|
|
|
|
static void __slab_free(struct kmem_cache *s, struct page *page,
|
|
|
|
|
void *x, unsigned long addr)
|
|
|
|
|
void *head, void *tail, int cnt,
|
|
|
|
|
unsigned long addr)
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
void *prior;
|
|
|
|
|
void **object = (void *)x;
|
|
|
|
|
int was_frozen;
|
|
|
|
|
struct page new;
|
|
|
|
|
unsigned long counters;
|
|
|
|
@ -2596,7 +2646,8 @@ static void __slab_free(struct kmem_cache *s, struct page *page,
|
|
|
|
|
stat(s, FREE_SLOWPATH);
|
|
|
|
|
|
|
|
|
|
if (kmem_cache_debug(s) &&
|
|
|
|
|
!(n = free_debug_processing(s, page, x, addr, &flags)))
|
|
|
|
|
!(n = free_debug_processing(s, page, head, tail, cnt,
|
|
|
|
|
addr, &flags)))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
@ -2606,10 +2657,10 @@ static void __slab_free(struct kmem_cache *s, struct page *page,
|
|
|
|
|
}
|
|
|
|
|
prior = page->freelist;
|
|
|
|
|
counters = page->counters;
|
|
|
|
|
set_freepointer(s, object, prior);
|
|
|
|
|
set_freepointer(s, tail, prior);
|
|
|
|
|
new.counters = counters;
|
|
|
|
|
was_frozen = new.frozen;
|
|
|
|
|
new.inuse--;
|
|
|
|
|
new.inuse -= cnt;
|
|
|
|
|
if ((!new.inuse || !prior) && !was_frozen) {
|
|
|
|
|
|
|
|
|
|
if (kmem_cache_has_cpu_partial(s) && !prior) {
|
|
|
|
@ -2640,7 +2691,7 @@ static void __slab_free(struct kmem_cache *s, struct page *page,
|
|
|
|
|
|
|
|
|
|
} while (!cmpxchg_double_slab(s, page,
|
|
|
|
|
prior, counters,
|
|
|
|
|
object, new.counters,
|
|
|
|
|
head, new.counters,
|
|
|
|
|
"__slab_free"));
|
|
|
|
|
|
|
|
|
|
if (likely(!n)) {
|
|
|
|
@ -2705,15 +2756,20 @@ slab_empty:
|
|
|
|
|
*
|
|
|
|
|
* If fastpath is not possible then fall back to __slab_free where we deal
|
|
|
|
|
* with all sorts of special processing.
|
|
|
|
|
*
|
|
|
|
|
* Bulk free of a freelist with several objects (all pointing to the
|
|
|
|
|
* same page) possible by specifying head and tail ptr, plus objects
|
|
|
|
|
* count (cnt). Bulk free indicated by tail pointer being set.
|
|
|
|
|
*/
|
|
|
|
|
static __always_inline void slab_free(struct kmem_cache *s,
|
|
|
|
|
struct page *page, void *x, unsigned long addr)
|
|
|
|
|
static __always_inline void slab_free(struct kmem_cache *s, struct page *page,
|
|
|
|
|
void *head, void *tail, int cnt,
|
|
|
|
|
unsigned long addr)
|
|
|
|
|
{
|
|
|
|
|
void **object = (void *)x;
|
|
|
|
|
void *tail_obj = tail ? : head;
|
|
|
|
|
struct kmem_cache_cpu *c;
|
|
|
|
|
unsigned long tid;
|
|
|
|
|
|
|
|
|
|
slab_free_hook(s, x);
|
|
|
|
|
slab_free_freelist_hook(s, head, tail);
|
|
|
|
|
|
|
|
|
|
redo:
|
|
|
|
|
/*
|
|
|
|
@ -2732,19 +2788,19 @@ redo:
|
|
|
|
|
barrier();
|
|
|
|
|
|
|
|
|
|
if (likely(page == c->page)) {
|
|
|
|
|
set_freepointer(s, object, c->freelist);
|
|
|
|
|
set_freepointer(s, tail_obj, c->freelist);
|
|
|
|
|
|
|
|
|
|
if (unlikely(!this_cpu_cmpxchg_double(
|
|
|
|
|
s->cpu_slab->freelist, s->cpu_slab->tid,
|
|
|
|
|
c->freelist, tid,
|
|
|
|
|
object, next_tid(tid)))) {
|
|
|
|
|
head, next_tid(tid)))) {
|
|
|
|
|
|
|
|
|
|
note_cmpxchg_failure("slab_free", s, tid);
|
|
|
|
|
goto redo;
|
|
|
|
|
}
|
|
|
|
|
stat(s, FREE_FASTPATH);
|
|
|
|
|
} else
|
|
|
|
|
__slab_free(s, page, x, addr);
|
|
|
|
|
__slab_free(s, page, head, tail_obj, cnt, addr);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -2753,59 +2809,116 @@ void kmem_cache_free(struct kmem_cache *s, void *x)
|
|
|
|
|
s = cache_from_obj(s, x);
|
|
|
|
|
if (!s)
|
|
|
|
|
return;
|
|
|
|
|
slab_free(s, virt_to_head_page(x), x, _RET_IP_);
|
|
|
|
|
slab_free(s, virt_to_head_page(x), x, NULL, 1, _RET_IP_);
|
|
|
|
|
trace_kmem_cache_free(_RET_IP_, x);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(kmem_cache_free);
|
|
|
|
|
|
|
|
|
|
/* Note that interrupts must be enabled when calling this function. */
|
|
|
|
|
void kmem_cache_free_bulk(struct kmem_cache *s, size_t size, void **p)
|
|
|
|
|
{
|
|
|
|
|
struct kmem_cache_cpu *c;
|
|
|
|
|
struct detached_freelist {
|
|
|
|
|
struct page *page;
|
|
|
|
|
int i;
|
|
|
|
|
void *tail;
|
|
|
|
|
void *freelist;
|
|
|
|
|
int cnt;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
c = this_cpu_ptr(s->cpu_slab);
|
|
|
|
|
/*
|
|
|
|
|
* This function progressively scans the array with free objects (with
|
|
|
|
|
* a limited look ahead) and extract objects belonging to the same
|
|
|
|
|
* page. It builds a detached freelist directly within the given
|
|
|
|
|
* page/objects. This can happen without any need for
|
|
|
|
|
* synchronization, because the objects are owned by running process.
|
|
|
|
|
* The freelist is build up as a single linked list in the objects.
|
|
|
|
|
* The idea is, that this detached freelist can then be bulk
|
|
|
|
|
* transferred to the real freelist(s), but only requiring a single
|
|
|
|
|
* synchronization primitive. Look ahead in the array is limited due
|
|
|
|
|
* to performance reasons.
|
|
|
|
|
*/
|
|
|
|
|
static int build_detached_freelist(struct kmem_cache *s, size_t size,
|
|
|
|
|
void **p, struct detached_freelist *df)
|
|
|
|
|
{
|
|
|
|
|
size_t first_skipped_index = 0;
|
|
|
|
|
int lookahead = 3;
|
|
|
|
|
void *object;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < size; i++) {
|
|
|
|
|
void *object = p[i];
|
|
|
|
|
/* Always re-init detached_freelist */
|
|
|
|
|
df->page = NULL;
|
|
|
|
|
|
|
|
|
|
BUG_ON(!object);
|
|
|
|
|
/* kmem cache debug support */
|
|
|
|
|
s = cache_from_obj(s, object);
|
|
|
|
|
if (unlikely(!s))
|
|
|
|
|
goto exit;
|
|
|
|
|
slab_free_hook(s, object);
|
|
|
|
|
do {
|
|
|
|
|
object = p[--size];
|
|
|
|
|
} while (!object && size);
|
|
|
|
|
|
|
|
|
|
page = virt_to_head_page(object);
|
|
|
|
|
if (!object)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (c->page == page) {
|
|
|
|
|
/* Fastpath: local CPU free */
|
|
|
|
|
set_freepointer(s, object, c->freelist);
|
|
|
|
|
c->freelist = object;
|
|
|
|
|
} else {
|
|
|
|
|
c->tid = next_tid(c->tid);
|
|
|
|
|
local_irq_enable();
|
|
|
|
|
/* Slowpath: overhead locked cmpxchg_double_slab */
|
|
|
|
|
__slab_free(s, page, object, _RET_IP_);
|
|
|
|
|
local_irq_disable();
|
|
|
|
|
c = this_cpu_ptr(s->cpu_slab);
|
|
|
|
|
/* Start new detached freelist */
|
|
|
|
|
set_freepointer(s, object, NULL);
|
|
|
|
|
df->page = virt_to_head_page(object);
|
|
|
|
|
df->tail = object;
|
|
|
|
|
df->freelist = object;
|
|
|
|
|
p[size] = NULL; /* mark object processed */
|
|
|
|
|
df->cnt = 1;
|
|
|
|
|
|
|
|
|
|
while (size) {
|
|
|
|
|
object = p[--size];
|
|
|
|
|
if (!object)
|
|
|
|
|
continue; /* Skip processed objects */
|
|
|
|
|
|
|
|
|
|
/* df->page is always set at this point */
|
|
|
|
|
if (df->page == virt_to_head_page(object)) {
|
|
|
|
|
/* Opportunity build freelist */
|
|
|
|
|
set_freepointer(s, object, df->freelist);
|
|
|
|
|
df->freelist = object;
|
|
|
|
|
df->cnt++;
|
|
|
|
|
p[size] = NULL; /* mark object processed */
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Limit look ahead search */
|
|
|
|
|
if (!--lookahead)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (!first_skipped_index)
|
|
|
|
|
first_skipped_index = size + 1;
|
|
|
|
|
}
|
|
|
|
|
exit:
|
|
|
|
|
c->tid = next_tid(c->tid);
|
|
|
|
|
local_irq_enable();
|
|
|
|
|
|
|
|
|
|
return first_skipped_index;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Note that interrupts must be enabled when calling this function. */
|
|
|
|
|
void kmem_cache_free_bulk(struct kmem_cache *orig_s, size_t size, void **p)
|
|
|
|
|
{
|
|
|
|
|
if (WARN_ON(!size))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
struct detached_freelist df;
|
|
|
|
|
struct kmem_cache *s;
|
|
|
|
|
|
|
|
|
|
/* Support for memcg */
|
|
|
|
|
s = cache_from_obj(orig_s, p[size - 1]);
|
|
|
|
|
|
|
|
|
|
size = build_detached_freelist(s, size, p, &df);
|
|
|
|
|
if (unlikely(!df.page))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
slab_free(s, df.page, df.freelist, df.tail, df.cnt, _RET_IP_);
|
|
|
|
|
} while (likely(size));
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(kmem_cache_free_bulk);
|
|
|
|
|
|
|
|
|
|
/* Note that interrupts must be enabled when calling this function. */
|
|
|
|
|
bool kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
|
|
|
|
|
void **p)
|
|
|
|
|
int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
|
|
|
|
|
void **p)
|
|
|
|
|
{
|
|
|
|
|
struct kmem_cache_cpu *c;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* memcg and kmem_cache debug support */
|
|
|
|
|
s = slab_pre_alloc_hook(s, flags);
|
|
|
|
|
if (unlikely(!s))
|
|
|
|
|
return false;
|
|
|
|
|
/*
|
|
|
|
|
* Drain objects in the per cpu slab, while disabling local
|
|
|
|
|
* IRQs, which protects against PREEMPT and interrupts
|
|
|
|
@ -2830,17 +2943,8 @@ bool kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
|
|
|
|
|
c = this_cpu_ptr(s->cpu_slab);
|
|
|
|
|
continue; /* goto for-loop */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* kmem_cache debug support */
|
|
|
|
|
s = slab_pre_alloc_hook(s, flags);
|
|
|
|
|
if (unlikely(!s))
|
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
|
|
c->freelist = get_freepointer(s, object);
|
|
|
|
|
p[i] = object;
|
|
|
|
|
|
|
|
|
|
/* kmem_cache debug support */
|
|
|
|
|
slab_post_alloc_hook(s, flags, object);
|
|
|
|
|
}
|
|
|
|
|
c->tid = next_tid(c->tid);
|
|
|
|
|
local_irq_enable();
|
|
|
|
@ -2853,12 +2957,14 @@ bool kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
|
|
|
|
|
memset(p[j], 0, s->object_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
/* memcg and kmem_cache debug support */
|
|
|
|
|
slab_post_alloc_hook(s, flags, size, p);
|
|
|
|
|
return i;
|
|
|
|
|
error:
|
|
|
|
|
__kmem_cache_free_bulk(s, i, p);
|
|
|
|
|
local_irq_enable();
|
|
|
|
|
return false;
|
|
|
|
|
slab_post_alloc_hook(s, flags, i, p);
|
|
|
|
|
__kmem_cache_free_bulk(s, i, p);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(kmem_cache_alloc_bulk);
|
|
|
|
|
|
|
|
|
@ -3523,7 +3629,7 @@ void kfree(const void *x)
|
|
|
|
|
__free_kmem_pages(page, compound_order(page));
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
slab_free(page->slab_cache, page, object, _RET_IP_);
|
|
|
|
|
slab_free(page->slab_cache, page, object, NULL, 1, _RET_IP_);
|
|
|
|
|
}
|
|
|
|
|
EXPORT_SYMBOL(kfree);
|
|
|
|
|
|
|
|
|
|