diff options
author | Dan Williams <dan.j.williams@intel.com> | 2016-06-06 20:42:38 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2016-07-07 20:11:09 -0400 |
commit | 29b9aa0aa3837c93ecd804dd3ada39b8cc75607d (patch) | |
tree | c3ee3dbe22fb1ec4a71ff887f382b3eac815d4b3 /drivers/nvdimm | |
parent | 81ed4e3670853e4cebad88aeffc0ba1d90d4d6ed (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/Kconfig | 2 | ||||
-rw-r--r-- | drivers/nvdimm/core.c | 123 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 1 |
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 @@ | |||
1 | menuconfig LIBNVDIMM | 1 | menuconfig 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 | |||
19 | config BLK_DEV_PMEM | 20 | config 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 | } |
58 | EXPORT_SYMBOL(is_nvdimm_bus_locked); | 59 | EXPORT_SYMBOL(is_nvdimm_bus_locked); |
59 | 60 | ||
61 | struct 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 | |||
74 | static 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 | |||
86 | static 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 | |||
127 | static 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 | |||
145 | static 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 | */ | ||
162 | void *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 | } | ||
180 | EXPORT_SYMBOL_GPL(devm_nvdimm_memremap); | ||
181 | |||
60 | u64 nd_fletcher64(void *addr, size_t len, bool le) | 182 | u64 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 | ||