RSS controller based on process containers (v3.1)
Subsystems (12)
cover RSS controller based on process containers (v3.1)
Adds RSS accounting and control within a container.
Changes from v3
- comments across the code
- git-bisect safe split
- lost places to move the page between active/inactive lists
Ported above Paul's containers V10 with fixes from Balbir.
RSS container includes the per-container RSS accounting and reclamation, and out-of-memory killer.
Each mapped page has an owning container and is linked into its LRU lists just like in the global LRU ones. The owner of the page is the container that touched the page first. As long as the page stays mapped it holds the container, is accounted into its usage and lives in its LRU list. When page is unmapped for the last time it releases the container. The RSS usage is exactly the number of pages in its booth LRU lists, i.e. the nu,ber of pages used by this container. When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
Thus the container behaves like a standalone machine - when it runs out of resources, it tries to reclaim some pages, and if it doesn't succeed, kills some task.
The testing scenario may look like this:
1. Prepare the containers
# mkdir -p /containers/rss # mount -t container none /containers/rss -o rss
2. Make the new group and move bash into it
# mkdir /containers/rss/0 # echo $$ > /containers/rss/0/tasks
Since now we're in the 0 container.
We can alter the RSS limit
# echo -n 6000 > /containers/rss/0/rss_limit
We can check the usage
# cat /containers/rss/0/rss_usage 25
And do other stuff. To check the reclamation to work we need a simple program that touches many pages of memory, like this:
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#ifndef PGSIZE
#define PGSIZE 4096
#endif
int main(int argc, char **argv)
{
unsigned long pages;
int i;
char *mem;
if (argc < 2) {
printf("Usage: %s <number_of_pages>\n", argv[0]);
return 1;
}
pages = strtol(argv[1], &mem, 10);
if (*mem != '\0') {
printf("Bad number %s\n", argv[1]);
return 1;
}
mem = mmap(NULL, pages * PGSIZE, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, 0, 0);
if (mem == MAP_FAILED) {
perror("map");
return 2;
}
for (i = 0; i < pages; i++)
mem[i * PGSIZE] = 0;
printf("OK\n");
return 0;
}PATCH 1/8 Resource counters
Introduce generic structures and routines for resource accounting.
Each resource accounting container is supposed to aggregate it, container_subsystem_state and its resource-specific members within.
---
include/linux/res_counter.h +102 -0
@@ -0,0 +1,102 @@ +#ifndef __RES_COUNTER_H__ +#define __RES_COUNTER_H__ + +/* +* resource counters +* contain common data types and routines for resource accounting +* +* Copyright 2007 OpenVZ SWsoft Inc +* +* Author: Pavel Emelianov <[email protected]> +* +*/ + +#include <linux/container.h> + +/* +* the core object. the container that wishes to account for some +* resource may include this counter into its structures and use +* the helpers described beyond +*/ + +struct res_counter { +/* +* the current resource consumption level +*/ +unsigned long usage; +/* +* the limit that usage cannot exceed +*/ +unsigned long limit; +/* +* the number of insuccessful attempts to consume the resource +*/ +unsigned long failcnt; +/* +* the lock to protect all of the above. +* the routines below consider this to be IRQ-safe +*/ +spinlock_t lock; +}; + +/* +* helpers to interact with userspace +* res_counter_read/_write - put/get the specified fields from the +* res_counter struct to/from the user +* +* @cnt: the counter in question +* @member: the field to work with (see RES_xxx below) +* @buf: the buffer to opeate on,... +* @nbytes: its size... +* @pos: and the offset. +*/ + +ssize_t res_counter_read (struct res_counter *cnt,int member, +const char __user *buf,size_t nbytes,loff_t *pos); +ssize_t res_counter_write (struct res_counter *cnt,int member, +const char __user *buf,size_t nbytes,loff_t *pos); + +/* +* the field descriptors. one for each member of res_counter +*/ + +enum { +RES_USAGE , +RES_LIMIT , +RES_FAILCNT , +}; + +/* +* helpers for accounting +*/ + +void res_counter_init (struct res_counter *cnt); + +/* +* charge - try to consume more resource. +* +* @cnt: the counter +* @val: the amount of the resource. each controller defines its own +* units, e.g. numbers, bytes, Kbytes, etc +* +* returns 0 on success and <0 if the cnt->usage will exceed the cnt->limit +* _locked call expects the cnt->lock to be taken +*/ + +int res_counter_charge_locked (struct res_counter *cnt,unsigned long val); +int res_counter_charge (struct res_counter *cnt,unsigned long val); + +/* +* uncharge - tell that some portion of the resource is released +* +* @cnt: the counter +* @val: the amount of the resource +* +* these calls check for usage underflow and show a warning on the console +* _locked call expects the cnt->lock to be taken +*/ + +void res_counter_uncharge_locked (struct res_counter *cnt,unsigned long val); +void res_counter_uncharge (struct res_counter *cnt,unsigned long val); + +#endif
init/Kconfig +4 -0
@@ -328,6 +328,10 @@ config CPUSETS Say N if unsure. +config RESOURCE_COUNTERS + bool + select CONTAINERS + config SYSFS_DEPRECATED bool "Create deprecated sysfs files" default y
kernel/Makefile +1 -0
@@ -56,6 +56,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o obj-$(CONFIG_UTS_NS) += utsname.o obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o +obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) # According to Alan Modra <[email protected]>, the -fno-omit-frame-pointer is
kernel/res_counter.c +121 -0
121 lines not on this page · raw message
PATCH 2/8 Add container pointer on struct page
In reply to Pavel EmelianovEach page is supposed to have an owner - the container that touched the page first. The owner stays alive during the page lifetime even if the task that touched the page dies or moves to another container.
This ownership is the forerunner for the "fair" page sharing accounting, in which page has as may owners as it is really used by.
Page ownership getter/setter are done as static inline function.
---
include/linux/mm.h +24 -0
@@ -249,6 +249,30 @@ struct vm_operations_struct {struct mmu_gather ;struct inode ; +struct page_container ; + +#ifdef CONFIG_RSS_CONTAINER +static inline struct page_container *page_container (struct page *pg) +{ +return pg->rss_container; +} + +static inline void set_page_container (struct page *pg, +struct page_container *container) +{ + pg->rss_container = container; +} +#else +static inline struct page_container *page_container (struct page *pg) +{ +return NULL ; +} + +static inline void set_page_container (struct page *pg, +struct page_container *container) +{ +} +#endif #define page_private (page) ((page)->private)#define set_page_private (page, v) ((page)->private = (v))
include/linux/mm_types.h +3 -0
@@ -83,6 +83,9 @@ struct page {unsigned int gfp_mask;unsigned long trace[8 ];#endif +#ifdef CONFIG_RSS_CONTAINER +struct page_container *rss_container; +#endif };#endif /* _LINUX_MM_TYPES_H */
PATCH 3/8 Add container pointer on mm_struct
In reply to Pavel EmelianovNaturally mm_struct determines the resource consumer in memory accounting. So each mm_struct should have a pointer on container it belongs to. When a new task is created its mm_struct is assigned to the container this task belongs to.
include/linux/rss_container.h is added in this patch to make this valid apart from the whole set, but the content of it is just a set of stubs.
---
include/linux/sched.h +3 -0
@@ -390,6 +390,9 @@ struct mm_struct {/* aio bits */ rwlock_t ioctx_list_lock;struct kioctx *ioctx_list; +#ifdef CONFIG_RSS_CONTAINER +struct rss_container *rss_container; +#endif };struct sighand_struct {
include/linux/rss_container.h +19 -0
@@ -0,0 +1,19 @@ +#ifndef __RSS_CONTAINER_H__ +#define __RSS_CONTAINER_H__ +/* +* RSS container +* +* Copyright 2007 OpenVZ SWsoft Inc +* +* Author: Pavel Emelianov <[email protected]> +* +*/ + +static inline void mm_init_container (struct mm_struct *mm,struct task_struct *t) +{ +} + +static inline void mm_free_container (struct mm_struct *mm) +{ +} +#endif
kernel/fork.c +10 -3
@@ -57,6 +57,8 @@#include <asm/cacheflush.h> #include <asm/tlbflush.h> +#include <linux/rss_container.h> +/* * Protected counters by write_lock_irq(&tasklist_lock) */
@@ -329,7 +331,7 @@ static inline void mm_free_pgd(struct mm#include <linux/init_task.h> -static struct mm_struct *mm_init (struct mm_struct * mm) +static struct mm_struct *mm_init (struct mm_struct *mm,struct task_struct *tsk) {atomic_set (&mm->mm_users,1 );atomic_set (&mm->mm_count,1 );
@@ -344,11 +346,14 @@ static struct mm_struct * mm_init(struct mm->ioctx_list =NULL ; mm->free_area_cache =TASK_UNMAPPED_BASE ; mm->cached_hole_size = ~0UL ; +mm_init_container (mm, tsk);if (likely (!mm_alloc_pgd (mm))) { mm->def_flags =0 ;return mm; } + +mm_free_container (mm);free_mm (mm);return NULL ; }
@@ -363,7 +368,7 @@ struct mm_struct * mm_alloc(void) mm =allocate_mm ();if (mm) {memset (mm,0 ,sizeof (*mm)); - mm =mm_init (mm); + mm =mm_init (mm, current); }return mm; }
@@ -377,6 +382,7 @@ void fastcall __mmdrop(struct mm_struct {BUG_ON (mm == &init_mm);mm_free_pgd (mm); +mm_free_container (mm);destroy_context (mm);free_mm (mm); }
@@ -497,7 +503,7 @@ static struct mm_struct *dup_mm(struct t mm->token_priority =0 ; mm->last_interval =0 ; -if (!mm_init (mm)) +if (!mm_init (mm, tsk)) goto fail_nomem;if (init_new_context (tsk, mm))
@@ -524,6 +530,7 @@ fail_nocontext:* because it calls destroy_context() */ mm_free_pgd (mm); +mm_free_container (mm);free_mm (mm);return NULL ; }
PATCH 4/8 Scanner changes needed to implement per-container scanner
In reply to Pavel EmelianovThe core change is that the isolate_lru_pages() call is replaced with struct scan_controll->isolate_pages() call.
Other changes include exporting __isolate_lru_page() for per-container isolator and handling variable-to-pointer changes in try_to_free_pages().
This makes possible to use different isolation routines for per-container page reclamation. This will be used by the following patch.
---
include/linux/swap.h +5 -0
@@ -192,6 +192,11 @@ extern void swap_setup(void);/* linux/mm/vmscan.c */ extern unsigned long try_to_free_pages (struct zone **zones,int order,gfp_t gfp_mask); + +struct rss_container ; +extern unsigned long try_to_free_pages_in_container (struct rss_container *); +int __isolate_lru_page (struct page *page,int mode); +extern unsigned long shrink_all_memory (unsigned long nr_pages);extern int vm_swappiness;extern int remove_mapping (struct address_space *mapping,struct page *page);
mm/vmscan.c +76 -28
200 lines not on this page · raw message
PATCH 5/8 RSS container core
In reply to Pavel EmelianovThe core routines for tracking the page ownership, RSS subsystem registration in the containers and the definition of the rss_container struct as container subsystem combined with the resource counter structure.
To make the whole set look more consistent the calls to the reclamation code and oom killer are removed from this patch, so if someone has the kernel snapshot stopped at this patch and calls the accounting routines and the limit will be hit, the current task will be killed immediately.
Includes fixes from Balbir Singh <[email protected]>
---
include/linux/container_subsys.h renamed from include/linux/container_subsys.h.rsscore +4 -0
@@ -11,6 +11,10 @@ SUBSYS(cpuacct)#endif +#ifdef CONFIG_RSS_CONTAINER +SUBSYS(rss) +#endif +/* */ #ifdef CONFIG_CPUSETS
include/linux/rss_container.h renamed from include/linux/rss_container.h.rsscore +88 -0
97 lines not on this page · raw message
init/Kconfig renamed from init/Kconfig.rsscore +11 -0
@@ -332,6 +332,17 @@ config RESOURCE_COUNTERS bool select CONTAINERS +config RSS_CONTAINER + bool "RSS accounting container" + select RESOURCE_COUNTERS + help + Provides a simple Resource Controller for monitoring and + controlling the total Resident Set Size of the tasks in a container + The reclaim logic is now container aware, when the container goes + overlimit the page reclaimer reclaims pages belonging to this + container. If we are unable to reclaim enough pages to satisfy the + request, the process is killed with an out of memory warning. + config SYSFS_DEPRECATED bool "Create deprecated sysfs files" default y
mm/Makefile renamed from mm/Makefile.rsscore +1 -0
@@ -30,4 +30,5 @@ obj-$(CONFIG_FS_XIP) += filemap_xip.o obj-$(CONFIG_MIGRATION) += migrate.o obj-$(CONFIG_SMP) += allocpercpu.o obj-$(CONFIG_QUICKLIST) += quicklist.o +obj-$(CONFIG_RSS_CONTAINER) += rss_container.o
mm/rss_container.c new file +323 -0
323 lines not on this page · raw message
PATCH 6/8 Per container OOM killer
In reply to Pavel EmelianovWhen container is completely out of memory some tasks should die. This is unfair to kill the current task, so a task with the largest RSS is chosen and killed. The code re-uses current OOM killer select_bad_process() for task selection.
---
include/linux/rss_container.h renamed from include/linux/rss_container.h.rssoom +2 -0
@@ -73,6 +73,8 @@ void container_rss_add(struct page_contavoid container_rss_del (struct page_container *);void container_rss_release (struct page_container *); +void container_out_of_memory (struct rss_container *); +void mm_init_container (struct mm_struct *mm,struct task_struct *tsk);void mm_free_container (struct mm_struct *mm);#else
mm/oom_kill.c renamed from mm/oom_kill.c.rssoom +38 -4
81 lines not on this page · raw message
PATCH 7/8 Per-container pages reclamation
In reply to Pavel EmelianovImplement try_to_free_pages_in_container() to free the pages in container that has run out of memory.
The scan_control->isolate_pages() function is set to isolate_pages_in_container() that isolates the container pages only. The exported __isolate_lru_page() call makes things look simpler than in the previous version.
Includes fix from Balbir Singh <[email protected]>
---
include/linux/rss_container.h renamed from include/linux/rss_container.h.rssreclaim +9 -0
@@ -77,6 +77,12 @@ void container_out_of_memory(struct rss_void mm_init_container (struct mm_struct *mm,struct task_struct *tsk);void mm_free_container (struct mm_struct *mm); + +void container_rss_move_lists (struct page *pg,bool active); +unsigned long isolate_pages_in_container (unsigned long nr_to_scan, +struct list_head *dst,unsigned long *scanned, +int order,int mode,struct zone *zone, +struct rss_container *,int active);#else static inline int container_rss_prepare (struct page *pg,struct vm_area_struct *vma,struct page_container **pc)
@@ -104,5 +110,8 @@ static inline void mm_init_container(strstatic inline void mm_free_container (struct mm_struct *mm) { } + +#define isolate_container_pages (nr, dst, scanned, rss, act, zone) ({ BUG(); 0;}) +#define container_rss_move_lists (pg, active) do { } while (0)#endif #endif
mm/rss_container.c renamed from mm/rss_container.c.rssreclaim +99 -2
113 lines not on this page · raw message
mm/swap.c renamed from mm/swap.c.rssreclaim +3 -0
@@ -31,6 +31,7 @@#include <linux/cpu.h> #include <linux/notifier.h> #include <linux/init.h> +#include <linux/rss_container.h> /* How many pages do we try to swap or page in/out together? */ int page_cluster;
@@ -128,6 +129,7 @@ int rotate_reclaimable_page(struct pageif (PageLRU (page) && !PageActive (page)) {list_move_tail (&page->lru, &zone->inactive_list);__count_vm_event (PGROTATED ); +container_rss_move_lists (page,0 ); }if (!test_clear_page_writeback (page))BUG ();
@@ -148,6 +150,7 @@ void fastcall activate_page(struct pageSetPageActive (page);add_page_to_active_list (zone, page);__count_vm_event (PGACTIVATE ); +container_rss_move_lists (page,1 ); }spin_unlock_irq (&zone->lru_lock); }
mm/vmscan.c renamed from mm/vmscan.c.rssreclaim +42 -2
70 lines not on this page · raw message
PATCH 8/8 RSS accounting hooks over the code
In reply to Pavel EmelianovAs described, pages are charged to their first touchers. The first toucher is determined using pages' _mapcount manipulations in rmap calls.
A page is charged in two stages:
1. preparation, in which the resource availability is checked. This stage may lead to page reclamation, thus it is performed in a "might-sleep" places;
2. the container assignment to page. This is done in an atomic code that handles races between multiple touchers.
The uncharge process is tricky. After the _mapcount is changed and happens to hit zero we cannot trust the page->container pointer any longer. So we get the pointer before the decrement.
Includes fixes from Balbir Singh <[email protected]>
---
fs/exec.c +12 -3
@@ -59,6 +59,8 @@#include <linux/kmod.h> #endif +#include <linux/rss_container.h> +int core_uses_pid;char core_pattern[CORENAME_MAX_SIZE ] ="core" ;int suid_dumpable =0 ;
@@ -314,27 +316,34 @@ void install_arg_page(struct vm_area_strstruct mm_struct *mm = vma->vm_mm;pte_t * pte;spinlock_t *ptl; +struct page_container *pcont;if (unlikely (anon_vma_prepare (vma))) goto out; +if (container_rss_prepare (page, vma, &pcont)) + goto out; +flush_dcache_page (page); pte =get_locked_pte (mm, address, &ptl);if (!pte) - goto out; + goto out_release;if (!pte_none (*pte)) {pte_unmap_unlock (pte, ptl); - goto out; + goto out_release; }inc_mm_counter (mm, anon_rss);lru_cache_add_active (page);set_pte_at (mm, address, pte,pte_mkdirty (pte_mkwrite (mk_pte ( page, vma->vm_page_prot)))); -page_add_new_anon_rmap (page, vma, address); +page_add_new_anon_rmap (page, vma, address, pcont);pte_unmap_unlock (pte, ptl);/* no need for flush_tlb */ return ; + +out_release: +container_rss_release (pcont); out:__free_page (page);force_sig (SIGKILL , current);
include/linux/rmap.h +7 -3
@@ -69,9 +69,13 @@ void __anon_vma_link(struct vm_area_stru/* * rmap interfaces called when adding or removing pte of page */ -void page_add_anon_rmap (struct page *,struct vm_area_struct *,unsigned long ); -void page_add_new_anon_rmap (struct page *,struct vm_area_struct *,unsigned long ); -void page_add_file_rmap (struct page *); +struct page_container ; + +void page_add_anon_rmap (struct page *,struct vm_area_struct *, +unsigned long ,struct page_container *); +void page_add_new_anon_rmap (struct page *,struct vm_area_struct *, +unsigned long ,struct page_container *); +void page_add_file_rmap (struct page *,struct page_container *);void page_remove_rmap (struct page *,struct vm_area_struct *);#ifdef CONFIG_DEBUG_VM
mm/memory.c +43 -10
183 lines not on this page · raw message
mm/migrate.c +12 -2
@@ -28,6 +28,7 @@#include <linux/mempolicy.h> #include <linux/vmalloc.h> #include <linux/security.h> +#include <linux/rss_container.h> #include "internal.h"
@@ -134,6 +135,7 @@ static void remove_migration_pte(structpte_t *ptep, pte;spinlock_t *ptl;unsigned long addr =page_address_in_vma (new, vma); +struct page_container *pcont;if (addr == -EFAULT )return ;
@@ -157,6 +159,11 @@ static void remove_migration_pte(structreturn ; } +if (container_rss_prepare (new, vma, &pcont)) { +pte_unmap (ptep); +return ; + } + ptl =pte_lockptr (mm, pmd);spin_lock (ptl); pte = *ptep;
@@ -175,16 +182,19 @@ static void remove_migration_pte(structset_pte_at (mm, addr, ptep, pte);if (PageAnon (new)) -page_add_anon_rmap (new, vma, addr); +page_add_anon_rmap (new, vma, addr, pcont);else -page_add_file_rmap (new); +page_add_file_rmap (new, pcont);/* No need to invalidate - it was non-present before */ update_mmu_cache (vma, addr, pte);lazy_mmu_prot_update (pte); +pte_unmap_unlock (ptep, ptl); +return ; out:pte_unmap_unlock (ptep, ptl); +container_rss_release (pcont); }/*
mm/page_alloc.c +3 -0
@@ -41,6 +41,7 @@#include <linux/pfn.h> #include <linux/backing-dev.h> #include <linux/fault-inject.h> +#include <linux/rss_container.h> #include <asm/tlbflush.h> #include <asm/div64.h>
@@ -1042,6 +1043,7 @@ static void fastcall free_hot_cold_page(if (!PageHighMem (page))debug_check_no_locks_freed (page_address (page),PAGE_SIZE ); +set_page_container (page,NULL );arch_free_page (page,0 );kernel_map_pages (page,1 ,0 );
@@ -2592,6 +2594,7 @@ void __meminit memmap_init_zone(unsignedset_page_links (page, zone, nid, pfn);init_page_count (page);reset_page_mapcount (page); +set_page_container (page,NULL );SetPageReserved (page);/*
mm/rmap.c +45 -7
110 lines not on this page · raw message
mm/swapfile.c +12 -3
@@ -32,6 +32,8 @@#include <asm/tlbflush.h> #include <linux/swapops.h> +#include <linux/rss_container.h> +DEFINE_SPINLOCK (swap_lock);unsigned int nr_swapfiles;long total_swap_pages;
@@ -507,13 +509,14 @@ unsigned int count_swap_pages(int type,* force COW, vm_page_prot omits write permission from any private vma. */ static void unuse_pte (struct vm_area_struct *vma,pte_t *pte, -unsigned long addr,swp_entry_t entry,struct page *page) +unsigned long addr,swp_entry_t entry,struct page *page, +struct page_container *pcont) {inc_mm_counter (vma->vm_mm, anon_rss);get_page (page);set_pte_at (vma->vm_mm, addr, pte,pte_mkold (mk_pte (page, vma->vm_page_prot))); -page_add_anon_rmap (page, vma, addr); +page_add_anon_rmap (page, vma, addr, pcont);swap_free (entry);/* * Move the page to the active list so it is not
@@ -530,6 +533,10 @@ static int unuse_pte_range(struct vm_arepte_t *pte;spinlock_t *ptl;int found =0 ; +struct page_container *pcont; + +if (container_rss_prepare (page, vma, &pcont)) +return 0 ; pte =pte_offset_map_lock (vma->vm_mm, pmd, addr, &ptl); do {
@@ -538,12 +545,14 @@ static int unuse_pte_range(struct vm_are* Test inline before going to call unuse_pte. */ if (unlikely (pte_same (*pte, swp_pte))) { -unuse_pte (vma, pte++, addr, entry, page); +unuse_pte (vma, pte++, addr, entry, page, pcont); found =1 ;break ; } }while (pte++, addr +=PAGE_SIZE , addr != end);pte_unmap_unlock (pte -1 , ptl); +if (!found) +container_rss_release (pcont);return found; }
Quoted 17 lines · Pavel Emelianov
Adds RSS accounting and control within a container.
Changes from v3
- comments across the code
- git-bisect safe split
- lost places to move the page between active/inactive listsPorted above Paul's containers V10 with fixes from Balbir.
RSS container includes the per-container RSS accounting and reclamation, and out-of-memory killer.
Each mapped page has an owning container and is linked into its LRU lists just like in the global LRU ones. The owner of the page is the container that touched the page first.
Quoted 6 lines
As long as the page stays mapped it holds the container, is accounted into its usage and lives in its LRU list. When page is unmapped for the last time it releases the container.
The RSS usage is exactly the number of pages in its booth LRU lists, i.e. the nu,ber of pages used by this container.
so there could be two guests, unified (i.e. sharing most of the files as hardlinks), where the first one holds 80% of the resulting pages, and the second one 20%, and thus shows much lower 'RSS' usage as the other one, although it is running the very same processes and providing identical services?
Quoted 3 lines
When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
so the system (physical machine) starts reclaiming and probably swapping even when there is no need to do so?
e.g. a system with a single guest, limited to 10k pages, with a working set of 15k pages in different apps would continuously swap (trash?) on an otherwise unused (100k+ pages) system?
Quoted 3 lines
Thus the container behaves like a standalone machine - when it runs out of resources, it tries to reclaim some pages, and if it doesn't succeed, kills some task.
is that really what we want?
I think we can do _better_ than a standalone machine and in many cases we really should ...
best,
Herbert
Quoted 66 lines
Signed-off-by Pavel Emelianov <[email protected]>The testing scenario may look like this:
1. Prepare the containers
# mkdir -p /containers/rss # mount -t container none /containers/rss -o rss2. Make the new group and move bash into it
# mkdir /containers/rss/0 # echo $$ > /containers/rss/0/tasksSince now we're in the 0 container.
We can alter the RSS limit# echo -n 6000 > /containers/rss/0/rss_limitWe can check the usage
# cat /containers/rss/0/rss_usage 25And do other stuff. To check the reclamation to work we need a simple program that touches many pages of memory, like this:
#include <
stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>#ifndef PGSIZE
#define PGSIZE 4096
#endifint
main(int argc, char **argv){ unsigned long pages; int i; char *mem; if (argc < 2) { printf("Usage: %s <number_of_pages>\n", argv[0]); return 1; } pages = strtol(argv[1], &mem, 10); if (*mem != '\0') { printf("Bad number %s\n", argv[1]); return 1; } mem = mmap(NULL, pages * PGSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); if (mem == MAP_FAILED) { perror("map"); return 2; } for (i = 0; i < pages; i++) mem[i * PGSIZE] = 0; printf("OK\n"); return 0; }_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers
Quoted 25 lines · Herbert Poetzl
Quoted 17 lines · Pavel Emelianov
Adds RSS accounting and control within a container.
Changes from v3
- comments across the code
- git-bisect safe split
- lost places to move the page between active/inactive listsPorted above Paul's containers V10 with fixes from Balbir.
RSS container includes the per-container RSS accounting and reclamation, and out-of-memory killer.
Each mapped page has an owning container and is linked into its LRU lists just like in the global LRU ones. The owner of the page is the container that touched the page first.
Quoted 6 lines
As long as the page stays mapped it holds the container, is accounted into its usage and lives in its LRU list. When page is unmapped for the last time it releases the container.
The RSS usage is exactly the number of pages in its booth LRU lists, i.e. the nu,ber of pages used by this container.
Quoted 6 lines
so there could be two guests, unified (i.e. sharing most of the files as hardlinks), where the first one holds 80% of the resulting pages, and the second one 20%, and thus shows much lower 'RSS' usage as the other one, although it is running the very same processes and providing identical services?
Herbert!!! Where have you been so long?
You must have missed that we've decided not to account pages sharing right now, but start with that model. Later we'll make sharing accountable.
( There's something bad with my memory. I have a feeling that I
have already told that... many times... )
Quoted 7 lines
Quoted 3 lines
When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
so the system (physical machine) starts reclaiming and probably swapping even when there is no need to do so?
Good catch! The system will start reclaiming right when the container hits the limit to expend its IO bandwidth. Not some other's one that hit the global limit due to some bad container was allowed to go above it.
Quoted 10 lines
e.g. a system with a single guest, limited to 10k pages, with a working set of 15k pages in different apps would continuously swap (trash?) on an otherwise unused (100k+ pages) system?
Quoted 3 lines
Thus the container behaves like a standalone machine - when it runs out of resources, it tries to reclaim some pages, and if it doesn't succeed, kills some task.
is that really what we want?
A kind of ;)
Quoted 2 lines
I think we can do _better_ than a standalone machine and in many cases we really should ...
That's it! You are right - this is our ultimate goal. And we plan to get there step by step. And we will appreciate your patches fixing BUGS, improving the performance, extending the functionality, etc.
Quoted 2 lines
best,
Herbert
Thanks for your attention,
Pavel
Quoted 67 lines
Quoted 66 lines
Signed-off-by Pavel Emelianov <[email protected]>The testing scenario may look like this:
1. Prepare the containers
# mkdir -p /containers/rss # mount -t container none /containers/rss -o rss2. Make the new group and move bash into it
# mkdir /containers/rss/0 # echo $$ > /containers/rss/0/tasksSince now we're in the 0 container.
We can alter the RSS limit# echo -n 6000 > /containers/rss/0/rss_limitWe can check the usage
# cat /containers/rss/0/rss_usage 25And do other stuff. To check the reclamation to work we need a simple program that touches many pages of memory, like this:
#include <
stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>#ifndef PGSIZE
#define PGSIZE 4096
#endifint
main(int argc, char **argv){ unsigned long pages; int i; char *mem; if (argc < 2) { printf("Usage: %s <number_of_pages>\n", argv[0]); return 1; } pages = strtol(argv[1], &mem, 10); if (*mem != '\0') { printf("Bad number %s\n", argv[1]); return 1; } mem = mmap(NULL, pages * PGSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); if (mem == MAP_FAILED) { perror("map"); return 2; } for (i = 0; i < pages; i++) mem[i * PGSIZE] = 0; printf("OK\n"); return 0; }_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers
Quoted 26 lines · Pavel Emelianov
Quoted 25 lines · Herbert Poetzl
Quoted 23 lines · Pavel Emelianov
Adds RSS accounting and control within a container.
Changes from v3
- comments across the code
- git-bisect safe split
- lost places to move the page between active/inactive listsPorted above Paul's containers V10 with fixes from Balbir.
RSS container includes the per-container RSS accounting and reclamation, and out-of-memory killer.
Each mapped page has an owning container and is linked into its LRU lists just like in the global LRU ones. The owner of the page is the container that touched the page first.
As long as the page stays mapped it holds the container, is accounted into its usage and lives in its LRU list. When page is unmapped for the last time it releases the container.
The RSS usage is exactly the number of pages in its booth LRU lists, i.e. the nu,ber of pages used by this container.
Quoted 8 lines
Quoted 6 lines
so there could be two guests, unified (i.e. sharing most of the files as hardlinks), where the first one holds 80% of the resulting pages, and the second one 20%, and thus shows much lower 'RSS' usage as the other one, although it is running the very same processes and providing identical services?
Herbert!!! Where have you been so long?
I was on vacation in april, and it took almost the entire may to process the backlog ...
Quoted 3 lines
You must have missed that we've decided not to account pages sharing right now, but start with that model. Later we'll make sharing accountable.
well, there are two ways not to account sharing:
1) account it to the first user
2) account it to every user
while the first one typically causes quite an imbalance when applied to shared resources (as Linux-VServer uses them), the latter one creates a new, but fair, metric for the usage
to make that clear: we definitely prefer the latter one over the former, because we do not want to bring that imbalance to our shared guests (besides that, the second one doesn't add any overhead compared to the first one, more than that it probably simplifies the entire design)
Quoted 2 lines
( There's something bad with my memory. I have a feeling that I
have already told that... many times... )
yeah, I have some kind of déjà vu here too ...
Quoted 4 lines
Quoted 4 lines
Quoted 3 lines
When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
Quoted 8 lines
Quoted 3 lines
so the system (physical machine) starts reclaiming and probably swapping even when there is no need to do so?
Good catch! The system will start reclaiming right when the container hits the limit to expend its IO bandwidth. Not some other's one that hit the global limit due to some bad container was allowed to go above it.
well, from the system PoV, a constantly swapping guest (on an otherwise unused host) is definitely something you do not really want, not to talk about a tightly packed host system, where guests start hogging the I/O with _unnecessary_ swapping
Quoted 5 lines
Quoted 5 lines
e.g. a system with a single guest, limited to 10k pages, with a working set of 15k pages in different apps would continuously swap (trash?) on an otherwise unused (100k+ pages) system?
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
Thus the container behaves like a standalone machine - when it runs out of resources, it tries to reclaim some pages, and if it doesn't succeed, kills some task.
is that really what we want?
A kind of ;)
okay, to clarify, we (Linux-VServer) do not want that behavior ...
Quoted 7 lines
Quoted 2 lines
I think we can do _better_ than a standalone machine and in many cases we really should ...
That's it! You are right - this is our ultimate goal. And we plan to get there step by step. And we will appreciate your patches fixing BUGS, improving the performance, extending the functionality, etc.
well, I would rip out the entire accounting and add a summation accounting as we do it right now, no problem with keeping the reclaim mechanisms though ... but I doubt that this is what you have in mind?
Quoted 5 lines
Quoted 2 lines
best,
HerbertThanks for your attention,
Pavel
just to make it clear, I don't want any limits in mainline which penalize the first started guest in a shared guest scenario .. or to rephrase, such a limit would be useless for our purpose
best,
Herbert
Quoted 67 lines
Quoted 67 lines
Quoted 66 lines
Signed-off-by Pavel Emelianov <[email protected]>The testing scenario may look like this:
1. Prepare the containers
# mkdir -p /containers/rss # mount -t container none /containers/rss -o rss2. Make the new group and move bash into it
# mkdir /containers/rss/0 # echo $$ > /containers/rss/0/tasksSince now we're in the 0 container.
We can alter the RSS limit# echo -n 6000 > /containers/rss/0/rss_limitWe can check the usage
# cat /containers/rss/0/rss_usage 25And do other stuff. To check the reclamation to work we need a simple program that touches many pages of memory, like this:
#include <
stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>#ifndef PGSIZE
#define PGSIZE 4096
#endifint
main(int argc, char **argv){ unsigned long pages; int i; char *mem; if (argc < 2) { printf("Usage: %s <number_of_pages>\n", argv[0]); return 1; } pages = strtol(argv[1], &mem, 10); if (*mem != '\0') { printf("Bad number %s\n", argv[1]); return 1; } mem = mmap(NULL, pages * PGSIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, 0, 0); if (mem == MAP_FAILED) { perror("map"); return 2; } for (i = 0; i < pages; i++) mem[i * PGSIZE] = 0; printf("OK\n"); return 0; }_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers
Quoted 24 lines · Herbert Poetzl
Quoted 23 lines · Pavel Emelianov
Quoted 22 lines · Herbert Poetzl
Quoted 21 lines · Pavel Emelianov
Adds RSS accounting and control within a container.
Changes from v3
- comments across the code
- git-bisect safe split
- lost places to move the page between active/inactive listsPorted above Paul's containers V10 with fixes from Balbir.
RSS container includes the per-container RSS accounting and reclamation, and out-of-memory killer.
Each mapped page has an owning container and is linked into its LRU lists just like in the global LRU ones. The owner of the page is the container that touched the page first.
As long as the page stays mapped it holds the container, is accounted into its usage and lives in its LRU list. When page is unmapped for the last time it releases the container.
The RSS usage is exactly the number of pages in its booth LRU lists, i.e. the nu,ber of pages used by this container.
Quoted 6 lines
Quoted 6 lines
Quoted 6 lines
so there could be two guests, unified (i.e. sharing most of the files as hardlinks), where the first one holds 80% of the resulting pages, and the second one 20%, and thus shows much lower 'RSS' usage as the other one, although it is running the very same processes and providing identical services?
Hi, Herbert,
For page reclaim one page can belong to only container LRU and only each physical page is accounted for. Consider what happens when we equally charge each container
1. Lets say you have containers A and B, both showing that they are charged 45% of memory usage
2. The sum of these charges is equal to 90%, but the real memory used is just 70%, since 20% of the charges are shared.
The system administrator will find this confusing while assigning resources
Quoted 19 lines
Quoted 1 line
Herbert!!! Where have you been so long?
I was on vacation in april, and it took almost the entire may to process the backlog ...
Quoted 3 lines
You must have missed that we've decided not to account pages sharing right now, but start with that model. Later we'll make sharing accountable.
well, there are two ways not to account sharing:
1) account it to the first user
2) account it to every userwhile the first one typically causes quite an imbalance when applied to shared resources (as Linux-VServer uses them), the latter one creates a new, but fair, metric for the usage
Quoted 7 lines
to make that clear: we definitely prefer the latter one over the former, because we do not want to bring that imbalance to our shared guests (besides that, the second one doesn't add any overhead compared to the first one, more than that it probably simplifies the entire design)
Please see comments by Nick at
http://lkml.org/lkml/2007/3/14/40
The imbalance is only temporary in the long run, the container that uses a page more aggressively will get charged for it. If the container that first brought the pages in is actively using it, then it's only fair to charge it.
Quoted 16 lines
Quoted 2 lines
( There's something bad with my memory. I have a feeling that I
have already told that... many times... )yeah, I have some kind of déjà vu here too ...
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines
When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
so the system (physical machine) starts reclaiming and probably swapping even when there is no need to do so?
Good catch! The system will start reclaiming right when the container hits the limit to expend its IO bandwidth. Not some other's one that hit the global limit due to some bad container was allowed to go above it.
Quoted 6 lines
well, from the system PoV, a constantly swapping guest (on an otherwise unused host) is definitely something you do not really want, not to talk about a tightly packed host system, where guests start hogging the I/O with _unnecessary_ swapping
Why do you call the swapping unnecessary? We defined a limit for the containers, so that other containers are not impacted by the memory usage of our container. We go overboard, it's either
1. We get penalized
2. We are not well configured, reconfigure
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
e.g. a system with a single guest, limited to 10k pages, with a working set of 15k pages in different apps would continuously swap (trash?) on an otherwise unused (100k+ pages) system?
What to do with the free memory is an open question, we've even discussed implementing soft limits (may be if required sometime in the future).
Quoted 9 lines
Quoted 5 lines
Quoted 4 lines
Quoted 3 lines
Thus the container behaves like a standalone machine - when it runs out of resources, it tries to reclaim some pages, and if it doesn't succeed, kills some task.
is that really what we want?
A kind of ;)
okay, to clarify, we (Linux-VServer) do not want that behavior ...
Hmmm... could you define the behaviour you expect?
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines
I think we can do _better_ than a standalone machine and in many cases we really should ...
That's it! You are right - this is our ultimate goal. And we plan to get there step by step. And we will appreciate your patches fixing BUGS, improving the performance, extending the functionality, etc.
Quoted 6 lines
well, I would rip out the entire accounting and add a summation accounting as we do it right now, no problem with keeping the reclaim mechanisms though ... but I doubt that this is what you have in mind?
Patches are always welcome!
Quoted 10 lines
Quoted 4 lines
Quoted 2 lines
best,
HerbertThanks for your attention,
Paveljust to make it clear, I don't want any limits in mainline which penalize the first started guest in a shared guest scenario .. or to rephrase, such a limit would be useless for our purpose
In summary, I would like to emphasize on the following points
1. Let's not build an ideal system, lets build something use-able, review-able and understand-able
2. Let's add more features when we hit a bottleneck/problem, code development process is always iterative
3. Please try our patches, test them, provide numbers, feedback, new patches to improve upon the existing code (in a reasonable amount of time, to keep the development on track), you never know you might end up liking what you use :-)
Quoted 3 lines
best,
Herbert
Signature · 4 lines
Warm Regards, Balbir Singh Linux Technology Center IBM, ISTL
Quoted 3 lines · Herbert Poetzl
Quoted 2 lines · Pavel Emelianov
Quoted 1 line · Herbert Poetzl
On Mon, Jun 04, 2007 at 05:25:25PM +0400, Pavel Emelianov wrote:
[snip]
Quoted 11 lines
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines
When this usage exceeds the limit set some pages are reclaimed from the owning container. In case no reclamation possible the OOM killer starts thinning out the container.
so the system (physical machine) starts reclaiming and probably swapping even when there is no need to do so?
Good catch! The system will start reclaiming right when the container hits the limit to expend its IO bandwidth. Not some other's one that hit the global limit due to some bad container was allowed to go above it.
Quoted 11 lines
well, from the system PoV, a constantly swapping guest (on an otherwise unused host) is definitely something you do not really want, not to talk about a tightly packed host system, where guests start hogging the I/O with _unnecessary_ swapping
Quoted 5 lines
Quoted 5 lines
e.g. a system with a single guest, limited to 10k pages, with a working set of 15k pages in different apps would continuously swap (trash?) on an otherwise unused (100k+ pages) system?
Hi Herbert,
When the reclaim process started, swappable pages are unmapped and moved to swapcache. The RSS accounting treats the page as dropped, but however the page will remain in memory until there is enough global pressure to push it out to disk. When a page is faulted-in, the page is just remapped from the swapcache.
In effect container 'trashing' is not as bad in an otherwise unused system. Well, certainly we pay a penalty to move around the pages instead of keeping them mapped in RSS as long as free memory was available.
The pagecache controller is suppose to track the pagecache and swapcache pages and push out pages to disk. As Balbir and Pavel have mentioned, we will be building the features in stages.
--Vaidy
[snip]