[PATCH v5 5/6] page_idle: Drain all LRU pagevec before idle tracking

From: Joel Fernandes (Google)
Date: Wed Aug 07 2019 - 13:16:28 EST


During idle page tracking, we see that sometimes faulted anon pages are in
pagevec but are not drained to LRU. Idle page tracking only considers pages
on LRU.

I am able to find multiple issues involving this. One issue looks like
idle tracking is completely broken. It shows up in my testing as if a
page that is marked as idle is always "accessed" -- because it was never
marked as idle (due to not draining of pagevec).

The other issue shows up as a failure during swapping (support for which
this series adds), with the following sequence:
1. Allocate some pages
2. Write to them
3. Mark them as idle <--- fails
4. Introduce some memory pressure to induce swapping.
5. Check the swap bit I introduced in this series. <--- fails to set idle
bit in swap PTE.

To fix this, this patch drains all CPU's pagevec before starting idle tracking.

Signed-off-by: Joel Fernandes (Google) <joel@xxxxxxxxxxxxxxxxx>
---
mm/page_idle.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)

diff --git a/mm/page_idle.c b/mm/page_idle.c
index 2766d4ab348c..26440a497609 100644
--- a/mm/page_idle.c
+++ b/mm/page_idle.c
@@ -180,6 +180,13 @@ static ssize_t page_idle_bitmap_read(struct file *file, struct kobject *kobj,
unsigned long pfn, end_pfn;
int bit, ret;

+ /*
+ * Idle page tracking currently works only on LRU pages, so drain
+ * them. This can cause slowness, but in the future we could
+ * remove this operation if we are tracking non-LRU pages too.
+ */
+ lru_add_drain_all();
+
ret = page_idle_get_frames(pos, count, NULL, &pfn, &end_pfn);
if (ret == -ENXIO)
return 0; /* Reads beyond max_pfn do nothing */
@@ -211,6 +218,13 @@ static ssize_t page_idle_bitmap_write(struct file *file, struct kobject *kobj,
unsigned long pfn, end_pfn;
int bit, ret;

+ /*
+ * Idle page tracking currently works only on LRU pages, so drain
+ * them. This can cause slowness, but in the future we could
+ * remove this operation if we are tracking non-LRU pages too.
+ */
+ lru_add_drain_all();
+
ret = page_idle_get_frames(pos, count, NULL, &pfn, &end_pfn);
if (ret)
return ret;
@@ -428,6 +442,13 @@ ssize_t page_idle_proc_generic(struct file *file, char __user *ubuff,
walk.private = &priv;
walk.mm = mm;

+ /*
+ * Idle page tracking currently works only on LRU pages, so drain
+ * them. This can cause slowness, but in the future we could
+ * remove this operation if we are tracking non-LRU pages too.
+ */
+ lru_add_drain_all();
+
down_read(&mm->mmap_sem);

/*
--
2.22.0.770.g0f2c4a37fd-goog