return pte_flags(a) & (_PAGE_PRESENT | _PAGE_PROTNONE);
 }
 
+#ifdef __HAVE_ARCH_PTE_DEVMAP
+static inline int pte_devmap(pte_t a)
+{
+       return (pte_flags(a) & _PAGE_DEVMAP) == _PAGE_DEVMAP;
+}
+#endif
+
 #define pte_accessible pte_accessible
 static inline bool pte_accessible(struct mm_struct *mm, pte_t a)
 {
 
 #include <linux/vmstat.h>
 #include <linux/highmem.h>
 #include <linux/swap.h>
+#include <linux/memremap.h>
 
 #include <asm/pgtable.h>
 
 #endif
 }
 
+static void undo_dev_pagemap(int *nr, int nr_start, struct page **pages)
+{
+       while ((*nr) - nr_start) {
+               struct page *page = pages[--(*nr)];
+
+               ClearPageReferenced(page);
+               put_page(page);
+       }
+}
+
 /*
  * The performance critical leaf functions are made noinline otherwise gcc
  * inlines everything into a single function which results in too much
 static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
                unsigned long end, int write, struct page **pages, int *nr)
 {
+       struct dev_pagemap *pgmap = NULL;
        unsigned long mask;
+       int nr_start = *nr;
        pte_t *ptep;
 
        mask = _PAGE_PRESENT|_PAGE_USER;
                        return 0;
                }
 
-               if ((pte_flags(pte) & (mask | _PAGE_SPECIAL)) != mask) {
+               page = pte_page(pte);
+               if (pte_devmap(pte)) {
+                       pgmap = get_dev_pagemap(pte_pfn(pte), pgmap);
+                       if (unlikely(!pgmap)) {
+                               undo_dev_pagemap(nr, nr_start, pages);
+                               pte_unmap(ptep);
+                               return 0;
+                       }
+               } else if ((pte_flags(pte) & (mask | _PAGE_SPECIAL)) != mask) {
                        pte_unmap(ptep);
                        return 0;
                }
                VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
-               page = pte_page(pte);
                get_page(page);
+               put_dev_pagemap(pgmap);
                SetPageReferenced(page);
                pages[*nr] = page;
                (*nr)++;
        SetPageReferenced(page);
 }
 
+static int __gup_device_huge_pmd(pmd_t pmd, unsigned long addr,
+               unsigned long end, struct page **pages, int *nr)
+{
+       int nr_start = *nr;
+       unsigned long pfn = pmd_pfn(pmd);
+       struct dev_pagemap *pgmap = NULL;
+
+       pfn += (addr & ~PMD_MASK) >> PAGE_SHIFT;
+       do {
+               struct page *page = pfn_to_page(pfn);
+
+               pgmap = get_dev_pagemap(pfn, pgmap);
+               if (unlikely(!pgmap)) {
+                       undo_dev_pagemap(nr, nr_start, pages);
+                       return 0;
+               }
+               SetPageReferenced(page);
+               pages[*nr] = page;
+               get_page(page);
+               put_dev_pagemap(pgmap);
+               (*nr)++;
+               pfn++;
+       } while (addr += PAGE_SIZE, addr != end);
+       return 1;
+}
+
 static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
                unsigned long end, int write, struct page **pages, int *nr)
 {
                mask |= _PAGE_RW;
        if ((pmd_flags(pmd) & mask) != mask)
                return 0;
+
+       VM_BUG_ON(!pfn_valid(pmd_pfn(pmd)));
+       if (pmd_devmap(pmd))
+               return __gup_device_huge_pmd(pmd, addr, end, pages, nr);
+
        /* hugepages are never "special" */
        VM_BUG_ON(pmd_flags(pmd) & _PAGE_SPECIAL);
-       VM_BUG_ON(!pfn_valid(pmd_pfn(pmd)));
 
        refs = 0;
        head = pmd_page(pmd);
 
                        int prot_numa);
 int vmf_insert_pfn_pmd(struct vm_area_struct *, unsigned long addr, pmd_t *,
                        pfn_t pfn, bool write);
-
 enum transparent_hugepage_flag {
        TRANSPARENT_HUGEPAGE_FLAG,
        TRANSPARENT_HUGEPAGE_REQ_MADV_FLAG,
 #define HPAGE_PMD_NR (1<<HPAGE_PMD_ORDER)
 
 #ifdef CONFIG_TRANSPARENT_HUGEPAGE
+struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
+               pmd_t *pmd, int flags);
+
 #define HPAGE_PMD_SHIFT PMD_SHIFT
 #define HPAGE_PMD_SIZE ((1UL) << HPAGE_PMD_SHIFT)
 #define HPAGE_PMD_MASK (~(HPAGE_PMD_SIZE - 1))
        return false;
 }
 
+
+static inline struct page *follow_devmap_pmd(struct vm_area_struct *vma,
+               unsigned long addr, pmd_t *pmd, int flags)
+{
+       return NULL;
+}
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
 #endif /* _LINUX_HUGE_MM_H */
 
 #include <linux/mm_types.h>
 #include <linux/range.h>
 #include <linux/pfn.h>
+#include <linux/percpu-refcount.h>
 #include <linux/bit_spinlock.h>
 #include <linux/shrinker.h>
 #include <linux/resource.h>
        return atomic_read(&compound_head(page)->_count);
 }
 
-static inline void get_page(struct page *page)
-{
-       page = compound_head(page);
-       /*
-        * Getting a normal page or the head of a compound page
-        * requires to already have an elevated page->_count.
-        */
-       VM_BUG_ON_PAGE(atomic_read(&page->_count) <= 0, page);
-       atomic_inc(&page->_count);
-}
-
 static inline struct page *virt_to_head_page(const void *x)
 {
        struct page *page = virt_to_page(x);
 
 void __put_page(struct page *page);
 
-static inline void put_page(struct page *page)
-{
-       page = compound_head(page);
-       if (put_page_testzero(page))
-               __put_page(page);
-}
-
 void put_pages_list(struct list_head *pages);
 
 void split_page(struct page *page, unsigned int order);
 }
 
 #ifdef CONFIG_ZONE_DEVICE
+void get_zone_device_page(struct page *page);
+void put_zone_device_page(struct page *page);
 static inline bool is_zone_device_page(const struct page *page)
 {
        return page_zonenum(page) == ZONE_DEVICE;
 }
 #else
+static inline void get_zone_device_page(struct page *page)
+{
+}
+static inline void put_zone_device_page(struct page *page)
+{
+}
 static inline bool is_zone_device_page(const struct page *page)
 {
        return false;
 }
 #endif
 
+static inline void get_page(struct page *page)
+{
+       page = compound_head(page);
+       /*
+        * Getting a normal page or the head of a compound page
+        * requires to already have an elevated page->_count.
+        */
+       VM_BUG_ON_PAGE(atomic_read(&page->_count) <= 0, page);
+       atomic_inc(&page->_count);
+
+       if (unlikely(is_zone_device_page(page)))
+               get_zone_device_page(page);
+}
+
+static inline void put_page(struct page *page)
+{
+       page = compound_head(page);
+
+       if (put_page_testzero(page))
+               __put_page(page);
+
+       if (unlikely(is_zone_device_page(page)))
+               put_zone_device_page(page);
+}
+
 #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP)
 #define SECTION_IN_PAGE_FLAGS
 #endif
 }
 #endif
 
+#ifndef __HAVE_ARCH_PTE_DEVMAP
+static inline int pte_devmap(pte_t pte)
+{
+       return 0;
+}
+#endif
+
 int vma_wants_writenotify(struct vm_area_struct *vma);
 
 extern pte_t *__get_locked_pte(struct mm_struct *mm, unsigned long addr,
 
        struct vmem_altmap altmap;
 };
 
+void get_zone_device_page(struct page *page)
+{
+       percpu_ref_get(page->pgmap->ref);
+}
+EXPORT_SYMBOL(get_zone_device_page);
+
+void put_zone_device_page(struct page *page)
+{
+       put_dev_pagemap(page->pgmap);
+}
+EXPORT_SYMBOL(put_zone_device_page);
+
 static void pgmap_radix_release(struct resource *res)
 {
        resource_size_t key;
 
 #include <linux/spinlock.h>
 
 #include <linux/mm.h>
+#include <linux/memremap.h>
 #include <linux/pagemap.h>
 #include <linux/rmap.h>
 #include <linux/swap.h>
                unsigned long address, pmd_t *pmd, unsigned int flags)
 {
        struct mm_struct *mm = vma->vm_mm;
+       struct dev_pagemap *pgmap = NULL;
        struct page *page;
        spinlock_t *ptl;
        pte_t *ptep, pte;
        }
 
        page = vm_normal_page(vma, address, pte);
-       if (unlikely(!page)) {
+       if (!page && pte_devmap(pte) && (flags & FOLL_GET)) {
+               /*
+                * Only return device mapping pages in the FOLL_GET case since
+                * they are only valid while holding the pgmap reference.
+                */
+               pgmap = get_dev_pagemap(pte_pfn(pte), NULL);
+               if (pgmap)
+                       page = pte_page(pte);
+               else
+                       goto no_page;
+       } else if (unlikely(!page)) {
                if (flags & FOLL_DUMP) {
                        /* Avoid special (like zero) pages in core dumps */
                        page = ERR_PTR(-EFAULT);
                goto retry;
        }
 
-       if (flags & FOLL_GET)
+       if (flags & FOLL_GET) {
                get_page(page);
+
+               /* drop the pgmap reference now that we hold the page */
+               if (pgmap) {
+                       put_dev_pagemap(pgmap);
+                       pgmap = NULL;
+               }
+       }
        if (flags & FOLL_TOUCH) {
                if ((flags & FOLL_WRITE) &&
                    !pte_dirty(pte) && !PageDirty(page))
        }
        if ((flags & FOLL_NUMA) && pmd_protnone(*pmd))
                return no_page_table(vma, flags);
+       if (pmd_devmap(*pmd)) {
+               ptl = pmd_lock(mm, pmd);
+               page = follow_devmap_pmd(vma, address, pmd, flags);
+               spin_unlock(ptl);
+               if (page)
+                       return page;
+       }
        if (likely(!pmd_trans_huge(*pmd)))
                return follow_page_pte(vma, address, pmd, flags);
 
 
 #include <linux/freezer.h>
 #include <linux/pfn_t.h>
 #include <linux/mman.h>
+#include <linux/memremap.h>
 #include <linux/pagemap.h>
 #include <linux/debugfs.h>
 #include <linux/migrate.h>
        return VM_FAULT_NOPAGE;
 }
 
+static void touch_pmd(struct vm_area_struct *vma, unsigned long addr,
+               pmd_t *pmd)
+{
+       pmd_t _pmd;
+
+       /*
+        * We should set the dirty bit only for FOLL_WRITE but for now
+        * the dirty bit in the pmd is meaningless.  And if the dirty
+        * bit will become meaningful and we'll only set it with
+        * FOLL_WRITE, an atomic set_bit will be required on the pmd to
+        * set the young bit, instead of the current set_pmd_at.
+        */
+       _pmd = pmd_mkyoung(pmd_mkdirty(*pmd));
+       if (pmdp_set_access_flags(vma, addr & HPAGE_PMD_MASK,
+                               pmd, _pmd,  1))
+               update_mmu_cache_pmd(vma, addr, pmd);
+}
+
+struct page *follow_devmap_pmd(struct vm_area_struct *vma, unsigned long addr,
+               pmd_t *pmd, int flags)
+{
+       unsigned long pfn = pmd_pfn(*pmd);
+       struct mm_struct *mm = vma->vm_mm;
+       struct dev_pagemap *pgmap;
+       struct page *page;
+
+       assert_spin_locked(pmd_lockptr(mm, pmd));
+
+       if (flags & FOLL_WRITE && !pmd_write(*pmd))
+               return NULL;
+
+       if (pmd_present(*pmd) && pmd_devmap(*pmd))
+               /* pass */;
+       else
+               return NULL;
+
+       if (flags & FOLL_TOUCH)
+               touch_pmd(vma, addr, pmd);
+
+       /*
+        * device mapped pages can only be returned if the
+        * caller will manage the page reference count.
+        */
+       if (!(flags & FOLL_GET))
+               return ERR_PTR(-EEXIST);
+
+       pfn += (addr & ~PMD_MASK) >> PAGE_SHIFT;
+       pgmap = get_dev_pagemap(pfn, NULL);
+       if (!pgmap)
+               return ERR_PTR(-EFAULT);
+       page = pfn_to_page(pfn);
+       get_page(page);
+       put_dev_pagemap(pgmap);
+
+       return page;
+}
+
 int copy_huge_pmd(struct mm_struct *dst_mm, struct mm_struct *src_mm,
                  pmd_t *dst_pmd, pmd_t *src_pmd, unsigned long addr,
                  struct vm_area_struct *vma)
 
        page = pmd_page(*pmd);
        VM_BUG_ON_PAGE(!PageHead(page), page);
-       if (flags & FOLL_TOUCH) {
-               pmd_t _pmd;
-               /*
-                * We should set the dirty bit only for FOLL_WRITE but
-                * for now the dirty bit in the pmd is meaningless.
-                * And if the dirty bit will become meaningful and
-                * we'll only set it with FOLL_WRITE, an atomic
-                * set_bit will be required on the pmd to set the
-                * young bit, instead of the current set_pmd_at.
-                */
-               _pmd = pmd_mkyoung(pmd_mkdirty(*pmd));
-               if (pmdp_set_access_flags(vma, addr & HPAGE_PMD_MASK,
-                                         pmd, _pmd,  1))
-                       update_mmu_cache_pmd(vma, addr, pmd);
-       }
+       if (flags & FOLL_TOUCH)
+               touch_pmd(vma, addr, pmd);
        if ((flags & FOLL_MLOCK) && (vma->vm_flags & VM_LOCKED)) {
                /*
                 * We don't mlock() pte-mapped THPs. This way we can avoid
 
 #include <linux/export.h>
 #include <linux/mm_inline.h>
 #include <linux/percpu_counter.h>
+#include <linux/memremap.h>
 #include <linux/percpu.h>
 #include <linux/cpu.h>
 #include <linux/notifier.h>