|
@@ -485,7 +485,8 @@ static int fallback_migrate_page(struct address_space *mapping,
|
|
|
* < 0 - error code
|
|
|
* == 0 - success
|
|
|
*/
|
|
|
-static int move_to_new_page(struct page *newpage, struct page *page)
|
|
|
+static int move_to_new_page(struct page *newpage, struct page *page,
|
|
|
+ int remap_swapcache)
|
|
|
{
|
|
|
struct address_space *mapping;
|
|
|
int rc;
|
|
@@ -520,10 +521,12 @@ static int move_to_new_page(struct page *newpage, struct page *page)
|
|
|
else
|
|
|
rc = fallback_migrate_page(mapping, newpage, page);
|
|
|
|
|
|
- if (!rc)
|
|
|
- remove_migration_ptes(page, newpage);
|
|
|
- else
|
|
|
+ if (rc) {
|
|
|
newpage->mapping = NULL;
|
|
|
+ } else {
|
|
|
+ if (remap_swapcache)
|
|
|
+ remove_migration_ptes(page, newpage);
|
|
|
+ }
|
|
|
|
|
|
unlock_page(newpage);
|
|
|
|
|
@@ -540,6 +543,7 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
int rc = 0;
|
|
|
int *result = NULL;
|
|
|
struct page *newpage = get_new_page(page, private, &result);
|
|
|
+ int remap_swapcache = 1;
|
|
|
int rcu_locked = 0;
|
|
|
int charge = 0;
|
|
|
struct mem_cgroup *mem = NULL;
|
|
@@ -601,18 +605,33 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
rcu_read_lock();
|
|
|
rcu_locked = 1;
|
|
|
|
|
|
- /*
|
|
|
- * If the page has no mappings any more, just bail. An
|
|
|
- * unmapped anon page is likely to be freed soon but worse,
|
|
|
- * it's possible its anon_vma disappeared between when
|
|
|
- * the page was isolated and when we reached here while
|
|
|
- * the RCU lock was not held
|
|
|
- */
|
|
|
- if (!page_mapped(page))
|
|
|
- goto rcu_unlock;
|
|
|
+ /* Determine how to safely use anon_vma */
|
|
|
+ if (!page_mapped(page)) {
|
|
|
+ if (!PageSwapCache(page))
|
|
|
+ goto rcu_unlock;
|
|
|
|
|
|
- anon_vma = page_anon_vma(page);
|
|
|
- atomic_inc(&anon_vma->external_refcount);
|
|
|
+ /*
|
|
|
+ * We cannot be sure that the anon_vma of an unmapped
|
|
|
+ * swapcache page is safe to use because we don't
|
|
|
+ * know in advance if the VMA that this page belonged
|
|
|
+ * to still exists. If the VMA and others sharing the
|
|
|
+ * data have been freed, then the anon_vma could
|
|
|
+ * already be invalid.
|
|
|
+ *
|
|
|
+ * To avoid this possibility, swapcache pages get
|
|
|
+ * migrated but are not remapped when migration
|
|
|
+ * completes
|
|
|
+ */
|
|
|
+ remap_swapcache = 0;
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Take a reference count on the anon_vma if the
|
|
|
+ * page is mapped so that it is guaranteed to
|
|
|
+ * exist when the page is remapped later
|
|
|
+ */
|
|
|
+ anon_vma = page_anon_vma(page);
|
|
|
+ atomic_inc(&anon_vma->external_refcount);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/*
|
|
@@ -647,9 +666,9 @@ static int unmap_and_move(new_page_t get_new_page, unsigned long private,
|
|
|
|
|
|
skip_unmap:
|
|
|
if (!page_mapped(page))
|
|
|
- rc = move_to_new_page(newpage, page);
|
|
|
+ rc = move_to_new_page(newpage, page, remap_swapcache);
|
|
|
|
|
|
- if (rc)
|
|
|
+ if (rc && remap_swapcache)
|
|
|
remove_migration_ptes(page, page);
|
|
|
rcu_unlock:
|
|
|
|