Kernel memory accounting container (v4)
Subsystems (3)
cover Kernel memory accounting container (v4)
Long time ago we decided to start memory control with the user memory container. Now this container in -mm tree and I think we can start with (at least discussion of) the kmem one.
Changes since v.3:
* moved alloc/free notification into slow path and make "notify-able" caches walk this path always;
* introduced some optimization for the case, when there's only one listener for SLUB events (saves more that 10% of performance);
* ported on 2.6.23-rc6-mm1 tree.
Changes since v.2:
* introduced generic notifiers for slub. right now there are only events, needed by accounting, but this set can be extended in the future;
* moved the controller core into separate file, so that its extension and/or porting on slAb will look more logical;
* fixed this message :).
Changes since v.1:
* fixed Paul's comment about subsystem registration;
* return ERR_PTR from ->create callback, not NULL;
* make container-to-object assignment in rcu-safe section;
* make turning accounting on and off with "1" and "0".
============================================================
First of all - why do we need this kind of control. The major "pros" is that kernel memory control protects the system from DoS attacks by processes that live in container. As our experience shows many exploits simply do not work in the container with limited kernel memory.
I can split the kernel memory container into 4 parts:
1. kmalloc-ed objects control
2. vmalloc-ed objects control
3. buddy allocated pages control
4. kmem_cache_alloc-ed objects control
the control of first tree types of objects has one peculiarity:
one need to explicitly point out which allocations he wants to account and this becomes not-configurable and is to be discussed.
On the other hands such objects as anon_vma-s, file-s, sighangds, vfsmounts, etc are created by user request always and should always be accounted. Fortunately they are allocated from their own caches and thus the whole kmem cache can be accountable.
This is exactly what this patchset does - it adds the ability to account for the total size of kmem-cache-allocated objects from specified kmem caches.
This is based on the SLUB allocator, Paul's containers and the resource counters I made for RSS controller and which are in -mm tree already.
To play with it, one need to mount the container file system with -o kmem and then mark some caches as accountable via /sys/slab/<cache_name>/cache_notify.
As I have already told kmalloc caches cannot be accounted easily so turning the accounting on for them will fail with -EINVAL.
Turning the accounting off is possible only if the cache has no objects. This is done so because turning accounting off implies unaccounting of all the objects in the cache, but due to full-pages in slub are not stored in any lists (usually) this is impossible to do so, however I'm open for discussion of how to make this work.
Thanks,
Pavel
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/4 Add notification about some major slab events
According to Christoph, there are already multiple people who want to control slab allocations and track memory for various reasons. So this is an introduction of such a hooks.
Currently, functions that are to call the notifiers are empty and marked as "weak". Thus, if there's only _one_ listener to these events, it can happily link with the vmlinux and handle the events with more than 10% of performance saved.
The events tracked are:
1. allocation of an object;
2. freeing of an object;
3. allocation of a new page for objects;
4. freeing this page.
More events can be added on demand.
The kmem cache marked with SLAB_NOTIFY flag will cause all the events above to generate notifications. By default no caches come with this flag.
The events are generated on slow paths only and as soon as the cache is marked as SLAB_NOTIFY, it will always use them for allocation.
---
include/linux/slab.h +1 -0
@@ -28,6 +28,7 @@#define SLAB_DESTROY_BY_RCU 0x00080000UL/* Defer freeing slabs to RCU */ #define SLAB_MEM_SPREAD 0x00100000UL/* Spread some memory over cpuset */ #define SLAB_TRACE 0x00200000UL/* Trace allocations and frees */ +#define SLAB_NOTIFY 0x00400000UL/* Notify major events */ /* The following flags affect the page allocator grouping pages by mobility */ #define SLAB_RECLAIM_ACCOUNT 0x00020000UL/* Objects are reclaimable */
mm/slub.c +52 -5
110 lines not on this page · raw message
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 1/4] Add notification about some major slab events
mm/slub.c load_freelist: · line 1601
Quoted 7 lines · Pavel Emelyanov
mm/slub.c +1
-1@@ -1486,7 +1597,7 @@ load_freelist: object = c->page->freelist;if (unlikely (!object)) goto another_slab; -if (unlikely (SlabDebug (c->page))) +if (unlikely (SlabDebug (c->page)) || (s->flags &SLAB_NOTIFY )) goto debug;
There is no need to check s->flags & SLAB_NOTIFY here. Just make sure that SlabDebug() is always set on allocated slabs. Set SlabDebug when a slab page is allocated is sufficient.
mm/slub.c new_slab: · line 1661
Quoted 7 lines
mm/slub.c +2
-1debug: object = c->page->freelist; -if (!alloc_debug_processing (s, c->page, object, addr)) +if (SlabDebug (c->page) && + !alloc_debug_processing (s, c->page, object, addr)) goto another_slab;
SlabDebug will always be set and does not need to be tested.
mm/slub.c new_slab: · line 1667
Quoted 5 lines
mm/slub.c +5
-0+if ((s->flags &SLAB_NOTIFY ) && +slub_alloc_notify (s, object, gfpflags) <0 ) { + object =NULL ; + goto out; + }
Looks like the above piece is the only addition we need in __slab_alloc().
mm/slub.c static void __slab_free(struct kmem_cach · line 1743
Quoted 7 lines
mm/slub.c +1
-1@@ -1620,7 +1739,7 @@ static void __slab_free(struct kmem_cachslab_lock (page); -if (unlikely (SlabDebug (page))) +if (unlikely (SlabDebug (page)) || (s->flags &SLAB_NOTIFY )) goto debug;
Not needed either.
mm/slub.c slab_empty: · line 1780
Quoted 9 lines
mm/slub.c +1
-1checks_ok: prior = object[offset] = page->freelist;@@ -1657,8 +1776,12 @@ slab_empty:return ; debug: -if (!free_debug_processing (s, page, x, addr)) +if (SlabDebug (page) && !free_debug_processing (s, page, x, addr)) goto out_unlock;
Ditto.
mm/slub.c slab_empty: · line 1784
Quoted 4 lines
mm/slub.c +4
-0+ +if (s->flags &SLAB_NOTIFY ) +slub_free_notify (s, x); +
Yup we need this for __slab_free.
PATCH 2/5 Generic notifiers for SLUB events
In reply to a message not in the archiveIf the user wants more than one listener for SLUB event, it can register them all as notifier_block-s so all of them will be notified.
This costs us about 10% of performance loss, in comparison with static linking.
The selected method of notification is srcu notifier blocks. This is selected because the "call" path, i.e. when the notification is done, is lockless and at the same time the handlers can sleep. Neither regular nor atomic notifiers provide such facilities.
---
init/Kconfig +10 -0
@@ -593,6 +599,16 @@ config SLUB_DEBUG SLUB sysfs support. /sys/slab will not exist and there will be no support for cache validation etc. +config SLUB_NOTIFY + default y + bool "Enable SLUB events generic notification" + depends on SLUB + help + When Y, this option enables generic notifications of some major + slub events. However, if you do know that there will be the + only listener for them, you may say N here, so that callbacks + will be called directly. + choice prompt "Choose SLAB allocator" default SLUB
include/linux/slub_def.h +16 -0
@@ -200,4 +202,20 @@ static __always_inline void *kmalloc_nod }#endif +struct slub_notify_struct { +struct kmem_cache *cachep; +void *objp; +gfp_t gfp; +}; + +enum { +SLUB_ALLOC , +SLUB_FREE , +SLUB_NEWPAGE , +SLUB_FREEPAGE , +}; + +int slub_register_notifier (struct notifier_block *nb); +void slub_unregister_notifier (struct notifier_block *nb); +#endif /* _LINUX_SLUB_DEF_H */
mm/slub.c +79 -2
99 lines not on this page · raw message
PATCH 3/5 Switch caches notification dynamically
In reply to a message not in the archiveThe /sys/slab/<name>/cache_notify attribute controls whether the cache <name> is to be accounted or not.
For the reasons described before kmalloc caches cannot be turned on.
By default no caches are accountable. Simply make
# echo -n 1 > /sys/slab/<name>cache_notify to turn notification of this cache on.
If we turn accounting on on some cache and this cache is merged with some other, this "other" will be notified as well. We can solve this by disabling of cache merging.
Turning the notification off is possible only when this cache is empty. The reason for this is that the pages, that are full of objects are not linked in any list, so we wouldn't be able to walk these pages and notify others that these objects are no longer tracked.
---
mm/slub.c +31 -0
@@ -3775,6 +3917,36 @@ static ssize_t defrag_ratio_store(structSLAB_ATTR (defrag_ratio);#endif +static ssize_t cache_notify_show (struct kmem_cache *s,char *buf) +{ +return sprintf (buf,"%d\n" , !!(s->flags &SLAB_NOTIFY )); +} + +static ssize_t cache_notify_store (struct kmem_cache *s, +const char *buf,size_t length) +{ +if (buf[0 ] =='1' ) { + s->flags |=SLAB_NOTIFY ; +return length; + } + +if (buf[0 ] =='0' ) { +if (any_slab_objects (s)) +/* +* we cannot turn this off because of the +* full slabs cannot be found in this case +*/ +return -EBUSY ; + + s->flags &= ~SLAB_NOTIFY ; +return length; + } + +return -EINVAL ; +} + +SLAB_ATTR (cache_notify); +static struct attribute * slab_attrs[] = { &slab_size_attr.attr, &object_size_attr.attr,
@@ -3805,6 +3984,7 @@ static struct attribute * slab_attrs[] =#ifdef CONFIG_NUMA &defrag_ratio_attr.attr, #endif + &cache_notify_attr.attr,NULL };
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH 3/5] Switch caches notification dynamically
Quoted 5 lines · Pavel Emelyanov
The /sys/slab/<name>/cache_notify attribute controls whether the cache <name> is to be accounted or not.
For the reasons described before kmalloc caches cannot be turned on.
It looks like the patch is forbidding to turn the notification off? On is okay even if there are already objects present? Full slabs may exist that cannot be accounted for now. I guess that is okay because we only want to track new allocations?
A message that is not in the archive (sent off-list or not yet received): Pine.LNX.4.64.0709241406280.30982-RYO/[email protected]
Re: [PATCH 3/5] Switch caches notification dynamically
Quoted 10 lines · Christoph Lameter
Quoted 5 lines · Pavel Emelyanov
The /sys/slab/<name>/cache_notify attribute controls whether the cache <name> is to be accounted or not.
For the reasons described before kmalloc caches cannot be turned on.
It looks like the patch is forbidding to turn the notification off? On is okay even if there are already objects present?
Yup. If we "on" the notifications we will just receive new events.
Quoted 2 lines
Full slabs may exist that cannot be accounted for now. I guess that is okay because we only want to track new allocations?
If we turn notifications off with full slabs in memory we won't be able to (in case of kmem accounting) release the page's metadata that the slub_newpage event attached to them.
We can make this by tracking all the full slabs, and at the moment of "off" walk them and generate slab_free and slab_release_page events, but does it worth doing it ever?
Thanks,
Pavel
PATCH 4/5 Setup the container
In reply to a message not in the archiveAttach the controller to the containers. This will work with the SLUB allocator only. However, if we need I can port this on SLAB (and maybe SLOB ;) ).
This setup is simple and stupid.
---
include/linux/container_subsys.h +6 -0
@@ -36,3 +36,9 @@ SUBSYS(mem_container)#endif /* */ + +#ifdef CONFIG_CONTAINER_KMEM +SUBSYS(kmem) +#endif + +/* */
init/Kconfig +6 -0
@@ -353,6 +353,12 @@ config CONTAINER_MEM_CONT Provides a memory controller that manages both page cache and RSS memory. +config CONTAINER_KMEM + bool "Kernel memory controller for containers" + depends on CONTAINERS && RESOURCE_COUNTERS && SLUB + help + Provides a kernel memory usage control for containers + config PROC_PID_CPUSET bool "Include legacy /proc/<pid>/cpuset file" depends on CPUSETS
mm/Makefile +1 -0
@@ -31,4 +31,5 @@ obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o obj-$(CONFIG_CONTAINER_MEM_CONT) += memcontrol.o +obj-$(CONFIG_CONTAINER_KMEM) += kmemcontrol.o
mm/kmemcontrol.c new file +123 -0
123 lines not on this page · raw message
PATCH 5/5 Account for the slub objects
In reply to a message not in the archiveThe struct page gets an extra pointer (just like it has with the RSS controller) and this pointer points to the array of the kmem_container pointers - one for each object stored on that page itself.
Thus the i'th object on the page is accounted to the container pointed by the i'th pointer on that array and when the object is freed we unaccount its size to this particular container, not the container current task belongs to.
This is done so, because the context objects are freed is most often not the same as the one this objects was allocated in (due to RCU and reference counters).
---
include/linux/mm_types.h +6 -1
@@ -83,9 +83,14 @@ struct page {void *virtual;/* Kernel virtual address (NULL if not kmapped, ie. highmem) */ #endif /* WANT_PAGE_VIRTUAL */ +union {#ifdef CONFIG_CONTAINER_MEM_CONT -unsigned long page_container; +unsigned long page_container; +#endif +#ifdef CONFIG_CONTAINER_KMEM +struct kmem_container **containers;#endif + };#ifdef CONFIG_PAGE_OWNER int order;unsigned int gfp_mask;
include/linux/slub_def.h +2 -0
@@ -69,6 +69,8 @@ struct kmem_cache {#endif }; +int slab_index (void *p,struct kmem_cache *s,void *addr); +/* * Kmalloc subsystem. */
mm/kmemcontrol.c new file +128 -0
137 lines not on this page · raw message
mm/slub.c +26 -1
@@ -327,7 +327,7 @@ static inline void set_freepointer(strucfor (__p = (__free); __p; __p =get_freepointer ((__s), __p))/* Determine object index from a given position */ -static inline int slab_index(void *p, struct kmem_cache *s, void *addr) +inline int slab_index(void *p, struct kmem_cache *s, void *addr) {return (p - addr) / s->size; }
@@ -2360,6 +2483,14 @@ EXPORT_SYMBOL(kmem_cache_destroy); struct kmem_cache kmalloc_caches[PAGE_SHIFT] __cacheline_aligned;EXPORT_SYMBOL (kmalloc_caches); +static inline int is_kmalloc_cache (struct kmem_cache *s) +{ +int km_idx; + + km_idx = s - kmalloc_caches; +return km_idx >=0 && km_idx <ARRAY_SIZE (kmalloc_caches); +} +#ifdef CONFIG_ZONE_DMA static struct kmem_cache *kmalloc_caches_dma[PAGE_SHIFT ];#endif
@@ -2811,6 +2943,16 @@ static int slab_unmergeable(struct kmem_if (s->refcount <0 )return 1 ; +#ifdef CONFIG_CONTAINER_KMEM +/* +* many caches that can be accountable are usually merged with +* kmalloc caches, which are disabled for accounting for a while +*/ + +if (is_kmalloc_cache (s)) +return 1 ; +#endif +return 0 ; }
@@ -3775,6 +3917,13 @@ static ssize_t defrag_ratio_store(struct const char *buf, size_t length) { if (buf[0] == '1') { +#ifdef CONFIG_CONTAINER_KMEM + if (is_kmalloc_cache(s)) + /* + * cannot just make these caches accountable + */ + return -EINVAL; +#endif s->flags |=SLAB_NOTIFY ;return length; }
Quoted 5 lines · Pavel Emelyanov
Long time ago we decided to start memory control with the user memory container. Now this container in -mm tree and I think we can start with (at least discussion of) the kmem one.
Hi, Pavel,
Yes, I remember the entire discussion. Also in the TODO list is an mlock() controller and a virtual memory controller. Just wanted to ensure that they are still in the TODO list.
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL