mm/mmu_notifier: use structure for invalidate_range_start/end callback
[linux-2.6-block.git] / mm / hmm.c
index 90c34f3d1243aa5ec18970ec7aac095d8f657d32..7895877312175206327b849f85a7c9c80d4fa3ac 100644 (file)
--- a/mm/hmm.c
+++ b/mm/hmm.c
@@ -189,35 +189,30 @@ static void hmm_release(struct mmu_notifier *mn, struct mm_struct *mm)
 }
 
 static int hmm_invalidate_range_start(struct mmu_notifier *mn,
-                                     struct mm_struct *mm,
-                                     unsigned long start,
-                                     unsigned long end,
-                                     bool blockable)
+                       const struct mmu_notifier_range *range)
 {
        struct hmm_update update;
-       struct hmm *hmm = mm->hmm;
+       struct hmm *hmm = range->mm->hmm;
 
        VM_BUG_ON(!hmm);
 
-       update.start = start;
-       update.end = end;
+       update.start = range->start;
+       update.end = range->end;
        update.event = HMM_UPDATE_INVALIDATE;
-       update.blockable = blockable;
+       update.blockable = range->blockable;
        return hmm_invalidate_range(hmm, true, &update);
 }
 
 static void hmm_invalidate_range_end(struct mmu_notifier *mn,
-                                    struct mm_struct *mm,
-                                    unsigned long start,
-                                    unsigned long end)
+                       const struct mmu_notifier_range *range)
 {
        struct hmm_update update;
-       struct hmm *hmm = mm->hmm;
+       struct hmm *hmm = range->mm->hmm;
 
        VM_BUG_ON(!hmm);
 
-       update.start = start;
-       update.end = end;
+       update.start = range->start;
+       update.end = range->end;
        update.event = HMM_UPDATE_INVALIDATE;
        update.blockable = true;
        hmm_invalidate_range(hmm, false, &update);
@@ -986,19 +981,13 @@ static void hmm_devmem_ref_exit(void *data)
        struct hmm_devmem *devmem;
 
        devmem = container_of(ref, struct hmm_devmem, ref);
+       wait_for_completion(&devmem->completion);
        percpu_ref_exit(ref);
-       devm_remove_action(devmem->device, &hmm_devmem_ref_exit, data);
 }
 
-static void hmm_devmem_ref_kill(void *data)
+static void hmm_devmem_ref_kill(struct percpu_ref *ref)
 {
-       struct percpu_ref *ref = data;
-       struct hmm_devmem *devmem;
-
-       devmem = container_of(ref, struct hmm_devmem, ref);
        percpu_ref_kill(ref);
-       wait_for_completion(&devmem->completion);
-       devm_remove_action(devmem->device, &hmm_devmem_ref_kill, data);
 }
 
 static int hmm_devmem_fault(struct vm_area_struct *vma,
@@ -1021,172 +1010,6 @@ static void hmm_devmem_free(struct page *page, void *data)
        devmem->ops->free(devmem, page);
 }
 
-static DEFINE_MUTEX(hmm_devmem_lock);
-static RADIX_TREE(hmm_devmem_radix, GFP_KERNEL);
-
-static void hmm_devmem_radix_release(struct resource *resource)
-{
-       resource_size_t key;
-
-       mutex_lock(&hmm_devmem_lock);
-       for (key = resource->start;
-            key <= resource->end;
-            key += PA_SECTION_SIZE)
-               radix_tree_delete(&hmm_devmem_radix, key >> PA_SECTION_SHIFT);
-       mutex_unlock(&hmm_devmem_lock);
-}
-
-static void hmm_devmem_release(struct device *dev, void *data)
-{
-       struct hmm_devmem *devmem = data;
-       struct resource *resource = devmem->resource;
-       unsigned long start_pfn, npages;
-       struct zone *zone;
-       struct page *page;
-
-       if (percpu_ref_tryget_live(&devmem->ref)) {
-               dev_WARN(dev, "%s: page mapping is still live!\n", __func__);
-               percpu_ref_put(&devmem->ref);
-       }
-
-       /* pages are dead and unused, undo the arch mapping */
-       start_pfn = (resource->start & ~(PA_SECTION_SIZE - 1)) >> PAGE_SHIFT;
-       npages = ALIGN(resource_size(resource), PA_SECTION_SIZE) >> PAGE_SHIFT;
-
-       page = pfn_to_page(start_pfn);
-       zone = page_zone(page);
-
-       mem_hotplug_begin();
-       if (resource->desc == IORES_DESC_DEVICE_PRIVATE_MEMORY)
-               __remove_pages(zone, start_pfn, npages, NULL);
-       else
-               arch_remove_memory(start_pfn << PAGE_SHIFT,
-                                  npages << PAGE_SHIFT, NULL);
-       mem_hotplug_done();
-
-       hmm_devmem_radix_release(resource);
-}
-
-static int hmm_devmem_pages_create(struct hmm_devmem *devmem)
-{
-       resource_size_t key, align_start, align_size, align_end;
-       struct device *device = devmem->device;
-       int ret, nid, is_ram;
-
-       align_start = devmem->resource->start & ~(PA_SECTION_SIZE - 1);
-       align_size = ALIGN(devmem->resource->start +
-                          resource_size(devmem->resource),
-                          PA_SECTION_SIZE) - align_start;
-
-       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__, devmem->resource);
-               return -ENXIO;
-       }
-       if (is_ram == REGION_INTERSECTS)
-               return -ENXIO;
-
-       if (devmem->resource->desc == IORES_DESC_DEVICE_PUBLIC_MEMORY)
-               devmem->pagemap.type = MEMORY_DEVICE_PUBLIC;
-       else
-               devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
-
-       devmem->pagemap.res = *devmem->resource;
-       devmem->pagemap.page_fault = hmm_devmem_fault;
-       devmem->pagemap.page_free = hmm_devmem_free;
-       devmem->pagemap.dev = devmem->device;
-       devmem->pagemap.ref = &devmem->ref;
-       devmem->pagemap.data = devmem;
-
-       mutex_lock(&hmm_devmem_lock);
-       align_end = align_start + align_size - 1;
-       for (key = align_start; key <= align_end; key += PA_SECTION_SIZE) {
-               struct hmm_devmem *dup;
-
-               dup = radix_tree_lookup(&hmm_devmem_radix,
-                                       key >> PA_SECTION_SHIFT);
-               if (dup) {
-                       dev_err(device, "%s: collides with mapping for %s\n",
-                               __func__, dev_name(dup->device));
-                       mutex_unlock(&hmm_devmem_lock);
-                       ret = -EBUSY;
-                       goto error;
-               }
-               ret = radix_tree_insert(&hmm_devmem_radix,
-                                       key >> PA_SECTION_SHIFT,
-                                       devmem);
-               if (ret) {
-                       dev_err(device, "%s: failed: %d\n", __func__, ret);
-                       mutex_unlock(&hmm_devmem_lock);
-                       goto error_radix;
-               }
-       }
-       mutex_unlock(&hmm_devmem_lock);
-
-       nid = dev_to_node(device);
-       if (nid < 0)
-               nid = numa_mem_id();
-
-       mem_hotplug_begin();
-       /*
-        * 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 device public memory, which is accesible by the CPU, we do
-        * want the linear mapping and thus use arch_add_memory().
-        */
-       if (devmem->pagemap.type == MEMORY_DEVICE_PUBLIC)
-               ret = arch_add_memory(nid, align_start, align_size, NULL,
-                               false);
-       else
-               ret = add_pages(nid, align_start >> PAGE_SHIFT,
-                               align_size >> PAGE_SHIFT, NULL, false);
-       if (ret) {
-               mem_hotplug_done();
-               goto error_add_memory;
-       }
-       move_pfn_range_to_zone(&NODE_DATA(nid)->node_zones[ZONE_DEVICE],
-                               align_start >> PAGE_SHIFT,
-                               align_size >> PAGE_SHIFT, NULL);
-       mem_hotplug_done();
-
-       /*
-        * Initialization of the pages has been deferred until now in order
-        * to allow us to do the work while not holding the hotplug lock.
-        */
-       memmap_init_zone_device(&NODE_DATA(nid)->node_zones[ZONE_DEVICE],
-                               align_start >> PAGE_SHIFT,
-                               align_size >> PAGE_SHIFT, &devmem->pagemap);
-
-       return 0;
-
-error_add_memory:
-       untrack_pfn(NULL, PHYS_PFN(align_start), align_size);
-error_radix:
-       hmm_devmem_radix_release(devmem->resource);
-error:
-       return ret;
-}
-
-static int hmm_devmem_match(struct device *dev, void *data, void *match_data)
-{
-       struct hmm_devmem *devmem = data;
-
-       return devmem->resource == match_data;
-}
-
-static void hmm_devmem_pages_remove(struct hmm_devmem *devmem)
-{
-       devres_release(devmem->device, &hmm_devmem_release,
-                      &hmm_devmem_match, devmem->resource);
-}
-
 /*
  * hmm_devmem_add() - hotplug ZONE_DEVICE memory for device memory
  *
@@ -1210,12 +1033,12 @@ struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
 {
        struct hmm_devmem *devmem;
        resource_size_t addr;
+       void *result;
        int ret;
 
        dev_pagemap_get_ops();
 
-       devmem = devres_alloc_node(&hmm_devmem_release, sizeof(*devmem),
-                                  GFP_KERNEL, dev_to_node(device));
+       devmem = devm_kzalloc(device, sizeof(*devmem), GFP_KERNEL);
        if (!devmem)
                return ERR_PTR(-ENOMEM);
 
@@ -1229,11 +1052,11 @@ struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
        ret = percpu_ref_init(&devmem->ref, &hmm_devmem_ref_release,
                              0, GFP_KERNEL);
        if (ret)
-               goto error_percpu_ref;
+               return ERR_PTR(ret);
 
-       ret = devm_add_action(device, hmm_devmem_ref_exit, &devmem->ref);
+       ret = devm_add_action_or_reset(device, hmm_devmem_ref_exit, &devmem->ref);
        if (ret)
-               goto error_devm_add_action;
+               return ERR_PTR(ret);
 
        size = ALIGN(size, PA_SECTION_SIZE);
        addr = min((unsigned long)iomem_resource.end,
@@ -1253,54 +1076,40 @@ struct hmm_devmem *hmm_devmem_add(const struct hmm_devmem_ops *ops,
 
                devmem->resource = devm_request_mem_region(device, addr, size,
                                                           dev_name(device));
-               if (!devmem->resource) {
-                       ret = -ENOMEM;
-                       goto error_no_resource;
-               }
+               if (!devmem->resource)
+                       return ERR_PTR(-ENOMEM);
                break;
        }
-       if (!devmem->resource) {
-               ret = -ERANGE;
-               goto error_no_resource;
-       }
+       if (!devmem->resource)
+               return ERR_PTR(-ERANGE);
 
        devmem->resource->desc = IORES_DESC_DEVICE_PRIVATE_MEMORY;
        devmem->pfn_first = devmem->resource->start >> PAGE_SHIFT;
        devmem->pfn_last = devmem->pfn_first +
                           (resource_size(devmem->resource) >> PAGE_SHIFT);
 
-       ret = hmm_devmem_pages_create(devmem);
-       if (ret)
-               goto error_pages;
-
-       devres_add(device, devmem);
-
-       ret = devm_add_action(device, hmm_devmem_ref_kill, &devmem->ref);
-       if (ret) {
-               hmm_devmem_remove(devmem);
-               return ERR_PTR(ret);
-       }
+       devmem->pagemap.type = MEMORY_DEVICE_PRIVATE;
+       devmem->pagemap.res = *devmem->resource;
+       devmem->pagemap.page_fault = hmm_devmem_fault;
+       devmem->pagemap.page_free = hmm_devmem_free;
+       devmem->pagemap.altmap_valid = false;
+       devmem->pagemap.ref = &devmem->ref;
+       devmem->pagemap.data = devmem;
+       devmem->pagemap.kill = hmm_devmem_ref_kill;
 
+       result = devm_memremap_pages(devmem->device, &devmem->pagemap);
+       if (IS_ERR(result))
+               return result;
        return devmem;
-
-error_pages:
-       devm_release_mem_region(device, devmem->resource->start,
-                               resource_size(devmem->resource));
-error_no_resource:
-error_devm_add_action:
-       hmm_devmem_ref_kill(&devmem->ref);
-       hmm_devmem_ref_exit(&devmem->ref);
-error_percpu_ref:
-       devres_free(devmem);
-       return ERR_PTR(ret);
 }
-EXPORT_SYMBOL(hmm_devmem_add);
+EXPORT_SYMBOL_GPL(hmm_devmem_add);
 
 struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
                                           struct device *device,
                                           struct resource *res)
 {
        struct hmm_devmem *devmem;
+       void *result;
        int ret;
 
        if (res->desc != IORES_DESC_DEVICE_PUBLIC_MEMORY)
@@ -1308,8 +1117,7 @@ struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
 
        dev_pagemap_get_ops();
 
-       devmem = devres_alloc_node(&hmm_devmem_release, sizeof(*devmem),
-                                  GFP_KERNEL, dev_to_node(device));
+       devmem = devm_kzalloc(device, sizeof(*devmem), GFP_KERNEL);
        if (!devmem)
                return ERR_PTR(-ENOMEM);
 
@@ -1323,71 +1131,32 @@ struct hmm_devmem *hmm_devmem_add_resource(const struct hmm_devmem_ops *ops,
        ret = percpu_ref_init(&devmem->ref, &hmm_devmem_ref_release,
                              0, GFP_KERNEL);
        if (ret)
-               goto error_percpu_ref;
+               return ERR_PTR(ret);
 
-       ret = devm_add_action(device, hmm_devmem_ref_exit, &devmem->ref);
+       ret = devm_add_action_or_reset(device, hmm_devmem_ref_exit,
+                       &devmem->ref);
        if (ret)
-               goto error_devm_add_action;
-
+               return ERR_PTR(ret);
 
        devmem->pfn_first = devmem->resource->start >> PAGE_SHIFT;
        devmem->pfn_last = devmem->pfn_first +
                           (resource_size(devmem->resource) >> PAGE_SHIFT);
 
-       ret = hmm_devmem_pages_create(devmem);
-       if (ret)
-               goto error_devm_add_action;
-
-       devres_add(device, devmem);
-
-       ret = devm_add_action(device, hmm_devmem_ref_kill, &devmem->ref);
-       if (ret) {
-               hmm_devmem_remove(devmem);
-               return ERR_PTR(ret);
-       }
+       devmem->pagemap.type = MEMORY_DEVICE_PUBLIC;
+       devmem->pagemap.res = *devmem->resource;
+       devmem->pagemap.page_fault = hmm_devmem_fault;
+       devmem->pagemap.page_free = hmm_devmem_free;
+       devmem->pagemap.altmap_valid = false;
+       devmem->pagemap.ref = &devmem->ref;
+       devmem->pagemap.data = devmem;
+       devmem->pagemap.kill = hmm_devmem_ref_kill;
 
+       result = devm_memremap_pages(devmem->device, &devmem->pagemap);
+       if (IS_ERR(result))
+               return result;
        return devmem;
-
-error_devm_add_action:
-       hmm_devmem_ref_kill(&devmem->ref);
-       hmm_devmem_ref_exit(&devmem->ref);
-error_percpu_ref:
-       devres_free(devmem);
-       return ERR_PTR(ret);
-}
-EXPORT_SYMBOL(hmm_devmem_add_resource);
-
-/*
- * hmm_devmem_remove() - remove device memory (kill and free ZONE_DEVICE)
- *
- * @devmem: hmm_devmem struct use to track and manage the ZONE_DEVICE memory
- *
- * This will hot-unplug memory that was hotplugged by hmm_devmem_add on behalf
- * of the device driver. It will free struct page and remove the resource that
- * reserved the physical address range for this device memory.
- */
-void hmm_devmem_remove(struct hmm_devmem *devmem)
-{
-       resource_size_t start, size;
-       struct device *device;
-       bool cdm = false;
-
-       if (!devmem)
-               return;
-
-       device = devmem->device;
-       start = devmem->resource->start;
-       size = resource_size(devmem->resource);
-
-       cdm = devmem->resource->desc == IORES_DESC_DEVICE_PUBLIC_MEMORY;
-       hmm_devmem_ref_kill(&devmem->ref);
-       hmm_devmem_ref_exit(&devmem->ref);
-       hmm_devmem_pages_remove(devmem);
-
-       if (!cdm)
-               devm_release_mem_region(device, start, size);
 }
-EXPORT_SYMBOL(hmm_devmem_remove);
+EXPORT_SYMBOL_GPL(hmm_devmem_add_resource);
 
 /*
  * A device driver that wants to handle multiple devices memory through a