Re: [RFC] slub memory quarantine

From: Dmitry Vyukov
Date: Tue Mar 03 2015 - 04:10:41 EST


Please hold on with this.
Dmitry Chernenkov is working on a quarantine that works with both slub
and slab, does not cause spurious OOMs and does not depend on
slub-debug which has unacceptable performance (acquires global lock).
Me or Dmitry C will send an email to kasan-dev@xxxxxxxxxxxxxxxx to
discuss quarantine development direction.



On Tue, Mar 3, 2015 at 11:55 AM, Andrey Ryabinin <a.ryabinin@xxxxxxxxxxx> wrote:
> Hi.
>
> One of the main problems in detecting use after free bugs is memory reuse.
> Freed could be quickly reallocated.
> Neither KASan nor slub poisoning could detect use after free after reallocation.
> Memory quarantine is aimed to solve this problem by delaying as much as possible
> actual freeing of object.
>
> The patch below implements quarantine for SLUB. Currently it has couple known issues:
> - runtime quarantine on/off switcher doesn't work well.
> - for some reason shrinker doesn't work well.
> Allocations with order > 3 may not succeed and lead to OOM
> even though we have a lot of in quarantine memory which could be freed.
>
> Before digging into those issues, I would like to hear you opinions about this patch.
>
>
> ------------------------------------------------------
> From: Konstantin Khlebnikov <koct9i@xxxxxxxxx>
>
> Quarantine isolates freed objects for some period of time in place of
> reusing them instantly. It helps in catching vague use-after-free
> bugs, especially when combined with kernel address sanitizer.
>
> This patch adds quarantine for slub. Like other slub debug features it
> might be enabled in command line: slub_debug=Q or in runtime via sysfs
> for individual slabs. All freed objects goes into quarantine and then
> completely quarantined slubs might be reclaimed by memory shinker.
> Thus quarantine consumes all available memory and doesn't require any
> tuning.
>
> Signed-off-by: Konstantin Khlebnikov <koct9i@xxxxxxxxx>
> ---
> Documentation/vm/slub.txt | 1 +
> include/linux/mm_types.h | 1 +
> include/linux/slab.h | 2 +
> include/linux/slub_def.h | 5 +-
> mm/slab.h | 8 +-
> mm/slab_common.c | 2 +-
> mm/slub.c | 388 +++++++++++++++++++++++++++++++++++++++++++++-
> 7 files changed, 396 insertions(+), 11 deletions(-)
>
> diff --git a/Documentation/vm/slub.txt b/Documentation/vm/slub.txt
> index b0c6d1b..08d90da 100644
> --- a/Documentation/vm/slub.txt
> +++ b/Documentation/vm/slub.txt
> @@ -44,6 +44,7 @@ Possible debug options are
> A Toggle failslab filter mark for the cache
> O Switch debugging off for caches that would have
> caused higher minimum slab orders
> + Q put freed objects into Quarantine
> - Switch all debugging off (useful if the kernel is
> configured with CONFIG_SLUB_DEBUG_ON)
>
> diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
> index 199a03a..5d89578 100644
> --- a/include/linux/mm_types.h
> +++ b/include/linux/mm_types.h
> @@ -56,6 +56,7 @@ struct page {
> * see PAGE_MAPPING_ANON below.
> */
> void *s_mem; /* slab first object */
> + void *quarantine; /* slub quarantine list */
> };
>
> /* Second double word */
> diff --git a/include/linux/slab.h b/include/linux/slab.h
> index 76f1fee..0e8e8f2 100644
> --- a/include/linux/slab.h
> +++ b/include/linux/slab.h
> @@ -87,6 +87,8 @@
> # define SLAB_FAILSLAB 0x00000000UL
> #endif
>
> +#define SLAB_QUARANTINE 0x04000000UL
> +
> /* The following flags affect the page allocator grouping pages by mobility */
> #define SLAB_RECLAIM_ACCOUNT 0x00020000UL /* Objects are reclaimable */
> #define SLAB_TEMPORARY SLAB_RECLAIM_ACCOUNT /* Objects are short-lived */
> diff --git a/include/linux/slub_def.h b/include/linux/slub_def.h
> index 3388511..bb13fcd 100644
> --- a/include/linux/slub_def.h
> +++ b/include/linux/slub_def.h
> @@ -35,6 +35,7 @@ enum stat_item {
> CPU_PARTIAL_FREE, /* Refill cpu partial on free */
> CPU_PARTIAL_NODE, /* Refill cpu partial from node partial */
> CPU_PARTIAL_DRAIN, /* Drain cpu partial to node partial */
> + QUARANTINE_BREACH, /* Slab left quarantine via reclaimer */
> NR_SLUB_STAT_ITEMS };
>
> struct kmem_cache_cpu {
> @@ -91,7 +92,9 @@ struct kmem_cache {
> struct kset *memcg_kset;
> #endif
> #endif
> -
> +#ifdef CONFIG_SLUB_DEBUG
> + struct kmem_cache_quarantine_shrinker *quarantine_shrinker;
> +#endif
> #ifdef CONFIG_NUMA
> /*
> * Defragmentation by allocating from a remote node.
> diff --git a/mm/slab.h b/mm/slab.h
> index 4c3ac12..f6d5c61 100644
> --- a/mm/slab.h
> +++ b/mm/slab.h
> @@ -120,7 +120,7 @@ static inline unsigned long kmem_cache_flags(unsigned long object_size,
> #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER)
> #elif defined(CONFIG_SLUB_DEBUG)
> #define SLAB_DEBUG_FLAGS (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
> - SLAB_TRACE | SLAB_DEBUG_FREE)
> + SLAB_TRACE | SLAB_DEBUG_FREE | SLAB_QUARANTINE)
> #else
> #define SLAB_DEBUG_FLAGS (0)
> #endif
> @@ -352,6 +352,12 @@ struct kmem_cache_node {
> atomic_long_t nr_slabs;
> atomic_long_t total_objects;
> struct list_head full;
> +
> + struct {
> + unsigned long nr_objects;
> + unsigned long nr_slabs;
> + struct list_head slabs;
> + } quarantine;
> #endif
> #endif
>
> diff --git a/mm/slab_common.c b/mm/slab_common.c
> index 999bb34..05e317d 100644
> --- a/mm/slab_common.c
> +++ b/mm/slab_common.c
> @@ -35,7 +35,7 @@ struct kmem_cache *kmem_cache;
> */
> #define SLAB_NEVER_MERGE (SLAB_RED_ZONE | SLAB_POISON | SLAB_STORE_USER | \
> SLAB_TRACE | SLAB_DESTROY_BY_RCU | SLAB_NOLEAKTRACE | \
> - SLAB_FAILSLAB)
> + SLAB_FAILSLAB | SLAB_QUARANTINE)
>
> #define SLAB_MERGE_SAME (SLAB_DEBUG_FREE | SLAB_RECLAIM_ACCOUNT | \
> SLAB_CACHE_DMA | SLAB_NOTRACK)
> diff --git a/mm/slub.c b/mm/slub.c
> index 6832c4e..27ab842 100644
> --- a/mm/slub.c
> +++ b/mm/slub.c
> @@ -161,7 +161,7 @@ static inline bool kmem_cache_has_cpu_partial(struct kmem_cache *s)
> #define MAX_PARTIAL 10
>
> #define DEBUG_DEFAULT_FLAGS (SLAB_DEBUG_FREE | SLAB_RED_ZONE | \
> - SLAB_POISON | SLAB_STORE_USER)
> + SLAB_POISON | SLAB_STORE_USER | SLAB_QUARANTINE)
>
> /*
> * Debugging flags that require metadata to be stored in the slab. These get
> @@ -454,6 +454,8 @@ static void get_map(struct kmem_cache *s, struct page *page, unsigned long *map)
>
> for (p = page->freelist; p; p = get_freepointer(s, p))
> set_bit(slab_index(p, s, addr), map);
> + for (p = page->quarantine; p; p = get_freepointer(s, p))
> + set_bit(slab_index(p, s, addr), map);
> }
>
> /*
> @@ -921,6 +923,30 @@ static int on_freelist(struct kmem_cache *s, struct page *page, void *search)
> nr++;
> }
>
> + fp = page->quarantine;
> + while (fp && nr <= page->objects) {
> + if (fp == search)
> + return 1;
> + if (!check_valid_pointer(s, page, fp)) {
> + if (object) {
> + object_err(s, page, object,
> + "Quarantine corrupt");
> + set_freepointer(s, object, NULL);
> + } else {
> + slab_err(s, page, "Quarantine corrupt");
> + page->quarantine = NULL;
> + slab_fix(s, "Quarantine cleared");
> + return 0;
> + }
> + break;
> + }
> + object = fp;
> + fp = get_freepointer(s, object);
> + /* In frozen slubs qurantine included into inuse */
> + if (!page->frozen)
> + nr++;
> + }
> +
> max_objects = order_objects(compound_order(page), s->size, s->reserved);
> if (max_objects > MAX_OBJS_PER_PAGE)
> max_objects = MAX_OBJS_PER_PAGE;
> @@ -980,6 +1006,219 @@ static void remove_full(struct kmem_cache *s, struct kmem_cache_node *n, struct
> list_del(&page->lru);
> }
>
> +/*
> + * Quarantine management.
> + */
> +static void add_quarantine(struct kmem_cache *s, struct kmem_cache_node *n,
> + struct page *page)
> +{
> + list_add_tail(&page->lru, &n->quarantine.slabs);
> + n->quarantine.nr_slabs++;
> +
> + if (!(s->flags & SLAB_RECLAIM_ACCOUNT)) {
> + struct zone *zone = page_zone(page);
> + int size = 1 << compound_order(page);
> +
> + mod_zone_page_state(zone, NR_SLAB_UNRECLAIMABLE, -size);
> + mod_zone_page_state(zone, NR_SLAB_RECLAIMABLE, size);
> + }
> +}
> +
> +static void remove_quarantine(struct kmem_cache *s, struct kmem_cache_node *n,
> + struct page *page)
> +{
> + list_del(&page->lru);
> + n->quarantine.nr_slabs--;
> +
> + if (!(s->flags & SLAB_RECLAIM_ACCOUNT)) {
> + struct zone *zone = page_zone(page);
> + int size = 1 << compound_order(page);
> +
> + mod_zone_page_state(zone, NR_SLAB_RECLAIMABLE, -size);
> + mod_zone_page_state(zone, NR_SLAB_UNRECLAIMABLE, size);
> + }
> +}
> +
> +static inline void dec_slabs_node(struct kmem_cache *s, int node, int objects);
> +
> +static bool put_in_qurantine(struct kmem_cache *s, struct kmem_cache_node *n,
> + struct page *page, void *object)
> +{
> + unsigned long counters;
> + struct page new;
> +
> + if (!(s->flags & SLAB_QUARANTINE))
> + return false;
> +
> + set_freepointer(s, object, page->quarantine);
> + page->quarantine = object;
> + n->quarantine.nr_objects++;
> +
> + /* deactivate_slab takes care about updating inuse */
> + if (page->frozen)
> + return true;
> +
> + do {
> + new.freelist = page->freelist;
> + counters = page->counters;
> + new.counters = counters;
> + new.inuse--;
> + } while (!cmpxchg_double_slab(s, page,
> + new.freelist, counters,
> + new.freelist, new.counters,
> + "put_in_qurantine"));
> +
> + /* All objects in quarantine, move slab into quarantine */
> + if (!new.inuse && !new.freelist) {
> + remove_full(s, n, page);
> + dec_slabs_node(s, page_to_nid(page), page->objects);
> + add_quarantine(s, n, page);
> + }
> +
> + return true;
> +}
> +
> +/* Moves objects from quarantine into freelist */
> +static void __flush_quarantine(struct kmem_cache *s, struct kmem_cache_node *n,
> + struct page *page)
> +{
> + void *object, *next;
> +
> + for (object = page->quarantine; object; object = next) {
> + next = get_freepointer(s, object);
> + set_freepointer(s, object, page->freelist);
> + page->freelist = object;
> + n->quarantine.nr_objects--;
> + }
> + page->quarantine = NULL;
> +}
> +
> +static void free_slab(struct kmem_cache *s, struct page *page);
> +
> +static void free_quarantine(struct kmem_cache *s, struct kmem_cache_node *n)
> +{
> + struct page *page, *next;
> +
> + list_for_each_entry_safe(page, next, &n->quarantine.slabs, lru) {
> + __flush_quarantine(s, n, page);
> + remove_quarantine(s, n, page);
> + free_slab(s, page);
> + stat(s, FREE_SLAB);
> + }
> +}
> +
> +static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n,
> + bool close);
> +
> +static void flush_quarantine(struct kmem_cache *s)
> +{
> + struct kmem_cache_node *n;
> + unsigned long flags;
> + int node;
> +
> + for_each_kmem_cache_node(s, node, n) {
> + spin_lock_irqsave(&n->list_lock, flags);
> + free_quarantine(s, n);
> + free_partial(s, n, false);
> + spin_unlock_irqrestore(&n->list_lock, flags);
> + }
> +}
> +
> +struct kmem_cache_quarantine_shrinker {
> + struct shrinker shrinker;
> + struct kmem_cache *cache;
> +};
> +
> +static struct kmem_cache *quarantine_shrinker_to_cache(struct shrinker *s)
> +{
> + return container_of(s, struct kmem_cache_quarantine_shrinker,
> + shrinker)->cache;
> +}
> +
> +static unsigned long count_quarantine(struct shrinker *shrinker,
> + struct shrink_control *sc)
> +{
> + struct kmem_cache *s = quarantine_shrinker_to_cache(shrinker);
> + struct kmem_cache_node *n = get_node(s, sc->nid);
> +
> + return n ? n->quarantine.nr_slabs : 0;
> +}
> +
> +/*
> + * This reclaims only completely quarantined slabs.
> + */
> +static unsigned long shrink_quarantine(struct shrinker *shrinker,
> + struct shrink_control *sc)
> +{
> + struct kmem_cache *s = quarantine_shrinker_to_cache(shrinker);
> + struct kmem_cache_node *n = get_node(s, sc->nid);
> + unsigned long flags, freed = 0;
> + struct page *page, *next;
> +
> + if (list_empty(&n->quarantine.slabs))
> + return SHRINK_STOP;
> +
> + spin_lock_irqsave(&n->list_lock, flags);
> + list_for_each_entry_safe(page, next, &n->quarantine.slabs, lru) {
> + if (!sc->nr_to_scan--)
> + break;
> +
> + /* A half goes to another round after examination */
> + if (sc->nr_to_scan & 1) {
> + void *p;
> +
> + check_slab(s, page);
> + on_freelist(s, page, NULL);
> + for_each_object(p, s, page_address(page), page->objects)
> + check_object(s, page, p, SLUB_RED_INACTIVE);
> + list_move(&page->lru, &n->quarantine.slabs);
> + continue;
> + }
> +
> + __flush_quarantine(s, n, page);
> + remove_quarantine(s, n, page);
> + free_slab(s, page);
> + stat(s, QUARANTINE_BREACH);
> + stat(s, FREE_SLAB);
> + freed++;
> + }
> + spin_unlock_irqrestore(&n->list_lock, flags);
> +
> + return freed;
> +}
> +
> +static int register_quarantine_shrinker(struct kmem_cache *s)
> +{
> + if ((slab_state >= FULL) && (s->flags & SLAB_QUARANTINE)) {
> + struct kmem_cache_quarantine_shrinker *qs;
> +
> + qs = kmalloc(sizeof(*qs), GFP_KERNEL);
> + if (!qs)
> + return -ENOMEM;
> +
> + s->quarantine_shrinker = qs;
> + qs->cache = s;
> +
> + qs->shrinker.count_objects = count_quarantine;
> + qs->shrinker.scan_objects = shrink_quarantine;
> + qs->shrinker.flags = SHRINKER_NUMA_AWARE;
> + qs->shrinker.seeks = DEFAULT_SEEKS; /* make it tunable? */
> + qs->shrinker.batch = 0;
> +
> + return register_shrinker(&qs->shrinker);
> + }
> + return 0;
> +}
> +
> +static void unregister_quarantine_shrinker(struct kmem_cache *s)
> +{
> + if (s->flags & SLAB_QUARANTINE) {
> + unregister_shrinker(&s->quarantine_shrinker->shrinker);
> + kfree(s->quarantine_shrinker);
> + s->quarantine_shrinker = NULL;
> + }
> +}
> +
> /* Tracking of the number of slabs for debugging purposes */
> static inline unsigned long slabs_node(struct kmem_cache *s, int node)
> {
> @@ -1108,6 +1347,12 @@ static noinline struct kmem_cache_node *free_debug_processing(
> init_object(s, object, SLUB_RED_INACTIVE);
> out:
> slab_unlock(page);
> +
> + if (put_in_qurantine(s, n, page, object)) {
> + spin_unlock_irqrestore(&n->list_lock, *flags);
> + n = NULL;
> + }
> +
> /*
> * Keep node_lock to preserve integrity
> * until the object is actually freed
> @@ -1176,6 +1421,9 @@ static int __init setup_slub_debug(char *str)
> case 'a':
> slub_debug |= SLAB_FAILSLAB;
> break;
> + case 'q':
> + slub_debug |= SLAB_QUARANTINE;
> + break;
> default:
> pr_err("slub_debug option '%c' unknown. skipped\n",
> *str);
> @@ -1241,7 +1489,13 @@ static inline void inc_slabs_node(struct kmem_cache *s, int node,
> int objects) {}
> static inline void dec_slabs_node(struct kmem_cache *s, int node,
> int objects) {}
> -
> +static inline int register_quarantine_shrinker(struct kmem_cache *s)
> + { return 0; }
> +static inline void unregister_quarantine_shrinker(struct kmem_cache *s) {}
> +static inline void free_quarantine(struct kmem_cache *s,
> + struct kmem_cache_node *n) {}
> +static inline void __flush_quarantine(struct kmem_cache *s,
> + struct kmem_cache_node *n, struct page *page) {}
> #endif /* CONFIG_SLUB_DEBUG */
>
> /*
> @@ -1448,6 +1702,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node)
> }
>
> page->freelist = start;
> + page->quarantine = NULL;
> page->inuse = page->objects;
> page->frozen = 1;
> out:
> @@ -1525,6 +1780,9 @@ static void free_slab(struct kmem_cache *s, struct page *page)
>
> static void discard_slab(struct kmem_cache *s, struct page *page)
> {
> + /* FIXME race with quarantine_store('0')
> + * n->quarantine.nr_objects isn't uptodate */
> + page->quarantine = NULL;
> dec_slabs_node(s, page_to_nid(page), page->objects);
> free_slab(s, page);
> }
> @@ -1861,6 +2119,29 @@ static void deactivate_slab(struct kmem_cache *s, struct page *page,
> freelist = nextfree;
> }
>
> + if (IS_ENABLED(CONFIG_SLUB_DEBUG) &&
> + unlikely(s->flags & SLAB_QUARANTINE)) {
> + int objects = 0;
> +
> + lock = 1;
> + spin_lock(&n->list_lock);
> +
> + for (nextfree = page->quarantine; nextfree;
> + nextfree = get_freepointer(s, nextfree))
> + objects++;
> +
> + do {
> + old.freelist = page->freelist;
> + old.counters = page->counters;
> + new.counters = old.counters;
> + VM_BUG_ON(objects > new.inuse);
> + new.inuse -= objects;
> + } while (!__cmpxchg_double_slab(s, page,
> + old.freelist, old.counters,
> + old.freelist, new.counters,
> + "commit quarantine"));
> + }
> +
> /*
> * Stage two: Ensure that the page is unfrozen while the
> * list presence reflects the actual number of objects
> @@ -2887,6 +3168,9 @@ init_kmem_cache_node(struct kmem_cache_node *n)
> atomic_long_set(&n->nr_slabs, 0);
> atomic_long_set(&n->total_objects, 0);
> INIT_LIST_HEAD(&n->full);
> + n->quarantine.nr_slabs = 0;
> + n->quarantine.nr_objects = 0;
> + INIT_LIST_HEAD(&n->quarantine.slabs);
> #endif
> }
>
> @@ -2939,6 +3223,7 @@ static void early_kmem_cache_node_alloc(int node)
> n = page->freelist;
> BUG_ON(!n);
> page->freelist = get_freepointer(kmem_cache_node, n);
> + page->quarantine = NULL;
> page->inuse = 1;
> page->frozen = 0;
> kmem_cache_node->node[node] = n;
> @@ -3186,9 +3471,17 @@ static int kmem_cache_open(struct kmem_cache *s, unsigned long flags)
> if (!init_kmem_cache_nodes(s))
> goto error;
>
> - if (alloc_kmem_cache_cpus(s))
> - return 0;
> + if (!alloc_kmem_cache_cpus(s))
> + goto error_cpus;
> +
> + if (register_quarantine_shrinker(s))
> + goto error_shrinker;
> +
> + return 0;
>
> +error_shrinker:
> + free_percpu(s->cpu_slab);
> +error_cpus:
> free_kmem_cache_nodes(s);
> error:
> if (flags & SLAB_PANIC)
> @@ -3230,15 +3523,17 @@ static void list_slab_objects(struct kmem_cache *s, struct page *page,
> * This is called from kmem_cache_close(). We must be the last thread
> * using the cache and therefore we do not need to lock anymore.
> */
> -static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n)
> +static void free_partial(struct kmem_cache *s, struct kmem_cache_node *n,
> + bool close)
> {
> struct page *page, *h;
>
> list_for_each_entry_safe(page, h, &n->partial, lru) {
> + __flush_quarantine(s, n, page);
> if (!page->inuse) {
> __remove_partial(n, page);
> discard_slab(s, page);
> - } else {
> + } else if (close) {
> list_slab_objects(s, page,
> "Objects remaining in %s on kmem_cache_close()");
> }
> @@ -3254,9 +3549,11 @@ static inline int kmem_cache_close(struct kmem_cache *s)
> struct kmem_cache_node *n;
>
> flush_all(s);
> + unregister_quarantine_shrinker(s);
> /* Attempt to free all objects */
> for_each_kmem_cache_node(s, node, n) {
> - free_partial(s, n);
> + free_quarantine(s, n);
> + free_partial(s, n, true);
> if (n->nr_partial || slabs_node(s, node))
> return 1;
> }
> @@ -4247,7 +4544,8 @@ enum slab_stat_type {
> SL_PARTIAL, /* Only partially allocated slabs */
> SL_CPU, /* Only slabs used for cpu caches */
> SL_OBJECTS, /* Determine allocated objects not slabs */
> - SL_TOTAL /* Determine object capacity not slabs */
> + SL_TOTAL, /* Determine object capacity not slabs */
> + SL_QUARANTINE, /* Determine objects in quarantine */
> };
>
> #define SO_ALL (1 << SL_ALL)
> @@ -4255,6 +4553,7 @@ enum slab_stat_type {
> #define SO_CPU (1 << SL_CPU)
> #define SO_OBJECTS (1 << SL_OBJECTS)
> #define SO_TOTAL (1 << SL_TOTAL)
> +#define SO_QUARANTINE (1 << SL_QUARANTINE)
>
> static ssize_t show_slab_objects(struct kmem_cache *s,
> char *buf, unsigned long flags)
> @@ -4325,6 +4624,17 @@ static ssize_t show_slab_objects(struct kmem_cache *s,
> nodes[node] += x;
> }
>
> + } else if (flags & SO_QUARANTINE) {
> + struct kmem_cache_node *n;
> +
> + for_each_kmem_cache_node(s, node, n) {
> + if (flags & SO_OBJECTS)
> + x = n->quarantine.nr_objects;
> + else
> + x = n->quarantine.nr_slabs;
> + total += x;
> + nodes[node] += x;
> + }
> } else
> #endif
> if (flags & SO_PARTIAL) {
> @@ -4597,6 +4907,58 @@ static ssize_t total_objects_show(struct kmem_cache *s, char *buf)
> }
> SLAB_ATTR_RO(total_objects);
>
> +static ssize_t quarantine_slabs_show(struct kmem_cache *s, char *buf)
> +{
> + return show_slab_objects(s, buf, SO_QUARANTINE);
> +}
> +SLAB_ATTR_RO(quarantine_slabs);
> +
> +static ssize_t quarantine_objects_show(struct kmem_cache *s, char *buf)
> +{
> + return show_slab_objects(s, buf, SO_QUARANTINE|SO_OBJECTS);
> +}
> +
> +static ssize_t quarantine_objects_store(struct kmem_cache *s,
> + const char *buf, size_t length)
> +{
> + if (buf[0] == '0')
> + flush_quarantine(s);
> + return length;
> +}
> +SLAB_ATTR(quarantine_objects);
> +
> +static ssize_t quarantine_show(struct kmem_cache *s, char *buf)
> +{
> + return sprintf(buf, "%d\n", !!(s->flags & SLAB_QUARANTINE));
> +}
> +
> +static ssize_t quarantine_store(struct kmem_cache *s,
> + const char *buf, size_t length)
> +{
> + if (buf[0] == '1') {
> + if (!(s->flags & SLAB_QUARANTINE)) {
> + s->flags &= ~__CMPXCHG_DOUBLE;
> + s->flags |= SLAB_QUARANTINE;
> + flush_all(s); /* FIXME stil racy? */
> + if (register_quarantine_shrinker(s)) {
> + s->flags &= ~SLAB_QUARANTINE;
> + flush_quarantine(s);
> + return -ENOMEM;
> + }
> + }
> + } else {
> + if (s->flags & SLAB_QUARANTINE) {
> + unregister_quarantine_shrinker(s);
> + s->flags &= ~SLAB_QUARANTINE;
> + /* FIXME race with deactivate_slab */
> + flush_all(s);
> + flush_quarantine(s);
> + }
> + }
> + return length;
> +}
> +SLAB_ATTR(quarantine);
> +
> static ssize_t sanity_checks_show(struct kmem_cache *s, char *buf)
> {
> return sprintf(buf, "%d\n", !!(s->flags & SLAB_DEBUG_FREE));
> @@ -4877,6 +5239,7 @@ STAT_ATTR(CPU_PARTIAL_ALLOC, cpu_partial_alloc);
> STAT_ATTR(CPU_PARTIAL_FREE, cpu_partial_free);
> STAT_ATTR(CPU_PARTIAL_NODE, cpu_partial_node);
> STAT_ATTR(CPU_PARTIAL_DRAIN, cpu_partial_drain);
> +STAT_ATTR(QUARANTINE_BREACH, quarantine_breach);
> #endif
>
> static struct attribute *slab_attrs[] = {
> @@ -4910,6 +5273,9 @@ static struct attribute *slab_attrs[] = {
> &validate_attr.attr,
> &alloc_calls_attr.attr,
> &free_calls_attr.attr,
> + &quarantine_attr.attr,
> + &quarantine_slabs_attr.attr,
> + &quarantine_objects_attr.attr,
> #endif
> #ifdef CONFIG_ZONE_DMA
> &cache_dma_attr.attr,
> @@ -4944,6 +5310,7 @@ static struct attribute *slab_attrs[] = {
> &cpu_partial_free_attr.attr,
> &cpu_partial_node_attr.attr,
> &cpu_partial_drain_attr.attr,
> + &quarantine_breach_attr.attr,
> #endif
> #ifdef CONFIG_FAILSLAB
> &failslab_attr.attr,
> @@ -5285,6 +5652,11 @@ static int __init slab_sysfs_init(void)
> if (err)
> pr_err("SLUB: Unable to add boot slab %s to sysfs\n",
> s->name);
> +
> + err = register_quarantine_shrinker(s);
> + if (err)
> + pr_err("SLUB: Unable to register quarantine shrinker %s",
> + s->name);
> }
>
> while (alias_list) {
> --
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/