diff options
author | Christoph Hellwig <hch@lst.de> | 2015-08-17 16:00:35 +0200 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2015-08-27 19:40:58 -0400 |
commit | 41e94a851304f7acac840adec4004f8aeee53ad4 (patch) | |
tree | fe4c599d1263b7fdd92eb2838e5d223015066718 /kernel/memremap.c | |
parent | 033fbae988fcb67e5077203512181890848b8e90 (diff) | |
download | lwn-41e94a851304f7acac840adec4004f8aeee53ad4.tar.gz lwn-41e94a851304f7acac840adec4004f8aeee53ad4.zip |
add devm_memremap_pages
This behaves like devm_memremap except that it ensures we have page
structures available that can back the region.
Signed-off-by: Christoph Hellwig <hch@lst.de>
[djbw: catch attempts to remap RAM, drop flags]
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'kernel/memremap.c')
-rw-r--r-- | kernel/memremap.c | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/kernel/memremap.c b/kernel/memremap.c index 5c9b55eaf121..72b0c66628b6 100644 --- a/kernel/memremap.c +++ b/kernel/memremap.c @@ -14,6 +14,7 @@ #include <linux/types.h> #include <linux/io.h> #include <linux/mm.h> +#include <linux/memory_hotplug.h> #ifndef ioremap_cache /* temporary while we convert existing ioremap_cache users to memremap */ @@ -135,3 +136,55 @@ void devm_memunmap(struct device *dev, void *addr) memunmap(addr); } EXPORT_SYMBOL(devm_memunmap); + +#ifdef CONFIG_ZONE_DEVICE +struct page_map { + struct resource res; +}; + +static void devm_memremap_pages_release(struct device *dev, void *res) +{ + struct page_map *page_map = res; + + /* pages are dead and unused, undo the arch mapping */ + arch_remove_memory(page_map->res.start, resource_size(&page_map->res)); +} + +void *devm_memremap_pages(struct device *dev, struct resource *res) +{ + int is_ram = region_intersects(res->start, resource_size(res), + "System RAM"); + struct page_map *page_map; + int error, nid; + + 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_INTERSECTS) + return __va(res->start); + + page_map = devres_alloc(devm_memremap_pages_release, + sizeof(*page_map), GFP_KERNEL); + if (!page_map) + return ERR_PTR(-ENOMEM); + + memcpy(&page_map->res, res, sizeof(*res)); + + nid = dev_to_node(dev); + if (nid < 0) + nid = 0; + + error = arch_add_memory(nid, res->start, resource_size(res), true); + if (error) { + devres_free(page_map); + return ERR_PTR(error); + } + + devres_add(dev, page_map); + return __va(res->start); +} +EXPORT_SYMBOL(devm_memremap_pages); +#endif /* CONFIG_ZONE_DEVICE */ |