Skip to contentSkip to conversation

Add soft limits to the memory controller v2

PATCH In review Balbir Singh 2 participants 9 messages Feb 19, 2008

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

cover Add soft limits to the memory controller v2

This patchset implements the basic changes required to implement soft limits in the memory controller. A soft limit is a variation of the currently supported hard limit feature. A memory cgroup can exceed it's soft limit provided there is no contention for memory.

These patches were tested on a PowerPC box, by running a programs in parallel, and checking their behaviour for various soft limit values.

These patches were developed on top of 2.6.25-rc2-mm1. Comments, suggestions, criticism are all welcome!

TODOs:

1. Currently there is no ordering of memory cgroups over their limit. We use a simple linked list to maintain a list of groups over their limit. In the future, we might want to create a heap of objects ordered by the amount by which they exceed soft limit.
2. Distribute the excessive (non-contended) resources between groups in the ratio of their soft limits
3. Merge with KAMEZAWA's and YAMAMOTO's water mark and background reclaim patches in the long-term

series
------
memory-controller-res_counters-soft-limit-setup.patch memory-controller-add-soft-limit-interface.patch memory-controller-reclaim-on-contention.patch memory-controller-add-soft-limit-documentation.patch

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#1 lore raw

PATCH 1/4 Modify resource counters to add soft limit support v2

Changelog v2
1. Remove memory controller specific comments from resource counters

The resource counter member limit is split into soft and hard limits. The same locking rule apply for both limits.

---

 include/linux/res_counter.h |   32 ++++++++++++++++++++++++--------
 kernel/res_counter.c        |   11 +++++++----
 mm/memcontrol.c             |   10 +++++-----
 3 files changed, 36 insertions(+), 17 deletions(-)
include/linux/res_counter.h renamed from include/linux/res_counter.h~memory-controller-res_counters-soft-limit-setup +24 -8
diff -puN include/linux/res_counter.h~memory-controller-res_counters-soft-limit-setup include/linux/res_counter.h
@@ -27,7 +27,11 @@ struct res_counter {
 	/*
 	 * the limit that usage cannot exceed
 	 */
-	unsigned long long limit;
+	unsigned long long hard_limit;
+	/*
+	 * the limit that usage can exceed
+	 */
+	unsigned long long soft_limit;
 	/*
 	 * the number of unsuccessful attempts to consume the resource
 	 */
@@ -64,7 +68,8 @@ ssize_t res_counter_write(struct res_cou
 
 enum {
 	RES_USAGE,
-	RES_LIMIT,
+	RES_SOFT_LIMIT,
+	RES_HARD_LIMIT,
 	RES_FAILCNT,
 };
 
@@ -101,11 +106,21 @@ int res_counter_charge(struct res_counte
 void res_counter_uncharge_locked(struct res_counter *counter, unsigned long val);
 void res_counter_uncharge(struct res_counter *counter, unsigned long val);
 
-static inline bool res_counter_limit_check_locked(struct res_counter *cnt)
+static inline bool res_counter_limit_check_locked(struct res_counter *cnt,
+							int member)
 {
-	if (cnt->usage < cnt->limit)
-		return true;
-
+	switch (member) {
+	case RES_HARD_LIMIT:
+		if (cnt->usage < cnt->hard_limit)
+			return true;
+		break;
+	case RES_SOFT_LIMIT:
+		if (cnt->usage < cnt->soft_limit)
+			return true;
+		break;
+	default:
+		BUG_ON(1);
+	}
 	return false;
 }
 
@@ -113,13 +128,14 @@ static inline bool res_counter_limit_che
  * Helper function to detect if the cgroup is within it's limit or
  * not. It's currently called from cgroup_rss_prepare()
  */
-static inline bool res_counter_check_under_limit(struct res_counter *cnt)
+static inline bool res_counter_check_under_limit(struct res_counter *cnt,
+							int member)
 {
 	bool ret;
 	unsigned long flags;
 
 	spin_lock_irqsave(&cnt->lock, flags);
-	ret = res_counter_limit_check_locked(cnt);
+	ret = res_counter_limit_check_locked(cnt, member);
 	spin_unlock_irqrestore(&cnt->lock, flags);
 	return ret;
 }
kernel/res_counter.c renamed from kernel/res_counter.c~memory-controller-res_counters-soft-limit-setup +7 -4
diff -puN kernel/res_counter.c~memory-controller-res_counters-soft-limit-setup kernel/res_counter.c
@@ -16,12 +16,13 @@
 void res_counter_init(struct res_counter *counter)
 {
 	spin_lock_init(&counter->lock);
-	counter->limit = (unsigned long long)LLONG_MAX;
+	counter->soft_limit = (unsigned long long)LLONG_MAX;
+	counter->hard_limit = (unsigned long long)LLONG_MAX;
 }
 
 int res_counter_charge_locked(struct res_counter *counter, unsigned long val)
 {
-	if (counter->usage + val > counter->limit) {
+	if (counter->usage + val > counter->hard_limit) {
 		counter->failcnt++;
 		return -ENOMEM;
 	}
@@ -65,8 +66,10 @@ res_counter_member(struct res_counter *c
 	switch (member) {
 	case RES_USAGE:
 		return &counter->usage;
-	case RES_LIMIT:
-		return &counter->limit;
+	case RES_SOFT_LIMIT:
+		return &counter->soft_limit;
+	case RES_HARD_LIMIT:
+		return &counter->hard_limit;
 	case RES_FAILCNT:
 		return &counter->failcnt;
 	};
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-res_counters-soft-limit-setup +5 -5
diff -puN mm/memcontrol.c~memory-controller-res_counters-soft-limit-setup mm/memcontrol.c
@@ -568,7 +568,7 @@ unsigned long mem_cgroup_isolate_pages(u
  * Charge the memory controller for page usage.
  * Return
  * 0 if the charge was successful
- * < 0 if the cgroup is over its limit
+ * < 0 if the cgroup is over its hard limit
  */
 static int mem_cgroup_charge_common(struct page *page, struct mm_struct *mm,
 				gfp_t gfp_mask, enum charge_type ctype)
@@ -632,7 +632,7 @@ retry:
 
 	/*
 	 * If we created the page_cgroup, we should free it on exceeding
-	 * the cgroup limit.
+	 * the cgroup hard limit.
 	 */
 	while (res_counter_charge(&mem->res, PAGE_SIZE)) {
 		if (!(gfp_mask & __GFP_WAIT))
@@ -645,10 +645,10 @@ retry:
  		 * try_to_free_mem_cgroup_pages() might not give us a full
  		 * picture of reclaim. Some pages are reclaimed and might be
  		 * moved to swap cache or just unmapped from the cgroup.
- 		 * Check the limit again to see if the reclaim reduced the
+ 		 * Check the hard limit again to see if the reclaim reduced the
  		 * current usage of the cgroup before giving up
  		 */
-		if (res_counter_check_under_limit(&mem->res))
+		if (res_counter_check_under_limit(&mem->res, RES_HARD_LIMIT))
 			continue;
 
 		if (!nr_retries--) {
@@ -1028,7 +1028,7 @@ static struct cftype mem_cgroup_files[] 
 	},
 	{
 		.name = "limit_in_bytes",
-		.private = RES_LIMIT,
+		.private = RES_HARD_LIMIT,
 		.write = mem_cgroup_write,
 		.read = mem_cgroup_read,
 	},

_

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#2 lore raw

PATCH 2/4 Add the soft limit interface v2

In reply to Balbir Singh

A new configuration file called soft_limit_in_bytes is added. The parsing and configuration rules remain the same as for the limit_in_bytes user interface.

A global list of all memory cgroups over their soft limit is maintained. This list is then used to reclaim memory on global pressure. A cgroup is removed from the list when the cgroup is deleted.

The global list is protected with a read-write spinlock.

---

 mm/memcontrol.c |   33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +32 -1
diff -puN mm/memcontrol.c~memory-controller-add-soft-limit-interface mm/memcontrol.c
@@ -35,6 +35,10 @@
 
 struct cgroup_subsys mem_cgroup_subsys;
 static const int MEM_CGROUP_RECLAIM_RETRIES = 5;
+static spinlock_t mem_cgroup_sl_list_lock;	/* spin lock that protects */
+						/* the list of cgroups over*/
+						/* their soft limit */
+static struct list_head mem_cgroup_sl_exceeded_list;
 
 /*
  * Statistics for memory cgroup.
@@ -136,6 +140,10 @@ struct mem_cgroup {
 	 * statistics.
 	 */
 	struct mem_cgroup_stat stat;
+	/*
+	 * List of all mem_cgroup's that exceed their soft limit
+	 */
+	struct list_head sl_exceeded_list;
 };
 
 /*
@@ -679,6 +687,18 @@ retry:
 		goto retry;
 	}
 
+	/*
+	 * If we exceed our soft limit, we get added to the list of
+	 * cgroups over their soft limit
+	 */
+	if (!res_counter_check_under_limit(&mem->res, RES_SOFT_LIMIT)) {
+		spin_lock_irqsave(&mem_cgroup_sl_list_lock, flags);
+		if (list_empty(&mem->sl_exceeded_list))
+			list_add_tail(&mem->sl_exceeded_list,
+						&mem_cgroup_sl_exceeded_list);
+		spin_unlock_irqrestore(&mem_cgroup_sl_list_lock, flags);
+	}
+
 	mz = page_cgroup_zoneinfo(pc);
 	spin_lock_irqsave(&mz->lru_lock, flags);
 	/* Update statistics vector */
@@ -736,13 +756,14 @@ void mem_cgroup_uncharge(struct page_cgr
 	if (atomic_dec_and_test(&pc->ref_cnt)) {
 		page = pc->page;
 		mz = page_cgroup_zoneinfo(pc);
+		mem = pc->mem_cgroup;
 		/*
 		 * get page->cgroup and clear it under lock.
 		 * force_empty can drop page->cgroup without checking refcnt.
 		 */
 		unlock_page_cgroup(page);
+
 		if (clear_page_cgroup(page, pc) == pc) {
-			mem = pc->mem_cgroup;
 			css_put(&mem->css);
 			res_counter_uncharge(&mem->res, PAGE_SIZE);
 			spin_lock_irqsave(&mz->lru_lock, flags);
@@ -1046,6 +1067,12 @@ static struct cftype mem_cgroup_files[] 
 		.name = "stat",
 		.open = mem_control_stat_open,
 	},
+	{
+		.name = "soft_limit_in_bytes",
+		.private = RES_SOFT_LIMIT,
+		.write = mem_cgroup_write,
+		.read = mem_cgroup_read,
+	},
 };
 
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
@@ -1097,6 +1124,9 @@ mem_cgroup_create(struct cgroup_subsys *
 	if (unlikely((cont->parent) == NULL)) {
 		mem = &init_mem_cgroup;
 		init_mm.mem_cgroup = mem;
+		INIT_LIST_HEAD(&mem->sl_exceeded_list);
+		spin_lock_init(&mem_cgroup_sl_list_lock);
+		INIT_LIST_HEAD(&mem_cgroup_sl_exceeded_list);
 	} else
 		mem = kzalloc(sizeof(struct mem_cgroup), GFP_KERNEL);
 
@@ -1104,6 +1134,7 @@ mem_cgroup_create(struct cgroup_subsys *
 		return NULL;
 
 	res_counter_init(&mem->res);
+	INIT_LIST_HEAD(&mem->sl_exceeded_list);
 
 	memset(&mem->info, 0, sizeof(mem->info));
 

_

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#3 lore raw

Re: [mm] [PATCH 2/4] Add the soft limit interface v2

Quoted 10 lines · Balbir Singh

A new configuration file called soft_limit_in_bytes is added. The parsing and configuration rules remain the same as for the limit_in_bytes user interface.

A global list of all memory cgroups over their soft limit is maintained. This list is then used to reclaim memory on global pressure. A cgroup is removed from the list when the cgroup is deleted.

The global list is protected with a read-write spinlock.

You are not using read-write spinlock..

Quoted 91 lines

---

 mm/memcontrol.c |   33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff -puN mm/memcontrol.c~memory-controller-add-soft-limit-interface mm/memcontrol.c

mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +31 -1
@@ -35,6 +35,10 @@
 
 struct cgroup_subsys mem_cgroup_subsys;
 static const int MEM_CGROUP_RECLAIM_RETRIES = 5;
+static spinlock_t mem_cgroup_sl_list_lock;	/* spin lock that protects */
+						/* the list of cgroups over*/
+						/* their soft limit */
+static struct list_head mem_cgroup_sl_exceeded_list;
 
 /*
  * Statistics for memory cgroup.
@@ -136,6 +140,10 @@ struct mem_cgroup {
 	 * statistics.
 	 */
 	struct mem_cgroup_stat stat;
+	/*
+	 * List of all mem_cgroup's that exceed their soft limit
+	 */
+	struct list_head sl_exceeded_list;
 };
 
 /*
@@ -679,6 +687,18 @@ retry:
 		goto retry;
 	}
 
+	/*
+	 * If we exceed our soft limit, we get added to the list of
+	 * cgroups over their soft limit
+	 */
+	if (!res_counter_check_under_limit(&mem->res, RES_SOFT_LIMIT)) {
+		spin_lock_irqsave(&mem_cgroup_sl_list_lock, flags);
+		if (list_empty(&mem->sl_exceeded_list))
+			list_add_tail(&mem->sl_exceeded_list,
+						&mem_cgroup_sl_exceeded_list);
+		spin_unlock_irqrestore(&mem_cgroup_sl_list_lock, flags);
+	}
+
 	mz = page_cgroup_zoneinfo(pc);
 	spin_lock_irqsave(&mz->lru_lock, flags);
 	/* Update statistics vector */
@@ -736,13 +756,14 @@ void mem_cgroup_uncharge(struct page_cgr
 	if (atomic_dec_and_test(&pc->ref_cnt)) {
 		page = pc->page;
 		mz = page_cgroup_zoneinfo(pc);
+		mem = pc->mem_cgroup;
 		/*
 		 * get page->cgroup and clear it under lock.
 		 * force_empty can drop page->cgroup without checking refcnt.
 		 */
 		unlock_page_cgroup(page);
+
 		if (clear_page_cgroup(page, pc) == pc) {
-			mem = pc->mem_cgroup;
 			css_put(&mem->css);
 			res_counter_uncharge(&mem->res, PAGE_SIZE);
 			spin_lock_irqsave(&mz->lru_lock, flags);
@@ -1046,6 +1067,12 @@ static struct cftype mem_cgroup_files[] 
 		.name = "stat",
 		.open = mem_control_stat_open,
 	},
+	{
+		.name = "soft_limit_in_bytes",
+		.private = RES_SOFT_LIMIT,
+		.write = mem_cgroup_write,
+		.read = mem_cgroup_read,
+	},
 };
 
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
@@ -1097,6 +1124,9 @@ mem_cgroup_create(struct cgroup_subsys *
 	if (unlikely((cont->parent) == NULL)) {
 		mem = &init_mem_cgroup;
 		init_mm.mem_cgroup = mem;
+		INIT_LIST_HEAD(&mem->sl_exceeded_list);
+		spin_lock_init(&mem_cgroup_sl_list_lock);
+		INIT_LIST_HEAD(&mem_cgroup_sl_exceeded_list);
 	} else
 		mem = kzalloc(sizeof(struct mem_cgroup), GFP_KERNEL);
 

mm/memcontrol.c mem_cgroup_create(struct cgroup_subsys * · line 1138

Quoted 5 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +1 -0
@@ -1104,6 +1134,7 @@ mem_cgroup_create(struct cgroup_subsys *
 		return NULL;
 
 	res_counter_init(&mem->res);
+	INIT_LIST_HEAD(&mem->sl_exceeded_list);
 

mem->sl_exceeded_list initialized twice ?

Quoted 4 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +0 -0
 	memset(&mem->info, 0, sizeof(mem->info));
 

_

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw

Re: [mm] [PATCH 2/4] Add the soft limit interface v2

Li Zefan a??e??:

Quoted 5 lines
Quoted 4 lines · Balbir Singh

A new configuration file called soft_limit_in_bytes is added. The parsing and configuration rules remain the same as for the limit_in_bytes user interface.

Quoted 9 lines
Quoted 6 lines

A global list of all memory cgroups over their soft limit is maintained. This list is then used to reclaim memory on global pressure. A cgroup is removed from the list when the cgroup is deleted.

The global list is protected with a read-write spinlock.

You are not using read-write spinlock..

Ah, the spinlock is changed to r/w spinlock in [PATCH 3/4].

Quoted 98 lines
Quoted 90 lines

---

 mm/memcontrol.c |   33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff -puN mm/memcontrol.c~memory-controller-add-soft-limit-interface mm/memcontrol.c

mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +31 -1
@@ -35,6 +35,10 @@
 
 struct cgroup_subsys mem_cgroup_subsys;
 static const int MEM_CGROUP_RECLAIM_RETRIES = 5;
+static spinlock_t mem_cgroup_sl_list_lock;	/* spin lock that protects */
+						/* the list of cgroups over*/
+						/* their soft limit */
+static struct list_head mem_cgroup_sl_exceeded_list;
 
 /*
  * Statistics for memory cgroup.
@@ -136,6 +140,10 @@ struct mem_cgroup {
 	 * statistics.
 	 */
 	struct mem_cgroup_stat stat;
+	/*
+	 * List of all mem_cgroup's that exceed their soft limit
+	 */
+	struct list_head sl_exceeded_list;
 };
 
 /*
@@ -679,6 +687,18 @@ retry:
 		goto retry;
 	}
 
+	/*
+	 * If we exceed our soft limit, we get added to the list of
+	 * cgroups over their soft limit
+	 */
+	if (!res_counter_check_under_limit(&mem->res, RES_SOFT_LIMIT)) {
+		spin_lock_irqsave(&mem_cgroup_sl_list_lock, flags);
+		if (list_empty(&mem->sl_exceeded_list))
+			list_add_tail(&mem->sl_exceeded_list,
+						&mem_cgroup_sl_exceeded_list);
+		spin_unlock_irqrestore(&mem_cgroup_sl_list_lock, flags);
+	}
+
 	mz = page_cgroup_zoneinfo(pc);
 	spin_lock_irqsave(&mz->lru_lock, flags);
 	/* Update statistics vector */
@@ -736,13 +756,14 @@ void mem_cgroup_uncharge(struct page_cgr
 	if (atomic_dec_and_test(&pc->ref_cnt)) {
 		page = pc->page;
 		mz = page_cgroup_zoneinfo(pc);
+		mem = pc->mem_cgroup;
 		/*
 		 * get page->cgroup and clear it under lock.
 		 * force_empty can drop page->cgroup without checking refcnt.
 		 */
 		unlock_page_cgroup(page);
+
 		if (clear_page_cgroup(page, pc) == pc) {
-			mem = pc->mem_cgroup;
 			css_put(&mem->css);
 			res_counter_uncharge(&mem->res, PAGE_SIZE);
 			spin_lock_irqsave(&mz->lru_lock, flags);
@@ -1046,6 +1067,12 @@ static struct cftype mem_cgroup_files[] 
 		.name = "stat",
 		.open = mem_control_stat_open,
 	},
+	{
+		.name = "soft_limit_in_bytes",
+		.private = RES_SOFT_LIMIT,
+		.write = mem_cgroup_write,
+		.read = mem_cgroup_read,
+	},
 };
 
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
@@ -1097,6 +1124,9 @@ mem_cgroup_create(struct cgroup_subsys *
 	if (unlikely((cont->parent) == NULL)) {
 		mem = &init_mem_cgroup;
 		init_mm.mem_cgroup = mem;
+		INIT_LIST_HEAD(&mem->sl_exceeded_list);
+		spin_lock_init(&mem_cgroup_sl_list_lock);
+		INIT_LIST_HEAD(&mem_cgroup_sl_exceeded_list);
 	} else
 		mem = kzalloc(sizeof(struct mem_cgroup), GFP_KERNEL);
 

mm/memcontrol.c mem_cgroup_create(struct cgroup_subsys * · line 1138

Quoted 5 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +1 -0
@@ -1104,6 +1134,7 @@ mem_cgroup_create(struct cgroup_subsys *
 		return NULL;
 
 	res_counter_init(&mem->res);
+	INIT_LIST_HEAD(&mem->sl_exceeded_list);
 

mem->sl_exceeded_list initialized twice ?

Quoted 5 lines
Quoted 4 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +0 -0
 	memset(&mem->info, 0, sizeof(mem->info));
 

_

--

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#5 lore raw

Re: [mm] [PATCH 2/4] Add the soft limit interface v2

Quoted 10 lines · Li Zefan

Li Zefan a??e??:

Quoted 9 lines
Quoted 8 lines · Balbir Singh

A new configuration file called soft_limit_in_bytes is added. The parsing and configuration rules remain the same as for the limit_in_bytes user interface.

A global list of all memory cgroups over their soft limit is maintained. This list is then used to reclaim memory on global pressure. A cgroup is removed from the list when the cgroup is deleted.

Quoted 104 lines
Quoted 4 lines
Quoted 2 lines

The global list is protected with a read-write spinlock.

You are not using read-write spinlock..

Ah, the spinlock is changed to r/w spinlock in [PATCH 3/4].

Quoted 97 lines
Quoted 90 lines

---

 mm/memcontrol.c |   33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff -puN mm/memcontrol.c~memory-controller-add-soft-limit-interface mm/memcontrol.c

mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +31 -1
@@ -35,6 +35,10 @@
 
 struct cgroup_subsys mem_cgroup_subsys;
 static const int MEM_CGROUP_RECLAIM_RETRIES = 5;
+static spinlock_t mem_cgroup_sl_list_lock;	/* spin lock that protects */
+						/* the list of cgroups over*/
+						/* their soft limit */
+static struct list_head mem_cgroup_sl_exceeded_list;
 
 /*
  * Statistics for memory cgroup.
@@ -136,6 +140,10 @@ struct mem_cgroup {
 	 * statistics.
 	 */
 	struct mem_cgroup_stat stat;
+	/*
+	 * List of all mem_cgroup's that exceed their soft limit
+	 */
+	struct list_head sl_exceeded_list;
 };
 
 /*
@@ -679,6 +687,18 @@ retry:
 		goto retry;
 	}
 
+	/*
+	 * If we exceed our soft limit, we get added to the list of
+	 * cgroups over their soft limit
+	 */
+	if (!res_counter_check_under_limit(&mem->res, RES_SOFT_LIMIT)) {
+		spin_lock_irqsave(&mem_cgroup_sl_list_lock, flags);
+		if (list_empty(&mem->sl_exceeded_list))
+			list_add_tail(&mem->sl_exceeded_list,
+						&mem_cgroup_sl_exceeded_list);
+		spin_unlock_irqrestore(&mem_cgroup_sl_list_lock, flags);
+	}
+
 	mz = page_cgroup_zoneinfo(pc);
 	spin_lock_irqsave(&mz->lru_lock, flags);
 	/* Update statistics vector */
@@ -736,13 +756,14 @@ void mem_cgroup_uncharge(struct page_cgr
 	if (atomic_dec_and_test(&pc->ref_cnt)) {
 		page = pc->page;
 		mz = page_cgroup_zoneinfo(pc);
+		mem = pc->mem_cgroup;
 		/*
 		 * get page->cgroup and clear it under lock.
 		 * force_empty can drop page->cgroup without checking refcnt.
 		 */
 		unlock_page_cgroup(page);
+
 		if (clear_page_cgroup(page, pc) == pc) {
-			mem = pc->mem_cgroup;
 			css_put(&mem->css);
 			res_counter_uncharge(&mem->res, PAGE_SIZE);
 			spin_lock_irqsave(&mz->lru_lock, flags);
@@ -1046,6 +1067,12 @@ static struct cftype mem_cgroup_files[] 
 		.name = "stat",
 		.open = mem_control_stat_open,
 	},
+	{
+		.name = "soft_limit_in_bytes",
+		.private = RES_SOFT_LIMIT,
+		.write = mem_cgroup_write,
+		.read = mem_cgroup_read,
+	},
 };
 
 static int alloc_mem_cgroup_per_zone_info(struct mem_cgroup *mem, int node)
@@ -1097,6 +1124,9 @@ mem_cgroup_create(struct cgroup_subsys *
 	if (unlikely((cont->parent) == NULL)) {
 		mem = &init_mem_cgroup;
 		init_mm.mem_cgroup = mem;
+		INIT_LIST_HEAD(&mem->sl_exceeded_list);
+		spin_lock_init(&mem_cgroup_sl_list_lock);
+		INIT_LIST_HEAD(&mem_cgroup_sl_exceeded_list);
 	} else
 		mem = kzalloc(sizeof(struct mem_cgroup), GFP_KERNEL);
 

mm/memcontrol.c mem_cgroup_create(struct cgroup_subsys * · line 1138

Quoted 5 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +1 -0
@@ -1104,6 +1134,7 @@ mem_cgroup_create(struct cgroup_subsys *
 		return NULL;
 
 	res_counter_init(&mem->res);
+	INIT_LIST_HEAD(&mem->sl_exceeded_list);
 

mem->sl_exceeded_list initialized twice ?

Good catch, yes for the root, it can be initialized twice. I'll fix it in v3.

Quoted 5 lines
Quoted 5 lines
Quoted 4 lines
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-add-soft-limit-interface +0 -0
 	memset(&mem->info, 0, sizeof(mem->info));
 

_

--

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#6 lore raw

Re: [mm] [PATCH 2/4] Add the soft limit interface v2

In reply to Li Zefan
Quoted 10 lines · Li Zefan

Li Zefan a??e??:

Quoted 9 lines
Quoted 8 lines · Balbir Singh

A new configuration file called soft_limit_in_bytes is added. The parsing and configuration rules remain the same as for the limit_in_bytes user interface.

A global list of all memory cgroups over their soft limit is maintained. This list is then used to reclaim memory on global pressure. A cgroup is removed from the list when the cgroup is deleted.

Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

The global list is protected with a read-write spinlock.

You are not using read-write spinlock..

Ah, the spinlock is changed to r/w spinlock in [PATCH 3/4].

Yes, I'll fix that as well in v3. Thanks for spotting it.

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#7 lore raw

PATCH 3/4 Reclaim from groups over their soft limit under memory pressure v2

In reply to Balbir Singh

Changelog v2

1. Take reference on mem->css in pushback (YAMAMOTO Takshi)
2. Move away from trying to reclaim nr_pages over soft limit to swap cluster at a time (KAMEZAWA Hiroyuki)

The global list of all cgroups over their soft limit is scanned under memory pressure. We call mem_cgroup_pushback_groups_over_soft_limit from __alloc_pages() prior to calling try_to_free_pages(), in an attempt to rescue memory from groups that are using memory above their soft limit. If this attempt is unsuccessfull, we call try_to_free_pages() and take the normal global reclaim path.

---

 include/linux/memcontrol.h  |    9 +++++
 include/linux/res_counter.h |   11 ++++++
 include/linux/swap.h        |    4 +-
 mm/memcontrol.c             |   76 ++++++++++++++++++++++++++++++++++++++++----
 mm/page_alloc.c             |   10 +++++
 mm/vmscan.c                 |   12 ++++--
 6 files changed, 110 insertions(+), 12 deletions(-)
include/linux/memcontrol.h renamed from include/linux/memcontrol.h~memory-controller-reclaim-on-contention +9 -0
diff -puN include/linux/memcontrol.h~memory-controller-reclaim-on-contention include/linux/memcontrol.h
@@ -71,6 +71,8 @@ extern long mem_cgroup_calc_reclaim_acti
 				struct zone *zone, int priority);
 extern long mem_cgroup_calc_reclaim_inactive(struct mem_cgroup *mem,
 				struct zone *zone, int priority);
+extern unsigned long
+mem_cgroup_pushback_groups_over_soft_limit(struct zone **zones, gfp_t gfp_mask);
 
 #else /* CONFIG_CGROUP_MEM_CONT */
 static inline void mm_init_cgroup(struct mm_struct *mm,
@@ -179,6 +181,13 @@ static inline long mem_cgroup_calc_recla
 {
 	return 0;
 }
+
+static inline unsigned long
+mem_cgroup_pushback_groups_over_soft_limit(struct zone **zones, gfp_t gfp_mask)
+{
+	return 0;
+}
+
 #endif /* CONFIG_CGROUP_MEM_CONT */
 
 #endif /* _LINUX_MEMCONTROL_H */
include/linux/res_counter.h renamed from include/linux/res_counter.h~memory-controller-reclaim-on-contention +11 -0
diff -puN include/linux/res_counter.h~memory-controller-reclaim-on-contention include/linux/res_counter.h
@@ -140,4 +140,15 @@ static inline bool res_counter_check_und
 	return ret;
 }
 
+static inline long long res_counter_sl_excess(struct res_counter *cnt)
+{
+	unsigned long flags;
+	long long ret;
+
+	spin_lock_irqsave(&cnt->lock, flags);
+	ret = cnt->usage - cnt->soft_limit;
+	spin_unlock_irqrestore(&cnt->lock, flags);
+	return ret;
+}
+
 #endif
include/linux/swap.h renamed from include/linux/swap.h~memory-controller-reclaim-on-contention +3 -1
diff -puN include/linux/swap.h~memory-controller-reclaim-on-contention include/linux/swap.h
@@ -184,7 +184,9 @@ extern void swap_setup(void);
 extern unsigned long try_to_free_pages(struct zone **zones, int order,
 					gfp_t gfp_mask);
 extern unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem,
-							gfp_t gfp_mask);
+							gfp_t gfp_mask,
+							unsigned long nr_pages,
+							struct zone **zones);
 extern int __isolate_lru_page(struct page *page, int mode);
 extern unsigned long shrink_all_memory(unsigned long nr_pages);
 extern int vm_swappiness;
mm/memcontrol.c renamed from mm/memcontrol.c~memory-controller-reclaim-on-contention +70 -6

117 lines not on this page · raw message

mm/page_alloc.c renamed from mm/page_alloc.c~memory-controller-reclaim-on-contention +9 -1
diff -puN mm/page_alloc.c~memory-controller-reclaim-on-contention mm/page_alloc.c
@@ -1635,7 +1635,15 @@ nofail_alloc:
 	reclaim_state.reclaimed_slab = 0;
 	p->reclaim_state = &reclaim_state;
 
-	did_some_progress = try_to_free_pages(zonelist->zones, order, gfp_mask);
+	/*
+	 * First reclaim from all memory control groups over their
+	 * soft limit
+	 */
+	did_some_progress = mem_cgroup_pushback_groups_over_soft_limit(
+						zonelist->zones, gfp_mask);
+	if (!did_some_progress)
+		did_some_progress =
+			try_to_free_pages(zonelist->zones, order, gfp_mask);
 
 	p->reclaim_state = NULL;
 	p->flags &= ~PF_MEMALLOC;
mm/vmscan.c renamed from mm/vmscan.c~memory-controller-reclaim-on-contention +8 -4
diff -puN mm/vmscan.c~memory-controller-reclaim-on-contention mm/vmscan.c
@@ -1440,22 +1440,26 @@ unsigned long try_to_free_pages(struct z
 #ifdef CONFIG_CGROUP_MEM_CONT
 
 unsigned long try_to_free_mem_cgroup_pages(struct mem_cgroup *mem_cont,
-						gfp_t gfp_mask)
+						gfp_t gfp_mask,
+						unsigned long nr_pages,
+						struct zone **zones)
 {
 	struct scan_control sc = {
 		.gfp_mask = gfp_mask,
 		.may_writepage = !laptop_mode,
 		.may_swap = 1,
-		.swap_cluster_max = SWAP_CLUSTER_MAX,
+		.swap_cluster_max = nr_pages,
 		.swappiness = vm_swappiness,
 		.order = 0,
 		.mem_cgroup = mem_cont,
 		.isolate_pages = mem_cgroup_isolate_pages,
 	};
-	struct zone **zones;
 	int target_zone = gfp_zone(GFP_HIGHUSER_MOVABLE);
 
-	zones = NODE_DATA(numa_node_id())->node_zonelists[target_zone].zones;
+	if (!zones)
+		zones =
+		NODE_DATA(numa_node_id())->node_zonelists[target_zone].zones;
+
 	if (do_try_to_free_pages(zones, sc.gfp_mask, &sc))
 		return 1;
 	return 0;

_

Signature · 4 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL
#8 lore raw

PATCH 4/4 Add soft limit documentation v2

In reply to Balbir Singh

Add documentation for the soft limit feature.

Changelog v2 (Thanks to the review by Randy Dunlap)
1. Change several misuses of it's to its
2. Fix spelling errors and punctuation

---

 Documentation/controllers/memory.txt |   18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)
Documentation/controllers/memory.txt renamed from Documentation/controllers/memory.txt~memory-controller-add-soft-limit-documentation +17 -1
diff -puN Documentation/controllers/memory.txt~memory-controller-add-soft-limit-documentation Documentation/controllers/memory.txt
@@ -201,6 +201,22 @@ The memory.force_empty gives an interfac
 
 will drop all charges in cgroup. Currently, this is maintained for test.
 
+The file memory.soft_limit_in_bytes allows users to set soft limits. A soft
+limit is set in a manner similar to limit. The limit feature described
+earlier is a hard limit. A group can never exceed its hard limit. A soft
+limit on the other hand can be exceeded. A group will be shrunk back
+to its soft limit, when there is memory pressure/contention.
+
+Ideally the soft limit should always be set to a value smaller than the
+hard limit. However, the code does not force the user to do so. The soft
+limit can be greater than the hard limit; then the soft limit has
+no meaning in that setup, since the group will always be restrained to its
+hard limit.
+
+Example setting of soft limit
+
+# echo -n 100M > memory.soft_limit_in_bytes
+
 4. Testing
 
 Balbir posted lmbench, AIM9, LTP and vmmstress results [10] and [11].
@@ -221,7 +237,7 @@ some of the pages cached in the cgroup (
 
 4.2 Task migration
 
-When a task migrates from one cgroup to another, it's charge is not
+When a task migrates from one cgroup to another, its charge is not
 carried forward. The pages allocated from the original cgroup still
 remain charged to it, the charge is dropped when the page is freed or
 reclaimed.

_

Signature · 10 lines
	Warm Regards,
	Balbir Singh
	Linux Technology Center
	IBM, ISTL

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>