Merge tag 'soc-fsl-fix-v5.1' of git://git.kernel.org/pub/scm/linux/kernel/git/leo...
[linux-2.6-microblaze.git] / kernel / memremap.c
index 9eced2c..1490e63 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/types.h>
 #include <linux/wait_bit.h>
 #include <linux/xarray.h>
+#include <linux/hmm.h>
 
 static DEFINE_XARRAY(pgmap_array);
 #define SECTION_MASK ~((1UL << PA_SECTION_SHIFT) - 1)
@@ -24,6 +25,9 @@ vm_fault_t device_private_entry_fault(struct vm_area_struct *vma,
                       pmd_t *pmdp)
 {
        struct page *page = device_private_entry_to_page(entry);
+       struct hmm_devmem *devmem;
+
+       devmem = container_of(page->pgmap, typeof(*devmem), pagemap);
 
        /*
         * The page_fault() callback must migrate page back to system memory
@@ -39,9 +43,8 @@ vm_fault_t device_private_entry_fault(struct vm_area_struct *vma,
         * There is a more in-depth description of what that callback can and
         * cannot do, in include/linux/memremap.h
         */
-       return page->pgmap->page_fault(vma, addr, page, flags, pmdp);
+       return devmem->page_fault(vma, addr, page, flags, pmdp);
 }
-EXPORT_SYMBOL(device_private_entry_fault);
 #endif /* CONFIG_DEVICE_PRIVATE */
 
 static void pgmap_array_delete(struct resource *res)
@@ -87,24 +90,29 @@ static void devm_memremap_pages_release(void *data)
        struct resource *res = &pgmap->res;
        resource_size_t align_start, align_size;
        unsigned long pfn;
+       int nid;
 
+       pgmap->kill(pgmap->ref);
        for_each_device_pfn(pfn, pgmap)
                put_page(pfn_to_page(pfn));
 
-       if (percpu_ref_tryget_live(pgmap->ref)) {
-               dev_WARN(dev, "%s: page mapping is still live!\n", __func__);
-               percpu_ref_put(pgmap->ref);
-       }
-
        /* pages are dead and unused, undo the arch mapping */
        align_start = res->start & ~(SECTION_SIZE - 1);
        align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE)
                - align_start;
 
+       nid = page_to_nid(pfn_to_page(align_start >> PAGE_SHIFT));
+
        mem_hotplug_begin();
-       arch_remove_memory(align_start, align_size, pgmap->altmap_valid ?
-                       &pgmap->altmap : NULL);
-       kasan_remove_zero_shadow(__va(align_start), align_size);
+       if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
+               pfn = align_start >> PAGE_SHIFT;
+               __remove_pages(page_zone(pfn_to_page(pfn)), pfn,
+                               align_size >> PAGE_SHIFT, NULL);
+       } else {
+               arch_remove_memory(nid, align_start, align_size,
+                               pgmap->altmap_valid ? &pgmap->altmap : NULL);
+               kasan_remove_zero_shadow(__va(align_start), align_size);
+       }
        mem_hotplug_done();
 
        untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
@@ -116,7 +124,7 @@ static void devm_memremap_pages_release(void *data)
 /**
  * devm_memremap_pages - remap and provide memmap backing for the given resource
  * @dev: hosting device for @res
- * @pgmap: pointer to a struct dev_pgmap
+ * @pgmap: pointer to a struct dev_pagemap
  *
  * Notes:
  * 1/ At a minimum the res, ref and type members of @pgmap must be initialized
@@ -125,11 +133,8 @@ static void devm_memremap_pages_release(void *data)
  * 2/ The altmap field may optionally be initialized, in which case altmap_valid
  *    must be set to true
  *
- * 3/ pgmap.ref must be 'live' on entry and 'dead' before devm_memunmap_pages()
- *    time (or devm release event). The expected order of events is that ref has
- *    been through percpu_ref_kill() before devm_memremap_pages_release(). The
- *    wait for the completion of all references being dropped and
- *    percpu_ref_exit() must occur after devm_memremap_pages_release().
+ * 3/ pgmap->ref must be 'live' on entry and will be killed at
+ *    devm_memremap_pages_release() time, or if this routine fails.
  *
  * 4/ res is expected to be a host memory range that could feasibly be
  *    treated as a "System RAM" range, i.e. not a device mmio range, but
@@ -142,9 +147,18 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
                        &pgmap->altmap : NULL;
        struct resource *res = &pgmap->res;
        struct dev_pagemap *conflict_pgmap;
+       struct mhp_restrictions restrictions = {
+               /*
+                * We do not want any optional features only our own memmap
+               */
+               .altmap = altmap,
+       };
        pgprot_t pgprot = PAGE_KERNEL;
        int error, nid, is_ram;
 
+       if (!pgmap->ref || !pgmap->kill)
+               return ERR_PTR(-EINVAL);
+
        align_start = res->start & ~(SECTION_SIZE - 1);
        align_size = ALIGN(res->start + resource_size(res), SECTION_SIZE)
                - align_start;
@@ -167,18 +181,13 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
        is_ram = region_intersects(align_start, align_size,
                IORESOURCE_SYSTEM_RAM, IORES_DESC_NONE);
 
-       if (is_ram == REGION_MIXED) {
-               WARN_ONCE(1, "%s attempted on mixed region %pr\n",
-                               __func__, res);
-               return ERR_PTR(-ENXIO);
+       if (is_ram != REGION_DISJOINT) {
+               WARN_ONCE(1, "%s attempted on %s region %pr\n", __func__,
+                               is_ram == REGION_MIXED ? "mixed" : "ram", res);
+               error = -ENXIO;
+               goto err_array;
        }
 
-       if (is_ram == REGION_INTERSECTS)
-               return __va(res->start);
-
-       if (!pgmap->ref)
-               return ERR_PTR(-EINVAL);
-
        pgmap->dev = dev;
 
        error = xa_err(xa_store_range(&pgmap_array, PHYS_PFN(res->start),
@@ -196,17 +205,40 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
                goto err_pfn_remap;
 
        mem_hotplug_begin();
-       error = kasan_add_zero_shadow(__va(align_start), align_size);
-       if (error) {
-               mem_hotplug_done();
-               goto err_kasan;
+
+       /*
+        * For device private memory we call add_pages() as we only need to
+        * allocate and initialize struct page for the device memory. More-
+        * over the device memory is un-accessible thus we do not want to
+        * create a linear mapping for the memory like arch_add_memory()
+        * would do.
+        *
+        * For all other device memory types, which are accessible by
+        * the CPU, we do want the linear mapping and thus use
+        * arch_add_memory().
+        */
+       if (pgmap->type == MEMORY_DEVICE_PRIVATE) {
+               error = add_pages(nid, align_start >> PAGE_SHIFT,
+                               align_size >> PAGE_SHIFT, &restrictions);
+       } else {
+               error = kasan_add_zero_shadow(__va(align_start), align_size);
+               if (error) {
+                       mem_hotplug_done();
+                       goto err_kasan;
+               }
+
+               error = arch_add_memory(nid, align_start, align_size,
+                                       &restrictions);
+       }
+
+       if (!error) {
+               struct zone *zone;
+
+               zone = &NODE_DATA(nid)->node_zones[ZONE_DEVICE];
+               move_pfn_range_to_zone(zone, align_start >> PAGE_SHIFT,
+                               align_size >> PAGE_SHIFT, altmap);
        }
 
-       error = arch_add_memory(nid, align_start, align_size, altmap, false);
-       if (!error)
-               move_pfn_range_to_zone(&NODE_DATA(nid)->node_zones[ZONE_DEVICE],
-                                       align_start >> PAGE_SHIFT,
-                                       align_size >> PAGE_SHIFT, altmap);
        mem_hotplug_done();
        if (error)
                goto err_add_memory;
@@ -220,7 +252,10 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
                                align_size >> PAGE_SHIFT, pgmap);
        percpu_ref_get_many(pgmap->ref, pfn_end(pgmap) - pfn_first(pgmap));
 
-       devm_add_action(dev, devm_memremap_pages_release, pgmap);
+       error = devm_add_action_or_reset(dev, devm_memremap_pages_release,
+                       pgmap);
+       if (error)
+               return ERR_PTR(error);
 
        return __va(res->start);
 
@@ -231,9 +266,10 @@ void *devm_memremap_pages(struct device *dev, struct dev_pagemap *pgmap)
  err_pfn_remap:
        pgmap_array_delete(res);
  err_array:
+       pgmap->kill(pgmap->ref);
        return ERR_PTR(error);
 }
-EXPORT_SYMBOL(devm_memremap_pages);
+EXPORT_SYMBOL_GPL(devm_memremap_pages);
 
 unsigned long vmem_altmap_offset(struct vmem_altmap *altmap)
 {