Skip to contentSkip to conversation

mm: vmpressure: dynamic window sizing.

RFC New Martijn Coenen 1 participant 1 message Feb 24, 2016

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

RFC 1/1 mm: vmpressure: dynamic window sizing.

The window size used for calculating vm pressure events was previously fixed at 512 pages. The window size has a big impact on the rate of notifications sent off to userspace, in particular when using the "low" level. On machines with a lot of memory, the current value is likely excessive. On the other hand, if the window size is too big, we might delay memory pressure events for too long, especially at critical levels of memory pressure.

This patch attempts to address that problem with two changes.

The first change is to calculate the window size based on the machine size, quite similar to how the vm watermarks are being calculated. This reduces the chance of false positives at any pressure level. Using the machine size only makes sense on the root cgroup though; for non-root cgroups, their hard memory limit is used to calculate the window size. If no hard memory limit is set, we fall back to the default window size that was previously used.

The second change is based on an idea from Johannes Weiner, to only report medium and low pressure levels for every X windows that we scan. This reduces the frequency with which we report low/medium pressure levels, but at the same time will still report critical memory pressure immediately.

Some potential items for discussion are:
- Instead of having a counter for the number of windows
that we encountered a particular pressure level, we could have separate windows per pressure level, each with a different size and its own scanned/reclaimed counters. This averages the windows better, but it made the code quite a bit more complex.
- What to do with non-root cgroups for which no limit is set? In some cases, a parent of that cgroup may have a limit set, but it's still hard to reason about what constitutes pressure in this case. Using the (low) default value seems like a safe option here.
- When setting a new memory limit on a memory cgroup, we only tell vmpressure about the new limit once we have reclaimed enough pages to fit the cgroup in the new limit. It could be argued that the limit should already be updated while we're trying to reclaim memory from that cgroup, since it's that exact reclaim which could be causing pressure on the cgroup.

---
 include/linux/vmpressure.h |  36 +++++++++++--
 mm/memcontrol.c            |  10 ++--
 mm/vmpressure.c            | 122  
+++++++++++++++++++++++++++++++++++----------
 3 files changed, 135 insertions(+), 33 deletions(-)
include/linux/vmpressure.h +31 -3
index 3347cc3..ca9bbf2 100644
@@ -9,13 +9,41 @@
 #include <linux/cgroup.h>
 #include <linux/eventfd.h>
 
+enum vmpressure_levels {
+	VMPRESSURE_LOW = 0,
+	VMPRESSURE_MEDIUM,
+	VMPRESSURE_CRITICAL,
+	VMPRESSURE_NUM_LEVELS,
+};
+
 struct vmpressure {
+	/*
+	 * The window size is the number of scanned pages before
+	 * we try to analyze scanned/reclaimed ratio. Using small window
+	 * sizes can cause lot of false positives, but too big window size will
+	 * delay the notifications.
+	 *
+	 * In order to reduce the amount of false positives for low and medium
+	 * levels, those levels aren't reported until we've seen multipl
+	 * windows at those respective pressure levels. This makes sure
+	 * sure that we don't delay notifications when encountering critical
+	 * levels of memory pressure, but also don't spam userspace in case
+	 * nothing serious is going on. The number of windows seen at each
+	 * pressure level is kept in nr_windows below.
+	 *
+	 * For the root mem cgroup, the window size is computed based on the
+	 * total amount of pages available in the system. For non-root cgroups,
+	 * we compute the window size based on the hard memory limit, or if
+	 * that is not set, we fall back to the default window size.
+	 */
+	unsigned long window_size;
+	/* The number of windows we've seen each pressure level occur for */
+	unsigned int nr_windows[VMPRESSURE_NUM_LEVELS];
 	unsigned long scanned;
 	unsigned long reclaimed;
-
 	unsigned long tree_scanned;
 	unsigned long tree_reclaimed;
-	/* The lock is used to keep the scanned/reclaimed above in sync. */
+	/* The lock is used to keep the members above in sync. */
 	struct spinlock sr_lock;
 
 	/* The list of vmpressure_event structs. */
@@ -33,10 +61,12 @@ extern void vmpressure(gfp_t gfp, struct mem_cgroup *memcg, bool tree,
 		       unsigned long scanned, unsigned long reclaimed);
 extern void vmpressure_prio(gfp_t gfp, struct mem_cgroup *memcg, int prio);
 
-extern void vmpressure_init(struct vmpressure *vmpr);
+extern void vmpressure_init(struct vmpressure *vmpr, bool is_root);
 extern void vmpressure_cleanup(struct vmpressure *vmpr);
 extern struct vmpressure *memcg_to_vmpressure(struct mem_cgroup *memcg);
 extern struct cgroup_subsys_state *vmpressure_to_css(struct vmpressure  

*vmpr);
+extern void vmpressure_update_mem_limit(struct mem_cgroup *memcg,
+ unsigned long new_limit);
extern int vmpressure_register_event(struct mem_cgroup *memcg,
struct eventfd_ctx *eventfd, const char *args);

mm/memcontrol.c +6 -4
index d06cae2..ffc3ba6 100644
@@ -2516,8 +2516,10 @@ static int mem_cgroup_resize_limit(struct mem_cgroup *memcg,
 		ret = page_counter_limit(&memcg->memory, limit);
 		mutex_unlock(&memcg_limit_mutex);
 
-		if (!ret)
+		if (!ret) {
+			vmpressure_update_mem_limit(memcg, limit);
 			break;
+		}
 
 		try_to_free_mem_cgroup_pages(memcg, 1, GFP_KERNEL, true);
 
@@ -4144,7 +4146,7 @@ static void mem_cgroup_free(struct mem_cgroup *memcg)
 	kfree(memcg);
 }
 
-static struct mem_cgroup *mem_cgroup_alloc(void)
+static struct mem_cgroup *mem_cgroup_alloc(bool is_root)
 {
 	struct mem_cgroup *memcg;
 	size_t size;
@@ -4173,7 +4175,7 @@ static struct mem_cgroup *mem_cgroup_alloc(void)
 	INIT_LIST_HEAD(&memcg->oom_notify);
 	mutex_init(&memcg->thresholds_lock);
 	spin_lock_init(&memcg->move_lock);
-	vmpressure_init(&memcg->vmpressure);
+	vmpressure_init(&memcg->vmpressure, is_root);
 	INIT_LIST_HEAD(&memcg->event_list);
 	spin_lock_init(&memcg->event_list_lock);
 	memcg->socket_pressure = jiffies;
@@ -4196,7 +4198,7 @@ mem_cgroup_css_alloc(struct cgroup_subsys_state *parent_css)
 	struct mem_cgroup *memcg;
 	long error = -ENOMEM;
 
-	memcg = mem_cgroup_alloc();
+	memcg = mem_cgroup_alloc(!parent);
 	if (!memcg)
 		return ERR_PTR(error);
 
mm/vmpressure.c +96 -26

201 lines not on this page · raw message

Signature · 7 lines
2.7.0.rc3.207.g0ac5344

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