Skip to contentSkip to conversation

Introduce memory allocation speed throttle in memcg

RFC In review [email protected] 5 participants 20 messages May 26, 2021 – Jun 4, 2021 1 message not in the archive

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

cover Introduce memory allocation speed throttle in memcg

From: Yulei Zhang <[email protected]>

In this patch set we present the idea to suppress the memory allocation speed in memory cgroup, which aims to avoid direct reclaim caused by memory allocation burst while under memory pressure.

As minimum watermark could be easily broken if certain tasks allocate massive amount of memory in a short period of time, in that case it will trigger the direct memory reclaim and cause unacceptable jitters for latency critical tasks, such as guaranteed pod task in K8s.

With memory allocation speed throttle(mst) mechanism we could lower the memory allocation speed in certian cgroup, usually for low priority tasks, so that could avoid the direct memory reclaim in time.

And per-memcg interfaces are introduced under memcg tree, not visiable for root memcg.
- <cgroup_root>/<cgroup_name>/memory.alloc_bps
- 0 -> means memory speed throttle disabled
- non-zero -> value in bytes for memory allocation speed limits

- <cgroup_root>/<cgroup_name>/memory.stat:mst_mem_spd_max
it records the max memory allocation speed of the memory cgroup in the last period of time slice

- <cgroup_root>/<cgroup_name>/memory.stat:mst_nr_throttled it represents the number of times for allocation throttling

Yulei Zhang (7):
mm: record total charge and max speed counter in memcg
mm: introduce alloc_bps to memcg for memory allocation speed throttle
mm: memory allocation speed throttle setup in hierarchy
mm: introduce slice analysis into memory speed throttle mechanism
mm: introduce memory allocation speed throttle
mm: record the numbers of memory allocation throttle
mm: introduce mst low and min watermark

 include/linux/memcontrol.h   |  23 +++
 include/linux/page_counter.h |   8 +
 init/Kconfig                 |   8 +
 mm/memcontrol.c              | 295 +++++++++++++++++++++++++++++++++++
 mm/page_counter.c            |  39 +++++
 5 files changed, 373 insertions(+)
Signature · 1 line
2.28.0

A message that is not in the archive (sent off-list or not yet received): cover.1622043596.git.yuleixzhang-1Nz4purKYjRBDgjK7y7TUQ@public.gmane.org

#2 lore raw

RFC 1/7 mm: record total charge and max speed counter in memcg

From: Yulei Zhang <[email protected]>

Add max memory allocation speed counter and total charge counter recording in memcg. Total charge page_counter aims to count the total number of charged pages, which will be used to do allocation speed throttle in following patches.

Max memory speed counter is used to record the memory alloction burst, and to evaluate the performace of the memory speed throttle(mst).

No lock used, we may live with the tiny inaccuracy.

---
 include/linux/page_counter.h |  8 ++++++++
 init/Kconfig                 |  8 ++++++++
 mm/memcontrol.c              | 38 +++++++++++++++++++++++++++++++++++
 mm/page_counter.c            | 39 ++++++++++++++++++++++++++++++++++++
 4 files changed, 93 insertions(+)
include/linux/page_counter.h +8 -0
index 679591301994..b8286c8f6704 100644
@@ -27,6 +27,14 @@ struct page_counter {
 	unsigned long watermark;
 	unsigned long failcnt;
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+	/* allocation speed throttle */
+	atomic_long_t total_chg;
+	atomic_long_t mem_spd_max;
+	atomic_long_t prev_spd_jifs;
+	atomic_long_t prev_total_chg;
+#endif
+
 	/*
 	 * 'parent' is placed here to be far from 'usage' to reduce
 	 * cache false sharing, as 'usage' is written mostly while
init/Kconfig +8 -0
index 1ea12c64e4c9..1e44be0675a2 100644
@@ -921,6 +921,14 @@ config MEMCG_KMEM
 	depends on MEMCG && !SLOB
 	default y
 
+config MEM_SPEED_THROTTLE
+	bool "Memory allocation speed throttle for memcg"
+	depends on MEMCG
+	help
+	  This enables memory allocation speed throttle feature for memcg.
+	  It can be used to limit the memory allocation speed(bps) for memcg,
+	  and only works under memory pressure.
+
 config BLK_CGROUP
 	bool "IO controller"
 	depends on BLOCK
mm/memcontrol.c +38 -0
index 64ada9e650a5..7ef6aa088680 100644
@@ -1392,6 +1392,37 @@ static int memcg_page_state_unit(int item)
 	}
 }
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+static unsigned long mem_cgroup_mst_get_mem_spd_max(struct mem_cgroup *memcg)
+{
+	struct page_counter *c = &memcg->memory;
+	unsigned long deadline;
+
+	/* Reset speed if it is too old */
+	deadline = atomic_long_read(&c->prev_spd_jifs) + 5 * HZ;
+	if (time_after(jiffies, deadline))
+		atomic_long_set(&c->mem_spd_max, 0);
+
+	/* Clear after read */
+	return atomic_long_xchg(&c->mem_spd_max, 0) << PAGE_SHIFT;
+}
+
+static void mem_cgroup_mst_show_mem_spd_max(struct mem_cgroup *memcg,
+					    struct seq_file *m)
+{
+	if (mem_cgroup_is_root(memcg))
+		return;
+
+	seq_printf(m, "mst_mem_spd_max %lu\n",
+		   mem_cgroup_mst_get_mem_spd_max(memcg));
+}
+#else /* CONFIG_MEM_SPEED_THROTTLE */
+static void mem_cgroup_mst_show_mem_spd_max(struct mem_cgroup *memcg,
+					    struct seq_file *m)
+{
+}
+#endif
+
 static inline unsigned long memcg_page_state_output(struct mem_cgroup *memcg,
 						    int item)
 {
@@ -1462,6 +1493,11 @@ static char *memory_stat_format(struct mem_cgroup *memcg)
 		       memcg_events(memcg, THP_COLLAPSE_ALLOC));
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+	seq_buf_printf(&s, "mst_mem_spd_max %lu\n",
+		mem_cgroup_mst_get_mem_spd_max(memcg));
+#endif
+
 	/* The above should easily fit into one page */
 	WARN_ON_ONCE(seq_buf_has_overflowed(&s));
 
@@ -3909,6 +3945,8 @@ static int memcg_stat_show(struct seq_file *m, void *v)
 	}
 #endif
 
+	mem_cgroup_mst_show_mem_spd_max(memcg, m);
+
 	return 0;
 }
 
mm/page_counter.c +39 -0
index 7d83641eb86b..d5e2cf4daf4f 100644
@@ -61,6 +61,39 @@ void page_counter_cancel(struct page_counter *counter, unsigned long nr_pages)
 	propagate_protected_usage(counter, new);
 }
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+static void mem_speed_update(struct page_counter *c, unsigned long nr_pages)
+{
+	unsigned long prev_jifs, chg, spd_max;
+	long delta_jifs, delta_chg;
+
+	chg = atomic_long_add_return(nr_pages, &c->total_chg);
+	prev_jifs = atomic_long_read(&c->prev_spd_jifs);
+
+	/* First time we get here? */
+	if (!prev_jifs)
+		goto done;
+
+	delta_jifs = jiffies - prev_jifs;
+	delta_chg = chg - atomic_long_read(&c->prev_total_chg);
+
+	if (delta_jifs <= HZ || delta_chg <= 0)
+		return;
+
+	spd_max = max(delta_chg * HZ / delta_jifs,
+			atomic_long_read(&c->mem_spd_max));
+	atomic_long_set(&c->mem_spd_max, spd_max);
+
+done:
+	atomic_long_set(&c->prev_spd_jifs, jiffies);
+	atomic_long_set(&c->prev_total_chg, chg);
+}
+#else
+static void mem_speed_update(struct page_counter *c, unsigned long nr_pages)
+{
+}
+#endif
+
 /**
  * page_counter_charge - hierarchically charge pages
  * @counter: counter
@@ -83,6 +116,9 @@ void page_counter_charge(struct page_counter *counter, unsigned long nr_pages)
 		 */
 		if (new > READ_ONCE(c->watermark))
 			WRITE_ONCE(c->watermark, new);
+
+		/* Update mem speed max, maybe inaccurate due to races */
+		mem_speed_update(c, nr_pages);
 	}
 }
 
@@ -137,6 +173,9 @@ bool page_counter_try_charge(struct page_counter *counter,
 		 */
 		if (new > READ_ONCE(c->watermark))
 			WRITE_ONCE(c->watermark, new);
+
+		/* Update mem speed max, maybe inaccurate due to races */
+		mem_speed_update(c, nr_pages);
 	}
 	return true;
 
Signature · 1 line
2.28.0
#3 lore raw

RFC 2/7 mm: introduce alloc_bps to memcg for memory allocation speed throttle

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

Add read/write attribute memory.alloc_bps to configure the allowable memory allocation speed of the memory cgroup in bytes.

---
 include/linux/memcontrol.h | 10 ++++++++++
 mm/memcontrol.c            | 38 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 48 insertions(+)
include/linux/memcontrol.h +10 -0
index c193be760709..4938397f321d 100644
@@ -230,6 +230,12 @@ struct obj_cgroup {
 	};
 };
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+struct mem_spd_ctl {
+	unsigned long mem_spd_lmt;
+};
+#endif
+
 /*
  * The memory controller data structure. The memory controller controls both
  * page cache and RSS per cgroup. We would eventually like to provide
@@ -349,6 +355,10 @@ struct mem_cgroup {
 	struct deferred_split deferred_split_queue;
 #endif
 
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+	struct mem_spd_ctl msc;
+#endif
+
 	struct mem_cgroup_per_node *nodeinfo[0];
 	/* WARNING: nodeinfo must be the last member here */
 };
mm/memcontrol.c +38 -0
index 7ef6aa088680..d59abe15d89d 100644
@@ -1393,6 +1393,28 @@ static int memcg_page_state_unit(int item)
 }
 
 #ifdef CONFIG_MEM_SPEED_THROTTLE
+static u64 mem_cgroup_mem_spd_lmt_read(struct cgroup_subsys_state *css,
+				       struct cftype *cft)
+{
+	struct mem_cgroup *memcg = mem_cgroup_from_css(css);
+
+	return (u64)(memcg->msc.mem_spd_lmt << PAGE_SHIFT);
+}
+
+static int mem_cgroup_mem_spd_lmt_write(struct cgroup_subsys_state *css,
+					struct cftype *cft, u64 val)
+{
+	struct mem_cgroup *memcg;
+	unsigned long lmt;
+
+	memcg = mem_cgroup_from_css(css);
+	lmt = val >> PAGE_SHIFT;
+
+	memcg->msc.mem_spd_lmt = lmt;
+
+	return 0;
+}
+
 static unsigned long mem_cgroup_mst_get_mem_spd_max(struct mem_cgroup *memcg)
 {
 	struct page_counter *c = &memcg->memory;
@@ -4805,6 +4827,14 @@ static struct cftype mem_cgroup_legacy_files[] = {
 		.write_u64 = mem_cgroup_hierarchy_write,
 		.read_u64 = mem_cgroup_hierarchy_read,
 	},
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+	{
+		.name = "alloc_bps",
+		.flags = CFTYPE_NOT_ON_ROOT,
+		.read_u64 = mem_cgroup_mem_spd_lmt_read,
+		.write_u64 = mem_cgroup_mem_spd_lmt_write,
+	},
+#endif
 	{
 		.name = "cgroup.event_control",		/* XXX: for compat */
 		.write = memcg_write_event_control,
@@ -6358,6 +6388,14 @@ static struct cftype memory_files[] = {
 		.seq_show = memory_oom_group_show,
 		.write = memory_oom_group_write,
 	},
+#ifdef CONFIG_MEM_SPEED_THROTTLE
+	{
+		.name = "alloc_bps",
+		.write_u64 = mem_cgroup_mem_spd_lmt_write,
+		.read_u64 = mem_cgroup_mem_spd_lmt_read,
+		.flags = CFTYPE_NOT_ON_ROOT,
+	},
+#endif
 	{ }	/* terminate */
 };
 
Signature · 1 line
2.28.0
#4 lore raw

RFC 3/7 mm: memory allocation speed throttle setup in hierarchy

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

Init the memory allocation speed throttle parametersi to make sure the throttle feature works in hierarchy structure.

---
 include/linux/memcontrol.h |  1 +
 mm/memcontrol.c            | 50 +++++++++++++++++++++++++++++++++++++-
 2 files changed, 50 insertions(+), 1 deletion(-)
include/linux/memcontrol.h +1 -0
index 4938397f321d..435515b1a709 100644
@@ -233,6 +233,7 @@ struct obj_cgroup {
 #ifdef CONFIG_MEM_SPEED_THROTTLE
 struct mem_spd_ctl {
 	unsigned long mem_spd_lmt;
+	int has_lmt;
 };
 #endif
 
mm/memcontrol.c +49 -1

86 lines not on this page · raw message

Signature · 1 line
2.28.0
#5 lore raw

RFC 4/7 mm: introduce slice analysis into memory speed throttle mechanism

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

We separate the statistical period into 10 slices, count the memory charge in each slice, and compare to the slice speed limit to determine to turn on the throttle or not.

---
 include/linux/memcontrol.h |  4 ++++
 mm/memcontrol.c            | 25 +++++++++++++++++++++++++
 2 files changed, 29 insertions(+)
include/linux/memcontrol.h +4 -0
index 435515b1a709..230acdc635b5 100644
@@ -231,8 +231,12 @@ struct obj_cgroup {
 };
 
 #ifdef CONFIG_MEM_SPEED_THROTTLE
+#define MST_SLICE		(HZ/10)
 struct mem_spd_ctl {
 	unsigned long mem_spd_lmt;
+	unsigned long slice_lmt;
+	unsigned long prev_thl_jifs;
+	unsigned long prev_chg;
 	int has_lmt;
 };
 #endif
mm/memcontrol.c +25 -0
index 9d7a86f7f51c..05bf4ba0da15 100644
@@ -1403,6 +1403,9 @@ static void mem_cgroup_mst_msc_reset(struct mem_cgroup *memcg)
 	msc = &memcg->msc;
 	msc->has_lmt = 0;
 	msc->mem_spd_lmt = 0;
+	msc->slice_lmt = 0;
+	msc->prev_chg = 0;
+	msc->prev_thl_jifs = 0;
 }
 
 static void mem_cgroup_mst_has_lmt_init(struct mem_cgroup *memcg)
@@ -1440,6 +1443,7 @@ static int mem_cgroup_mem_spd_lmt_write(struct cgroup_subsys_state *css,
 	lmt = val >> PAGE_SHIFT;
 
 	memcg->msc.mem_spd_lmt = lmt;
+	memcg->msc.slice_lmt = lmt * MST_SLICE / HZ;
 
 	/* Sync with mst_has_lmt_init*/
 	synchronize_rcu();
@@ -1452,6 +1456,27 @@ static int mem_cgroup_mem_spd_lmt_write(struct cgroup_subsys_state *css,
 	return 0;
 }
 
+/*
+ * Update the memory speed throttle slice window.
+ * Return 0 when we still in the previous slice window
+ * Return 1 when we start a new slice window
+ */
+static int mem_cgroup_update_mst_slice(struct mem_cgroup *memcg)
+{
+	unsigned long total_charge;
+	struct mem_spd_ctl *msc = &memcg->msc;
+
+	if (msc->prev_thl_jifs &&
+	    time_before(jiffies, (msc->prev_thl_jifs + MST_SLICE)))
+		return 0;
+
+	total_charge = atomic_long_read(&memcg->memory.total_chg);
+	msc->prev_chg = total_charge;
+	msc->prev_thl_jifs = jiffies;
+
+	return 1;
+}
+
 static unsigned long mem_cgroup_mst_get_mem_spd_max(struct mem_cgroup *memcg)
 {
 	struct page_counter *c = &memcg->memory;
Signature · 1 line
2.28.0
#6 lore raw

RFC 5/7 mm: introduce memory allocation speed throttle

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

In the memory charge handling, check if the allocated memory exceed the limitation in each slice period, then throttle the executing for a while to lower the memory allocation speed in memory cgroup to avoid triggering the direct reclaim frequently.

---
 include/linux/memcontrol.h |   1 +
 mm/memcontrol.c            | 103 +++++++++++++++++++++++++++++++++++--
 2 files changed, 99 insertions(+), 5 deletions(-)
include/linux/memcontrol.h +1 -0
index 230acdc635b5..b20820f7fdbf 100644
@@ -238,6 +238,7 @@ struct mem_spd_ctl {
 	unsigned long prev_thl_jifs;
 	unsigned long prev_chg;
 	int has_lmt;
+	atomic_t updating;
 };
 #endif
 
mm/memcontrol.c +98 -5

140 lines not on this page · raw message

Signature · 1 line
2.28.0
#7 lore raw

RFC 6/7 mm: record the numbers of memory allocation throttle

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

Record the number of times we trigger the memory allocation throttle.

---
 include/linux/memcontrol.h |  1 +
 mm/memcontrol.c            | 34 +++++++++++++++++++++++++++++-----
 2 files changed, 30 insertions(+), 5 deletions(-)
include/linux/memcontrol.h +1 -0
index b20820f7fdbf..59e6cb78a07a 100644
@@ -239,6 +239,7 @@ struct mem_spd_ctl {
 	unsigned long prev_chg;
 	int has_lmt;
 	atomic_t updating;
+	atomic_long_t nr_throttled;
 };
 #endif
 
mm/memcontrol.c +29 -5
index a1e33a9e6594..ca39974403a3 100644
@@ -1401,11 +1401,19 @@ static void mem_cgroup_mst_msc_reset(struct mem_cgroup *memcg)
 		return;
 
 	msc = &memcg->msc;
-	msc->has_lmt = 0;
-	msc->mem_spd_lmt = 0;
-	msc->slice_lmt = 0;
-	msc->prev_chg = 0;
-	msc->prev_thl_jifs = 0;
+	while (msc->prev_thl_jifs ||
+	    msc->prev_chg ||
+	    msc->slice_lmt ||
+	    msc->mem_spd_lmt ||
+	    msc->has_lmt ||
+	    atomic_long_read(&msc->nr_throttled)) {
+		atomic_long_set(&msc->nr_throttled, 0);
+		msc->has_lmt = 0;
+		msc->mem_spd_lmt = 0;
+		msc->slice_lmt = 0;
+		msc->prev_chg = 0;
+		msc->prev_thl_jifs = 0;
+	}
 }
 
 static void mem_cgroup_mst_has_lmt_init(struct mem_cgroup *memcg)
@@ -1552,6 +1560,8 @@ static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
 	if (!ret)
 		return;
 
+	atomic_long_inc(&msc->nr_throttled);
+
 	/*
 	 * Throttle the allocation for amount of jiffies according to
 	 * the fraction between the actual memory usage and allowed
@@ -1586,11 +1596,22 @@ static void mem_cgroup_mst_show_mem_spd_max(struct mem_cgroup *memcg,
 	seq_printf(m, "mst_mem_spd_max %lu\n",
 		   mem_cgroup_mst_get_mem_spd_max(memcg));
 }
+
+static void mem_cgroup_mst_show_nr_throttled(struct mem_cgroup *memcg,
+					     struct seq_file *m)
+{
+	seq_printf(m, "mst_nr_throttled %lu\n",
+		   atomic_long_read(&memcg->msc.nr_throttled));
+}
 #else /* CONFIG_MEM_SPEED_THROTTLE */
 static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
 {
 }
 
+static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
+{
+}
+
 static void mem_cgroup_mst_has_lmt_init(struct mem_cgroup *memcg)
 {
 }
@@ -1678,6 +1699,8 @@ static char *memory_stat_format(struct mem_cgroup *memcg)
 #ifdef CONFIG_MEM_SPEED_THROTTLE
 	seq_buf_printf(&s, "mst_mem_spd_max %lu\n",
 		mem_cgroup_mst_get_mem_spd_max(memcg));
+	seq_buf_printf(&s, "mst_nr_throttled %lu\n",
+		atomic_long_read(&memcg->msc.nr_throttled));
 #endif
 
 	/* The above should easily fit into one page */
@@ -4131,6 +4154,7 @@ static int memcg_stat_show(struct seq_file *m, void *v)
 #endif
 
 	mem_cgroup_mst_show_mem_spd_max(memcg, m);
+	mem_cgroup_mst_show_nr_throttled(memcg, m);
 
 	return 0;
 }
Signature · 1 line
2.28.0
#8 lore raw

Re: [RFC 6/7] mm: record the numbers of memory allocation throttle

[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]

Hi,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on linus/master]
[also build test ERROR on v5.13-rc3]
[cannot apply to hnaz-linux-mm/master next-20210526]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/yulei-kernel-gmail-com/Introduce-memory-allocation-speed-throttle-in-memcg/20210527-002103
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ad9f25d338605d26acedcaf3ba5fab5ca26f1c10
config: x86_64-randconfig-s022-20210526 (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.3-341-g8af24329-dirty
        # https://github.com/0day-ci/linux/commit/9ae850a0a4c9f5b03c8673b0bbe10344c637b4ec
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review yulei-kernel-gmail-com/Introduce-memory-allocation-speed-throttle-in-memcg/20210527-002103
        git checkout 9ae850a0a4c9f5b03c8673b0bbe10344c637b4ec
        # save the attached .config to linux build tree
        make W=1 C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate

All errors (new ones prefixed by >>):

>> mm/memcontrol.c:1611:13: error: redefinition of 'mem_cgroup_mst_spd_throttle'
    1611 | static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/memcontrol.c:1607:13: note: previous definition of 'mem_cgroup_mst_spd_throttle' was here
    1607 | static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
         |             ^~~~~~~~~~~~~~~~~~~~~~~~~~~
   mm/memcontrol.c: In function 'memcg_stat_show':
>> mm/memcontrol.c:4157:2: error: implicit declaration of function 'mem_cgroup_mst_show_nr_throttled'; did you mean 'mem_cgroup_mst_spd_throttle'? [-Werror=implicit-function-declaration]
    4157 |  mem_cgroup_mst_show_nr_throttled(memcg, m);
         |  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         |  mem_cgroup_mst_spd_throttle

At top level:
mm/memcontrol.c:1607:13: warning: 'mem_cgroup_mst_spd_throttle' defined but not used [-Wunused-function]
1607 | static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
cc1: some warnings being treated as errors

vim +/mem_cgroup_mst_spd_throttle +1611 mm/memcontrol.c

1610

> 1611	static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)

1612 {
1613 }
1614

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#9 lore raw

Re: [RFC 6/7] mm: record the numbers of memory allocation throttle

In reply to [email protected]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch]

Hi,

[FYI, it's a private test report for your RFC patch.]
[auto build test ERROR on linus/master]
[also build test ERROR on v5.13-rc3]
[cannot apply to hnaz-linux-mm/master next-20210526]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/yulei-kernel-gmail-com/Introduce-memory-allocation-speed-throttle-in-memcg/20210527-002103
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git ad9f25d338605d26acedcaf3ba5fab5ca26f1c10
config: arm64-randconfig-r014-20210526 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project 99155e913e9bad5f7f8a247f8bb3a3ff3da74af1)

reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# install arm64 cross compiling tool for clang build
# apt-get install binutils-aarch64-linux-gnu
# https://github.com/0day-ci/linux/commit/9ae850a0a4c9f5b03c8673b0bbe10344c637b4ec
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review yulei-kernel-gmail-com/Introduce-memory-allocation-speed-throttle-in-memcg/20210527-002103 git checkout 9ae850a0a4c9f5b03c8673b0bbe10344c637b4ec
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64

If you fix the issue, kindly add following tag as appropriate

All errors (new ones prefixed by >>):

mm/memcontrol.c:1611:13: error: redefinition of 'mem_cgroup_mst_spd_throttle' static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)

               ^
   mm/memcontrol.c:1607:13: note: previous definition is here
   static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
               ^
>> mm/memcontrol.c:4157:2: error: implicit declaration of function 'mem_cgroup_mst_show_nr_throttled' [-Werror,-Wimplicit-function-declaration]
           mem_cgroup_mst_show_nr_throttled(memcg, m);
           ^
   mm/memcontrol.c:4157:2: note: did you mean 'mem_cgroup_mst_spd_throttle'?
   mm/memcontrol.c:1611:13: note: 'mem_cgroup_mst_spd_throttle' declared here
   static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
               ^

2 errors generated.

vim +/mem_cgroup_mst_show_nr_throttled +4157 mm/memcontrol.c

4155
4156 mem_cgroup_mst_show_mem_spd_max(memcg, m);

> 4157		mem_cgroup_mst_show_nr_throttled(memcg, m);

4158
4159 return 0;
4160 }
4161

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

#10 lore raw

RFC 7/7 mm: introduce mst low and min watermark

In reply to a message not in the archive

From: Yulei Zhang <[email protected]>

Memory allocation speed throttle aims to avoid direct reclaim caused by memory usage burst, which mostly happens under memory pressure. As unconditional throttling could introduce unnecessary overhead, we add two watermarks to control the throttling.

When mst low wmark reached, we start to throttle memory speed if it is overspeed. And if mst min wmark reached, we do throttling directly everytime charging without checking the memory allocation speed.

---
 include/linux/memcontrol.h |  6 ++++++
 mm/memcontrol.c            | 31 ++++++++++++++++++++++++++++++-
 2 files changed, 36 insertions(+), 1 deletion(-)
include/linux/memcontrol.h +6 -0
index 59e6cb78a07a..011d1426a924 100644
@@ -241,6 +241,12 @@ struct mem_spd_ctl {
 	atomic_t updating;
 	atomic_long_t nr_throttled;
 };
+
+enum mst_wmark_stat {
+	WMARK_OK = 0,
+	WMARK_REACH_LOW,
+	WMARK_REACH_MIN,
+};
 #endif
 
 /*
mm/memcontrol.c +30 -1
index ca39974403a3..b1111dc8e585 100644
@@ -1546,18 +1546,47 @@ static int mem_cgroup_mst_overspd_tree(struct mem_cgroup *memcg)
 	return ret;
 }
 
+static enum mst_wmark_stat mem_cgroup_mst_wmark_ok(struct mem_cgroup *memcg)
+{
+	int nid;
+
+	for_each_online_node(nid) {
+		unsigned long free, low, min;
+		struct zone *zone;
+
+		zone = &NODE_DATA(nid)->node_zones[ZONE_NORMAL];
+		free = zone_page_state(zone, NR_FREE_PAGES);
+		low = low_wmark_pages(zone);
+		min = min_wmark_pages(zone);
+		min += (low - min) >> 2;
+
+		if (free <= min)
+			return WMARK_REACH_MIN;
+
+		if (free <= low)
+			return WMARK_REACH_LOW;
+
+	}
+	return WMARK_OK;
+}
+
 static void mem_cgroup_mst_spd_throttle(struct mem_cgroup *memcg)
 {
 	struct mem_spd_ctl *msc = &memcg->msc;
 	long timeout;
 	int ret = 0;
+	enum mst_wmark_stat stat;
 
 	if (!memcg->msc.has_lmt || in_interrupt() || in_atomic() ||
 		irqs_disabled() || oops_in_progress)
 		return;
 
+	stat = mem_cgroup_mst_wmark_ok(memcg);
+	if (stat == WMARK_OK)
+		return;
+
 	ret = mem_cgroup_mst_overspd_tree(memcg);
-	if (!ret)
+	if (stat == WMARK_REACH_LOW && !ret)
 		return;
 
 	atomic_long_inc(&msc->nr_throttled);
Signature · 1 line
2.28.0
#11 lore raw
In reply to [email protected]

Adding linux-mm and related folks.

Quoted 7 lines · On Wed, May 26, 2021 at 9:18 AM <[email protected]…

From: Yulei Zhang <[email protected]>

In this patch set we present the idea to suppress the memory allocation speed in memory cgroup, which aims to avoid direct reclaim caused by memory allocation burst while under memory pressure.

I am assuming here direct reclaim means global reclaim.

Quoted 8 lines

As minimum watermark could be easily broken if certain tasks allocate massive amount of memory in a short period of time, in that case it will trigger the direct memory reclaim and cause unacceptable jitters for latency critical tasks, such as guaranteed pod task in K8s.

With memory allocation speed throttle(mst) mechanism we could lower the memory allocation speed in certian cgroup, usually for low priority tasks, so that could avoid the direct memory reclaim in time.

Can you please explain why memory.high is not good enough for your use-case? You can orchestrate the memory.high limits in such a way that those certain cgroups hit their memory.high limit before causing the global reclaim. You might need to dynamically adjust the limits based on other workloads or unaccounted memory.

Quoted 33 lines

And per-memcg interfaces are introduced under memcg tree, not visiable for root memcg.
- <cgroup_root>/<cgroup_name>/memory.alloc_bps
- 0 -> means memory speed throttle disabled
- non-zero -> value in bytes for memory allocation speed limits

- <cgroup_root>/<cgroup_name>/memory.stat:mst_mem_spd_max
it records the max memory allocation speed of the memory cgroup in the last period of time slice

- <cgroup_root>/<cgroup_name>/memory.stat:mst_nr_throttled it represents the number of times for allocation throttling

Yulei Zhang (7):
mm: record total charge and max speed counter in memcg
mm: introduce alloc_bps to memcg for memory allocation speed throttle
mm: memory allocation speed throttle setup in hierarchy
mm: introduce slice analysis into memory speed throttle mechanism
mm: introduce memory allocation speed throttle
mm: record the numbers of memory allocation throttle
mm: introduce mst low and min watermark

 include/linux/memcontrol.h   |  23 +++
 include/linux/page_counter.h |   8 +
 init/Kconfig                 |   8 +
 mm/memcontrol.c              | 295 +++++++++++++++++++++++++++++++++++
 mm/page_counter.c            |  39 +++++
 5 files changed, 373 insertions(+)

--
2.28.0

#12 lore raw
Quoted 7 lines · Shakeel Butt

Adding linux-mm and related folks.

Quoted 1 line · On Wed, May 26, 2021 at 9:18 AM <[email protected]…
Quoted 9 lines
Quoted 6 lines

From: Yulei Zhang <[email protected]>

In this patch set we present the idea to suppress the memory allocation speed in memory cgroup, which aims to avoid direct reclaim caused by memory allocation burst while under memory pressure.

I am assuming here direct reclaim means global reclaim.

Yep.

Quoted 9 lines
Quoted 8 lines

As minimum watermark could be easily broken if certain tasks allocate massive amount of memory in a short period of time, in that case it will trigger the direct memory reclaim and cause unacceptable jitters for latency critical tasks, such as guaranteed pod task in K8s.

With memory allocation speed throttle(mst) mechanism we could lower the memory allocation speed in certian cgroup, usually for low priority tasks, so that could avoid the direct memory reclaim in time.

Quoted 6 lines

Can you please explain why memory.high is not good enough for your use-case? You can orchestrate the memory.high limits in such a way that those certain cgroups hit their memory.high limit before causing the global reclaim. You might need to dynamically adjust the limits based on other workloads or unaccounted memory.

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,
which may not be suitable in certain usage cases when we want the services alive. Using throttle to suppress the allocation may help keep the activities and
doesn't impact others. Thanks.

Quoted 33 lines
Quoted 33 lines

And per-memcg interfaces are introduced under memcg tree, not visiable for root memcg.
- <cgroup_root>/<cgroup_name>/memory.alloc_bps
- 0 -> means memory speed throttle disabled
- non-zero -> value in bytes for memory allocation speed limits

- <cgroup_root>/<cgroup_name>/memory.stat:mst_mem_spd_max
it records the max memory allocation speed of the memory cgroup in the last period of time slice

- <cgroup_root>/<cgroup_name>/memory.stat:mst_nr_throttled it represents the number of times for allocation throttling

Yulei Zhang (7):
mm: record total charge and max speed counter in memcg
mm: introduce alloc_bps to memcg for memory allocation speed throttle
mm: memory allocation speed throttle setup in hierarchy
mm: introduce slice analysis into memory speed throttle mechanism
mm: introduce memory allocation speed throttle
mm: record the numbers of memory allocation throttle
mm: introduce mst low and min watermark

 include/linux/memcontrol.h   |  23 +++
 include/linux/page_counter.h |   8 +
 init/Kconfig                 |   8 +
 mm/memcontrol.c              | 295 +++++++++++++++++++++++++++++++++++
 mm/page_counter.c            |  39 +++++
 5 files changed, 373 insertions(+)

--
2.28.0

#13 lore raw
Quoted 1 line · yulei zhang

[...]

Quoted 10 lines
Quoted 6 lines

Can you please explain why memory.high is not good enough for your use-case? You can orchestrate the memory.high limits in such a way that those certain cgroups hit their memory.high limit before causing the global reclaim. You might need to dynamically adjust the limits based on other workloads or unaccounted memory.

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,

Can you please elaborate a bit more on this? The memory.high has a strong throttling mechanism, so if you are observing memory.high being ineffective then we need to fix that. Also can you please explain a bit more on the specific of the workload which is able to escape memory.high throttling e.g. the normal number of processes/threads in the workload?

#14 lore raw
In reply to yulei zhang
Quoted 3 lines · yulei zhang

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,

To go further on Shakeel's point, which I agree with, memory.high should _never_ result in memcg OOM. Even if the limit is breached dramatically, we don't OOM the cgroup. If you have a demonstration of memory.high resulting in cgroup-level OOM kills in recent kernels, then that needs to be provided. :-)

#15 lore raw
Quoted 10 lines · Chris Down
Quoted 3 lines · yulei zhang

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,

To go further on Shakeel's point, which I agree with, memory.high should _never_ result in memcg OOM. Even if the limit is breached dramatically, we don't OOM the cgroup. If you have a demonstration of memory.high resulting in cgroup-level OOM kills in recent kernels, then that needs to be provided. :-)

You are right, I mistook it for max. Shakeel means the throttling during context switch
which uses memory.high as threshold to calculate the sleep time. Currently it only applies
to cgroupv2. In this patchset we explore another idea to throttle the memory usage, which
rely on setting an average allocation speed in memcg. We hope to suppress the memory
usage in low priority cgroups when it reaches the system watermark and still keep the activities
alive.

#16 lore raw
Quoted 13 lines · yulei zhang
Quoted 10 lines · Chris Down
Quoted 3 lines · yulei zhang

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,

To go further on Shakeel's point, which I agree with, memory.high should _never_ result in memcg OOM. Even if the limit is breached dramatically, we don't OOM the cgroup. If you have a demonstration of memory.high resulting in cgroup-level OOM kills in recent kernels, then that needs to be provided. :-)

Quoted 11 lines

You are right, I mistook it for max. Shakeel means the throttling during context switch
which uses memory.high as threshold to calculate the sleep time. Currently it only applies
to cgroupv2. In this patchset we explore another idea to throttle the memory usage, which
rely on setting an average allocation speed in memcg. We hope to suppress the memory
usage in low priority cgroups when it reaches the system watermark and still keep the activities
alive.

I think you need to make the case: why should we add one more form of throttling? Basically why memory.high is not good for your use-case and the proposed solution works better. Though IMO it would be a hard sell.

#17 lore raw
Quoted 27 lines · Shakeel Butt
Quoted 13 lines · yulei zhang
Quoted 10 lines · Chris Down
Quoted 3 lines · yulei zhang

Yep, dynamically adjust the memory.high limits can ease the memory pressure and postpone the global reclaim, but it can easily trigger the oom in the cgroups,

To go further on Shakeel's point, which I agree with, memory.high should _never_ result in memcg OOM. Even if the limit is breached dramatically, we don't OOM the cgroup. If you have a demonstration of memory.high resulting in cgroup-level OOM kills in recent kernels, then that needs to be provided. :-)

Quoted 11 lines

You are right, I mistook it for max. Shakeel means the throttling during context switch
which uses memory.high as threshold to calculate the sleep time. Currently it only applies
to cgroupv2. In this patchset we explore another idea to throttle the memory usage, which
rely on setting an average allocation speed in memcg. We hope to suppress the memory
usage in low priority cgroups when it reaches the system watermark and still keep the activities
alive.

Quoted 4 lines

I think you need to make the case: why should we add one more form of throttling? Basically why memory.high is not good for your use-case and the proposed solution works better. Though IMO it would be a hard sell.

Thanks. IMHO, there are differences between these two throttlings. memory.high is a per-memcg throttle which targets to limit the memory usage of the tasks in the cgroup. For the memory allocation speed throttle(MST), the purpose is to avoid the memory burst in cgroup which would trigger the global reclaim and affects the timing sensitive workloads in other cgroup. For example, we have two pods with memory overcommit enabled, one includes online tasks and the other has offline tasks, if we restrict the memory usage of the offline pod with memory.high, it will lose the benefit of memory overcommit when the other workloads are idle. On the other hand, if we don't limit the memory
usage, it will easily break the system watermark when there suddenly has massive memory operations. If enable MST in this case, we will be able to avoid the direct
reclaim and leverage the overcommit.
.

#18 lore raw
Quoted 6 lines · yulei zhang

Thanks. IMHO, there are differences between these two throttlings. memory.high is a per-memcg throttle which targets to limit the memory usage of the tasks in the cgroup. For the memory allocation speed throttle(MST), the purpose is to avoid the memory burst in cgroup which would trigger the global reclaim and affects the timing sensitive workloads in other cgroup. For example, we have two pods with memory overcommit enabled, one includes

Quoted 8 lines

online tasks and the other has offline tasks, if we restrict the memory usage of the offline pod with memory.high, it will lose the benefit of memory overcommit when the other workloads are idle. On the other hand, if we don't limit the memory
usage, it will easily break the system watermark when there suddenly has massive memory operations. If enable MST in this case, we will be able to avoid the direct
reclaim and leverage the overcommit.

Having a speed throttle is a very primitive knob: it's hard to know what the correct values are for a user. That's one of the reasons why we've moved away from that kind of tunable for blkio.

Ultimately, if you want work-conserving behaviour, why not use memory.low?

#19 lore raw
Quoted 17 lines · Chris Down
Quoted 6 lines · yulei zhang

Thanks. IMHO, there are differences between these two throttlings. memory.high is a per-memcg throttle which targets to limit the memory usage of the tasks in the cgroup. For the memory allocation speed throttle(MST), the purpose is to avoid the memory burst in cgroup which would trigger the global reclaim and affects the timing sensitive workloads in other cgroup. For example, we have two pods with memory overcommit enabled, one includes

Quoted 8 lines

online tasks and the other has offline tasks, if we restrict the memory usage of the offline pod with memory.high, it will lose the benefit of memory overcommit when the other workloads are idle. On the other hand, if we don't limit the memory
usage, it will easily break the system watermark when there suddenly has massive memory operations. If enable MST in this case, we will be able to avoid the direct
reclaim and leverage the overcommit.

Quoted 5 lines

Having a speed throttle is a very primitive knob: it's hard to know what the correct values are for a user. That's one of the reasons why we've moved away from that kind of tunable for blkio.

Ultimately, if you want work-conserving behaviour, why not use memory.low?

Thanks. But currently low and high are for cgroup v2 setting, do you think we'd better
extend the same mechanism to cgroup v1?

#20 lore raw
Quoted 9 lines · yulei zhang
Quoted 5 lines

Having a speed throttle is a very primitive knob: it's hard to know what the correct values are for a user. That's one of the reasons why we've moved away from that kind of tunable for blkio.

Ultimately, if you want work-conserving behaviour, why not use memory.low?

Thanks. But currently low and high are for cgroup v2 setting, do you think we'd better
extend the same mechanism to cgroup v1?

The cgroup v1 interface is frozen and in pure maintenance mode -- we're not adding new features there and haven't done so for some time.