Skip to contentSkip to conversation

RSS controller based on process containers (v3.1)

PATCH In review Pavel Emelianov 4 participants 14 messages Jun 4, 2007 – Jun 8, 2007

Subsystems (12)
lore
Share & tools
lore
#0 lore raw

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;
}
#1 lore raw

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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/res_counter.h linux-2.6.22-rc2-mm1-2/include/linux/res_counter.h
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/init/Kconfig linux-2.6.22-rc2-mm1-2/init/Kconfig
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/kernel/Makefile linux-2.6.22-rc2-mm1-2/kernel/Makefile
@@ -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

#2 lore raw

PATCH 2/8 Add container pointer on struct page

In reply to Pavel Emelianov

Each 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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/mm.h linux-2.6.22-rc2-mm1-2/include/linux/mm.h
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/mm_types.h linux-2.6.22-rc2-mm1-2/include/linux/mm_types.h
@@ -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 */
#3 lore raw

PATCH 3/8 Add container pointer on mm_struct

In reply to Pavel Emelianov

Naturally 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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/sched.h linux-2.6.22-rc2-mm1-2/include/linux/sched.h
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/rss_container.h linux-2.6.22-rc2-mm1-2/include/linux/rss_container.h
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/kernel/fork.c linux-2.6.22-rc2-mm1-2/kernel/fork.c
@@ -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;
 }
#4 lore raw

PATCH 4/8 Scanner changes needed to implement per-container scanner

In reply to Pavel Emelianov

The 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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/swap.h linux-2.6.22-rc2-mm1-2/include/linux/swap.h
@@ -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

#5 lore raw

PATCH 5/8 RSS container core

In reply to Pavel Emelianov

The 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

#6 lore raw

PATCH 6/8 Per container OOM killer

In reply to Pavel Emelianov

When 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_conta
 void 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

#7 lore raw

PATCH 7/8 Per-container pages reclamation

In reply to Pavel Emelianov

Implement 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(str
 static 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 page 
 	if (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 page 
 		SetPageActive(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

#8 lore raw

PATCH 8/8 RSS accounting hooks over the code

In reply to Pavel Emelianov

As 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
diff -upr linux-2.6.22-rc2-mm1.orig/fs/exec.c linux-2.6.22-rc2-mm1-2/fs/exec.c
@@ -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_str
 	struct 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
diff -upr linux-2.6.22-rc2-mm1.orig/include/linux/rmap.h linux-2.6.22-rc2-mm1-2/include/linux/rmap.h
@@ -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
diff -upr linux-2.6.22-rc2-mm1.orig/mm/migrate.c linux-2.6.22-rc2-mm1-2/mm/migrate.c
@@ -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(struct 
 	pte_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(struct 
  		return;
  	}
 
+	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(struct 
 	set_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
diff -upr linux-2.6.22-rc2-mm1.orig/mm/page_alloc.c linux-2.6.22-rc2-mm1-2/mm/page_alloc.c
@@ -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(unsigned
 		set_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
diff -upr linux-2.6.22-rc2-mm1.orig/mm/swapfile.c linux-2.6.22-rc2-mm1-2/mm/swapfile.c
@@ -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_are
 	pte_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;
 }
 
#9 lore raw
In reply to Pavel Emelianov
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 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.

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

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;
}

_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers

#10 lore raw
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 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.

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

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;
}

_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers

#11 lore raw
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 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.

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,
Herbert

Thanks 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

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;
}

_______________________________________________
Containers mailing list
[email protected]
https://lists.linux-foundation.org/mailman/listinfo/containers

#12 lore raw
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 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.

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 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

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,
Herbert

Thanks 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

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
#13 lore raw
In reply to Herbert Poetzl
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]