|
@@ -36,6 +36,7 @@
|
|
|
#include <linux/hugetlb.h>
|
|
|
#include <linux/hugetlb_cgroup.h>
|
|
|
#include <linux/gfp.h>
|
|
|
+#include <linux/memremap.h>
|
|
|
#include <linux/balloon_compaction.h>
|
|
|
#include <linux/mmu_notifier.h>
|
|
|
#include <linux/page_idle.h>
|
|
@@ -237,7 +238,13 @@ static bool remove_migration_pte(struct page *page, struct vm_area_struct *vma,
|
|
|
if (is_write_migration_entry(entry))
|
|
|
pte = maybe_mkwrite(pte, vma);
|
|
|
|
|
|
- flush_dcache_page(new);
|
|
|
+ if (unlikely(is_zone_device_page(new)) &&
|
|
|
+ is_device_private_page(new)) {
|
|
|
+ entry = make_device_private_entry(new, pte_write(pte));
|
|
|
+ pte = swp_entry_to_pte(entry);
|
|
|
+ } else
|
|
|
+ flush_dcache_page(new);
|
|
|
+
|
|
|
#ifdef CONFIG_HUGETLB_PAGE
|
|
|
if (PageHuge(new)) {
|
|
|
pte = pte_mkhuge(pte);
|
|
@@ -2205,17 +2212,40 @@ again:
|
|
|
pte = *ptep;
|
|
|
pfn = pte_pfn(pte);
|
|
|
|
|
|
- if (!pte_present(pte)) {
|
|
|
+ if (pte_none(pte)) {
|
|
|
mpfn = pfn = 0;
|
|
|
goto next;
|
|
|
}
|
|
|
|
|
|
+ if (!pte_present(pte)) {
|
|
|
+ mpfn = pfn = 0;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Only care about unaddressable device page special
|
|
|
+ * page table entry. Other special swap entries are not
|
|
|
+ * migratable, and we ignore regular swapped page.
|
|
|
+ */
|
|
|
+ entry = pte_to_swp_entry(pte);
|
|
|
+ if (!is_device_private_entry(entry))
|
|
|
+ goto next;
|
|
|
+
|
|
|
+ page = device_private_entry_to_page(entry);
|
|
|
+ mpfn = migrate_pfn(page_to_pfn(page))|
|
|
|
+ MIGRATE_PFN_DEVICE | MIGRATE_PFN_MIGRATE;
|
|
|
+ if (is_write_device_private_entry(entry))
|
|
|
+ mpfn |= MIGRATE_PFN_WRITE;
|
|
|
+ } else {
|
|
|
+ page = vm_normal_page(migrate->vma, addr, pte);
|
|
|
+ mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
|
|
|
+ mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0;
|
|
|
+ }
|
|
|
+
|
|
|
/* FIXME support THP */
|
|
|
- page = vm_normal_page(migrate->vma, addr, pte);
|
|
|
if (!page || !page->mapping || PageTransCompound(page)) {
|
|
|
mpfn = pfn = 0;
|
|
|
goto next;
|
|
|
}
|
|
|
+ pfn = page_to_pfn(page);
|
|
|
|
|
|
/*
|
|
|
* By getting a reference on the page we pin it and that blocks
|
|
@@ -2228,8 +2258,6 @@ again:
|
|
|
*/
|
|
|
get_page(page);
|
|
|
migrate->cpages++;
|
|
|
- mpfn = migrate_pfn(pfn) | MIGRATE_PFN_MIGRATE;
|
|
|
- mpfn |= pte_write(pte) ? MIGRATE_PFN_WRITE : 0;
|
|
|
|
|
|
/*
|
|
|
* Optimize for the common case where page is only mapped once
|
|
@@ -2256,10 +2284,13 @@ again:
|
|
|
*/
|
|
|
page_remove_rmap(page, false);
|
|
|
put_page(page);
|
|
|
- unmapped++;
|
|
|
+
|
|
|
+ if (pte_present(pte))
|
|
|
+ unmapped++;
|
|
|
}
|
|
|
|
|
|
next:
|
|
|
+ migrate->dst[migrate->npages] = 0;
|
|
|
migrate->src[migrate->npages++] = mpfn;
|
|
|
}
|
|
|
arch_leave_lazy_mmu_mode();
|
|
@@ -2329,6 +2360,28 @@ static bool migrate_vma_check_page(struct page *page)
|
|
|
if (PageCompound(page))
|
|
|
return false;
|
|
|
|
|
|
+ /* Page from ZONE_DEVICE have one extra reference */
|
|
|
+ if (is_zone_device_page(page)) {
|
|
|
+ /*
|
|
|
+ * Private page can never be pin as they have no valid pte and
|
|
|
+ * GUP will fail for those. Yet if there is a pending migration
|
|
|
+ * a thread might try to wait on the pte migration entry and
|
|
|
+ * will bump the page reference count. Sadly there is no way to
|
|
|
+ * differentiate a regular pin from migration wait. Hence to
|
|
|
+ * avoid 2 racing thread trying to migrate back to CPU to enter
|
|
|
+ * infinite loop (one stoping migration because the other is
|
|
|
+ * waiting on pte migration entry). We always return true here.
|
|
|
+ *
|
|
|
+ * FIXME proper solution is to rework migration_entry_wait() so
|
|
|
+ * it does not need to take a reference on page.
|
|
|
+ */
|
|
|
+ if (is_device_private_page(page))
|
|
|
+ return true;
|
|
|
+
|
|
|
+ /* Other ZONE_DEVICE memory type are not supported */
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
if ((page_count(page) - extra) > page_mapcount(page))
|
|
|
return false;
|
|
|
|
|
@@ -2379,24 +2432,30 @@ static void migrate_vma_prepare(struct migrate_vma *migrate)
|
|
|
migrate->src[i] |= MIGRATE_PFN_LOCKED;
|
|
|
}
|
|
|
|
|
|
- if (!PageLRU(page) && allow_drain) {
|
|
|
- /* Drain CPU's pagevec */
|
|
|
- lru_add_drain_all();
|
|
|
- allow_drain = false;
|
|
|
- }
|
|
|
+ /* ZONE_DEVICE pages are not on LRU */
|
|
|
+ if (!is_zone_device_page(page)) {
|
|
|
+ if (!PageLRU(page) && allow_drain) {
|
|
|
+ /* Drain CPU's pagevec */
|
|
|
+ lru_add_drain_all();
|
|
|
+ allow_drain = false;
|
|
|
+ }
|
|
|
|
|
|
- if (isolate_lru_page(page)) {
|
|
|
- if (remap) {
|
|
|
- migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
|
|
|
- migrate->cpages--;
|
|
|
- restore++;
|
|
|
- } else {
|
|
|
- migrate->src[i] = 0;
|
|
|
- unlock_page(page);
|
|
|
- migrate->cpages--;
|
|
|
- put_page(page);
|
|
|
+ if (isolate_lru_page(page)) {
|
|
|
+ if (remap) {
|
|
|
+ migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
|
|
|
+ migrate->cpages--;
|
|
|
+ restore++;
|
|
|
+ } else {
|
|
|
+ migrate->src[i] = 0;
|
|
|
+ unlock_page(page);
|
|
|
+ migrate->cpages--;
|
|
|
+ put_page(page);
|
|
|
+ }
|
|
|
+ continue;
|
|
|
}
|
|
|
- continue;
|
|
|
+
|
|
|
+ /* Drop the reference we took in collect */
|
|
|
+ put_page(page);
|
|
|
}
|
|
|
|
|
|
if (!migrate_vma_check_page(page)) {
|
|
@@ -2405,14 +2464,19 @@ static void migrate_vma_prepare(struct migrate_vma *migrate)
|
|
|
migrate->cpages--;
|
|
|
restore++;
|
|
|
|
|
|
- get_page(page);
|
|
|
- putback_lru_page(page);
|
|
|
+ if (!is_zone_device_page(page)) {
|
|
|
+ get_page(page);
|
|
|
+ putback_lru_page(page);
|
|
|
+ }
|
|
|
} else {
|
|
|
migrate->src[i] = 0;
|
|
|
unlock_page(page);
|
|
|
migrate->cpages--;
|
|
|
|
|
|
- putback_lru_page(page);
|
|
|
+ if (!is_zone_device_page(page))
|
|
|
+ putback_lru_page(page);
|
|
|
+ else
|
|
|
+ put_page(page);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -2483,7 +2547,10 @@ restore:
|
|
|
unlock_page(page);
|
|
|
restore--;
|
|
|
|
|
|
- putback_lru_page(page);
|
|
|
+ if (is_zone_device_page(page))
|
|
|
+ put_page(page);
|
|
|
+ else
|
|
|
+ putback_lru_page(page);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2514,6 +2581,26 @@ static void migrate_vma_pages(struct migrate_vma *migrate)
|
|
|
|
|
|
mapping = page_mapping(page);
|
|
|
|
|
|
+ if (is_zone_device_page(newpage)) {
|
|
|
+ if (is_device_private_page(newpage)) {
|
|
|
+ /*
|
|
|
+ * For now only support private anonymous when
|
|
|
+ * migrating to un-addressable device memory.
|
|
|
+ */
|
|
|
+ if (mapping) {
|
|
|
+ migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ /*
|
|
|
+ * Other types of ZONE_DEVICE page are not
|
|
|
+ * supported.
|
|
|
+ */
|
|
|
+ migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
r = migrate_page(mapping, newpage, page, MIGRATE_SYNC_NO_COPY);
|
|
|
if (r != MIGRATEPAGE_SUCCESS)
|
|
|
migrate->src[i] &= ~MIGRATE_PFN_MIGRATE;
|
|
@@ -2554,11 +2641,17 @@ static void migrate_vma_finalize(struct migrate_vma *migrate)
|
|
|
unlock_page(page);
|
|
|
migrate->cpages--;
|
|
|
|
|
|
- putback_lru_page(page);
|
|
|
+ if (is_zone_device_page(page))
|
|
|
+ put_page(page);
|
|
|
+ else
|
|
|
+ putback_lru_page(page);
|
|
|
|
|
|
if (newpage != page) {
|
|
|
unlock_page(newpage);
|
|
|
- putback_lru_page(newpage);
|
|
|
+ if (is_zone_device_page(newpage))
|
|
|
+ put_page(newpage);
|
|
|
+ else
|
|
|
+ putback_lru_page(newpage);
|
|
|
}
|
|
|
}
|
|
|
}
|