aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nvdimm
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-06-06 20:42:38 -0400
committerDan Williams <dan.j.williams@intel.com>2016-07-07 20:11:09 -0400
commit29b9aa0aa3837c93ecd804dd3ada39b8cc75607d (patch)
treec3ee3dbe22fb1ec4a71ff887f382b3eac815d4b3 /drivers/nvdimm
parent81ed4e3670853e4cebad88aeffc0ba1d90d4d6ed (diff)
libnvdimm: introduce devm_nvdimm_memremap(), convert nfit_spa_map() users
In preparation for generically mapping flush hint addresses for both the BLK and PMEM use case, provide a generic / reference counted mapping api. Given the fact that a dimm may belong to multiple regions (PMEM and BLK), the flush hint addresses need to be held valid as long as any region associated with the dimm is active. This is similar to the existing BLK-region case where multiple BLK-regions may share an aperture mapping. Up-level this shared / reference-counted mapping capability from the nfit driver to a core nvdimm capability. This eliminates the need for the nd_blk_region.disable() callback. Note that the removal of nfit_spa_map() and related infrastructure is deferred to a later patch. Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r--drivers/nvdimm/Kconfig2
-rw-r--r--drivers/nvdimm/core.c123
-rw-r--r--drivers/nvdimm/nd-core.h1
3 files changed, 125 insertions, 1 deletions
diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index 7c8a3bf07884..124c2432ac9c 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -1,6 +1,7 @@
1menuconfig LIBNVDIMM 1menuconfig LIBNVDIMM
2 tristate "NVDIMM (Non-Volatile Memory Device) Support" 2 tristate "NVDIMM (Non-Volatile Memory Device) Support"
3 depends on PHYS_ADDR_T_64BIT 3 depends on PHYS_ADDR_T_64BIT
4 depends on HAS_IOMEM
4 depends on BLK_DEV 5 depends on BLK_DEV
5 help 6 help
6 Generic support for non-volatile memory devices including 7 Generic support for non-volatile memory devices including
@@ -19,7 +20,6 @@ if LIBNVDIMM
19config BLK_DEV_PMEM 20config BLK_DEV_PMEM
20 tristate "PMEM: Persistent memory block device support" 21 tristate "PMEM: Persistent memory block device support"
21 default LIBNVDIMM 22 default LIBNVDIMM
22 depends on HAS_IOMEM
23 select ND_BTT if BTT 23 select ND_BTT if BTT
24 select ND_PFN if NVDIMM_PFN 24 select ND_PFN if NVDIMM_PFN
25 help 25 help
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index 32e4fe2f6274..757e0cf028bf 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -20,6 +20,7 @@
20#include <linux/ndctl.h> 20#include <linux/ndctl.h>
21#include <linux/mutex.h> 21#include <linux/mutex.h>
22#include <linux/slab.h> 22#include <linux/slab.h>
23#include <linux/io.h>
23#include "nd-core.h" 24#include "nd-core.h"
24#include "nd.h" 25#include "nd.h"
25 26
@@ -57,6 +58,127 @@ bool is_nvdimm_bus_locked(struct device *dev)
57} 58}
58EXPORT_SYMBOL(is_nvdimm_bus_locked); 59EXPORT_SYMBOL(is_nvdimm_bus_locked);
59 60
61struct nvdimm_map {
62 struct nvdimm_bus *nvdimm_bus;
63 struct list_head list;
64 resource_size_t offset;
65 unsigned long flags;
66 size_t size;
67 union {
68 void *mem;
69 void __iomem *iomem;
70 };
71 struct kref kref;
72};
73
74static struct nvdimm_map *find_nvdimm_map(struct device *dev,
75 resource_size_t offset)
76{
77 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
78 struct nvdimm_map *nvdimm_map;
79
80 list_for_each_entry(nvdimm_map, &nvdimm_bus->mapping_list, list)
81 if (nvdimm_map->offset == offset)
82 return nvdimm_map;
83 return NULL;
84}
85
86static struct nvdimm_map *alloc_nvdimm_map(struct device *dev,
87 resource_size_t offset, size_t size, unsigned long flags)
88{
89 struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
90 struct nvdimm_map *nvdimm_map;
91
92 nvdimm_map = kzalloc(sizeof(*nvdimm_map), GFP_KERNEL);
93 if (!nvdimm_map)
94 return NULL;
95
96 INIT_LIST_HEAD(&nvdimm_map->list);
97 nvdimm_map->nvdimm_bus = nvdimm_bus;
98 nvdimm_map->offset = offset;
99 nvdimm_map->flags = flags;
100 nvdimm_map->size = size;
101 kref_init(&nvdimm_map->kref);
102
103 if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev)))
104 goto err_request_region;
105
106 if (flags)
107 nvdimm_map->mem = memremap(offset, size, flags);
108 else
109 nvdimm_map->iomem = ioremap(offset, size);
110
111 if (!nvdimm_map->mem)
112 goto err_map;
113
114 dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev), "%s: bus unlocked!",
115 __func__);
116 list_add(&nvdimm_map->list, &nvdimm_bus->mapping_list);
117
118 return nvdimm_map;
119
120 err_map:
121 release_mem_region(offset, size);
122 err_request_region:
123 kfree(nvdimm_map);
124 return NULL;
125}
126
127static void nvdimm_map_release(struct kref *kref)
128{
129 struct nvdimm_bus *nvdimm_bus;
130 struct nvdimm_map *nvdimm_map;
131
132 nvdimm_map = container_of(kref, struct nvdimm_map, kref);
133 nvdimm_bus = nvdimm_map->nvdimm_bus;
134
135 dev_dbg(&nvdimm_bus->dev, "%s: %pa\n", __func__, &nvdimm_map->offset);
136 list_del(&nvdimm_map->list);
137 if (nvdimm_map->flags)
138 memunmap(nvdimm_map->mem);
139 else
140 iounmap(nvdimm_map->iomem);
141 release_mem_region(nvdimm_map->offset, nvdimm_map->size);
142 kfree(nvdimm_map);
143}
144
145static void nvdimm_map_put(void *data)
146{
147 struct nvdimm_map *nvdimm_map = data;
148 struct nvdimm_bus *nvdimm_bus = nvdimm_map->nvdimm_bus;
149
150 nvdimm_bus_lock(&nvdimm_bus->dev);
151 kref_put(&nvdimm_map->kref, nvdimm_map_release);
152 nvdimm_bus_unlock(&nvdimm_bus->dev);
153}
154
155/**
156 * devm_nvdimm_memremap - map a resource that is shared across regions
157 * @dev: device that will own a reference to the shared mapping
158 * @offset: physical base address of the mapping
159 * @size: mapping size
160 * @flags: memremap flags, or, if zero, perform an ioremap instead
161 */
162void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset,
163 size_t size, unsigned long flags)
164{
165 struct nvdimm_map *nvdimm_map;
166
167 nvdimm_bus_lock(dev);
168 nvdimm_map = find_nvdimm_map(dev, offset);
169 if (!nvdimm_map)
170 nvdimm_map = alloc_nvdimm_map(dev, offset, size, flags);
171 else
172 kref_get(&nvdimm_map->kref);
173 nvdimm_bus_unlock(dev);
174
175 if (devm_add_action_or_reset(dev, nvdimm_map_put, nvdimm_map))
176 return NULL;
177
178 return nvdimm_map->mem;
179}
180EXPORT_SYMBOL_GPL(devm_nvdimm_memremap);
181
60u64 nd_fletcher64(void *addr, size_t len, bool le) 182u64 nd_fletcher64(void *addr, size_t len, bool le)
61{ 183{
62 u32 *buf = addr; 184 u32 *buf = addr;
@@ -335,6 +457,7 @@ struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
335 if (!nvdimm_bus) 457 if (!nvdimm_bus)
336 return NULL; 458 return NULL;
337 INIT_LIST_HEAD(&nvdimm_bus->list); 459 INIT_LIST_HEAD(&nvdimm_bus->list);
460 INIT_LIST_HEAD(&nvdimm_bus->mapping_list);
338 INIT_LIST_HEAD(&nvdimm_bus->poison_list); 461 INIT_LIST_HEAD(&nvdimm_bus->poison_list);
339 init_waitqueue_head(&nvdimm_bus->probe_wait); 462 init_waitqueue_head(&nvdimm_bus->probe_wait);
340 nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 463 nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index 284cdaa268cf..790b62cc81ed 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -31,6 +31,7 @@ struct nvdimm_bus {
31 struct device dev; 31 struct device dev;
32 int id, probe_active; 32 int id, probe_active;
33 struct list_head poison_list; 33 struct list_head poison_list;
34 struct list_head mapping_list;
34 struct mutex reconfig_mutex; 35 struct mutex reconfig_mutex;
35}; 36};
36 37