summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJérôme Glisse <jglisse@redhat.com>2018-09-28 13:59:22 -0400
committerJérôme Glisse <jglisse@redhat.com>2018-09-28 13:59:22 -0400
commita5dbc0fe7e71d347067579f13579df372ec48389 (patch)
treecf5e66b84e85d21de9471dbf88bd4365a1a917b1
parentae596de1a0c8c2c924dc99d23c026259372ab234 (diff)
mm/vmscan: do not try to reclaim page pinned by a GUP (get_user_page)
Page pinned by a get user as we won't be able to free them. This avoids doing all the heavy work: scanning page mapping reference status, trying to unmap the page, write it to disk (if file back and dirty or swap), trying to release page (if file back) ... Signed-off-by: Jérôme Glisse <jglisse@redhat.com>
-rw-r--r--mm/vmscan.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 7e7d25504651..a4e4a4725afc 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -4138,15 +4138,32 @@ int node_reclaim(struct pglist_data *pgdat, gfp_t gfp_mask, unsigned int order)
* Reasons page might not be evictable:
* (1) page's mapping marked unevictable
* (2) page is part of an mlocked VMA
- *
+ * (3) page is pinned by some GUP (get_user_pages())
*/
int page_evictable(struct page *page)
{
- int ret;
+ struct address_space *mapping = page_mapping(page);
+ int ret, extra;
+
+ /*
+ * If page has a mapping then it has an extra refcount and can another
+ * extra one if it has its private fields set.
+ */
+ extra = mapping ? 1 + page_has_private(page) : 0;
+ /* If page is isolated from lru then it has an extra refcount. */
+ extra += PageLRU(page) ? 0 : 1;
+ /*
+ * GUP (get_user_pages()) will increment the refcount on the page so at
+ * this point if the refcount is bigger than mapcount it means that the
+ * page is pinned by some GUP and it is pointless to try to reclaim any
+ * such page. Ignore any extra reference that are not from a GUP.
+ */
+ if ((page_count(page) - extra) > page_mapcount(page))
+ return 0;
/* Prevent address_space of inode and swap cache from being freed */
rcu_read_lock();
- ret = !mapping_unevictable(page_mapping(page)) && !PageMlocked(page);
+ ret = !mapping_unevictable(mapping) && !PageMlocked(page);
rcu_read_unlock();
return ret;
}