diff options
Diffstat (limited to 'drivers/nvdimm/core.c')
-rw-r--r-- | drivers/nvdimm/core.c | 253 |
1 files changed, 128 insertions, 125 deletions
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index be89764315c2..715583f69d28 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c | |||
@@ -20,12 +20,12 @@ | |||
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 | ||
26 | LIST_HEAD(nvdimm_bus_list); | 27 | LIST_HEAD(nvdimm_bus_list); |
27 | DEFINE_MUTEX(nvdimm_bus_list_mutex); | 28 | DEFINE_MUTEX(nvdimm_bus_list_mutex); |
28 | static DEFINE_IDA(nd_ida); | ||
29 | 29 | ||
30 | void nvdimm_bus_lock(struct device *dev) | 30 | void nvdimm_bus_lock(struct device *dev) |
31 | { | 31 | { |
@@ -57,6 +57,127 @@ bool is_nvdimm_bus_locked(struct device *dev) | |||
57 | } | 57 | } |
58 | EXPORT_SYMBOL(is_nvdimm_bus_locked); | 58 | EXPORT_SYMBOL(is_nvdimm_bus_locked); |
59 | 59 | ||
60 | struct nvdimm_map { | ||
61 | struct nvdimm_bus *nvdimm_bus; | ||
62 | struct list_head list; | ||
63 | resource_size_t offset; | ||
64 | unsigned long flags; | ||
65 | size_t size; | ||
66 | union { | ||
67 | void *mem; | ||
68 | void __iomem *iomem; | ||
69 | }; | ||
70 | struct kref kref; | ||
71 | }; | ||
72 | |||
73 | static struct nvdimm_map *find_nvdimm_map(struct device *dev, | ||
74 | resource_size_t offset) | ||
75 | { | ||
76 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); | ||
77 | struct nvdimm_map *nvdimm_map; | ||
78 | |||
79 | list_for_each_entry(nvdimm_map, &nvdimm_bus->mapping_list, list) | ||
80 | if (nvdimm_map->offset == offset) | ||
81 | return nvdimm_map; | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | static struct nvdimm_map *alloc_nvdimm_map(struct device *dev, | ||
86 | resource_size_t offset, size_t size, unsigned long flags) | ||
87 | { | ||
88 | struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev); | ||
89 | struct nvdimm_map *nvdimm_map; | ||
90 | |||
91 | nvdimm_map = kzalloc(sizeof(*nvdimm_map), GFP_KERNEL); | ||
92 | if (!nvdimm_map) | ||
93 | return NULL; | ||
94 | |||
95 | INIT_LIST_HEAD(&nvdimm_map->list); | ||
96 | nvdimm_map->nvdimm_bus = nvdimm_bus; | ||
97 | nvdimm_map->offset = offset; | ||
98 | nvdimm_map->flags = flags; | ||
99 | nvdimm_map->size = size; | ||
100 | kref_init(&nvdimm_map->kref); | ||
101 | |||
102 | if (!request_mem_region(offset, size, dev_name(&nvdimm_bus->dev))) | ||
103 | goto err_request_region; | ||
104 | |||
105 | if (flags) | ||
106 | nvdimm_map->mem = memremap(offset, size, flags); | ||
107 | else | ||
108 | nvdimm_map->iomem = ioremap(offset, size); | ||
109 | |||
110 | if (!nvdimm_map->mem) | ||
111 | goto err_map; | ||
112 | |||
113 | dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev), "%s: bus unlocked!", | ||
114 | __func__); | ||
115 | list_add(&nvdimm_map->list, &nvdimm_bus->mapping_list); | ||
116 | |||
117 | return nvdimm_map; | ||
118 | |||
119 | err_map: | ||
120 | release_mem_region(offset, size); | ||
121 | err_request_region: | ||
122 | kfree(nvdimm_map); | ||
123 | return NULL; | ||
124 | } | ||
125 | |||
126 | static void nvdimm_map_release(struct kref *kref) | ||
127 | { | ||
128 | struct nvdimm_bus *nvdimm_bus; | ||
129 | struct nvdimm_map *nvdimm_map; | ||
130 | |||
131 | nvdimm_map = container_of(kref, struct nvdimm_map, kref); | ||
132 | nvdimm_bus = nvdimm_map->nvdimm_bus; | ||
133 | |||
134 | dev_dbg(&nvdimm_bus->dev, "%s: %pa\n", __func__, &nvdimm_map->offset); | ||
135 | list_del(&nvdimm_map->list); | ||
136 | if (nvdimm_map->flags) | ||
137 | memunmap(nvdimm_map->mem); | ||
138 | else | ||
139 | iounmap(nvdimm_map->iomem); | ||
140 | release_mem_region(nvdimm_map->offset, nvdimm_map->size); | ||
141 | kfree(nvdimm_map); | ||
142 | } | ||
143 | |||
144 | static void nvdimm_map_put(void *data) | ||
145 | { | ||
146 | struct nvdimm_map *nvdimm_map = data; | ||
147 | struct nvdimm_bus *nvdimm_bus = nvdimm_map->nvdimm_bus; | ||
148 | |||
149 | nvdimm_bus_lock(&nvdimm_bus->dev); | ||
150 | kref_put(&nvdimm_map->kref, nvdimm_map_release); | ||
151 | nvdimm_bus_unlock(&nvdimm_bus->dev); | ||
152 | } | ||
153 | |||
154 | /** | ||
155 | * devm_nvdimm_memremap - map a resource that is shared across regions | ||
156 | * @dev: device that will own a reference to the shared mapping | ||
157 | * @offset: physical base address of the mapping | ||
158 | * @size: mapping size | ||
159 | * @flags: memremap flags, or, if zero, perform an ioremap instead | ||
160 | */ | ||
161 | void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset, | ||
162 | size_t size, unsigned long flags) | ||
163 | { | ||
164 | struct nvdimm_map *nvdimm_map; | ||
165 | |||
166 | nvdimm_bus_lock(dev); | ||
167 | nvdimm_map = find_nvdimm_map(dev, offset); | ||
168 | if (!nvdimm_map) | ||
169 | nvdimm_map = alloc_nvdimm_map(dev, offset, size, flags); | ||
170 | else | ||
171 | kref_get(&nvdimm_map->kref); | ||
172 | nvdimm_bus_unlock(dev); | ||
173 | |||
174 | if (devm_add_action_or_reset(dev, nvdimm_map_put, nvdimm_map)) | ||
175 | return NULL; | ||
176 | |||
177 | return nvdimm_map->mem; | ||
178 | } | ||
179 | EXPORT_SYMBOL_GPL(devm_nvdimm_memremap); | ||
180 | |||
60 | u64 nd_fletcher64(void *addr, size_t len, bool le) | 181 | u64 nd_fletcher64(void *addr, size_t len, bool le) |
61 | { | 182 | { |
62 | u32 *buf = addr; | 183 | u32 *buf = addr; |
@@ -73,25 +194,6 @@ u64 nd_fletcher64(void *addr, size_t len, bool le) | |||
73 | } | 194 | } |
74 | EXPORT_SYMBOL_GPL(nd_fletcher64); | 195 | EXPORT_SYMBOL_GPL(nd_fletcher64); |
75 | 196 | ||
76 | static void nvdimm_bus_release(struct device *dev) | ||
77 | { | ||
78 | struct nvdimm_bus *nvdimm_bus; | ||
79 | |||
80 | nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); | ||
81 | ida_simple_remove(&nd_ida, nvdimm_bus->id); | ||
82 | kfree(nvdimm_bus); | ||
83 | } | ||
84 | |||
85 | struct nvdimm_bus *to_nvdimm_bus(struct device *dev) | ||
86 | { | ||
87 | struct nvdimm_bus *nvdimm_bus; | ||
88 | |||
89 | nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); | ||
90 | WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release); | ||
91 | return nvdimm_bus; | ||
92 | } | ||
93 | EXPORT_SYMBOL_GPL(to_nvdimm_bus); | ||
94 | |||
95 | struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) | 197 | struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) |
96 | { | 198 | { |
97 | /* struct nvdimm_bus definition is private to libnvdimm */ | 199 | /* struct nvdimm_bus definition is private to libnvdimm */ |
@@ -99,18 +201,12 @@ struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) | |||
99 | } | 201 | } |
100 | EXPORT_SYMBOL_GPL(to_nd_desc); | 202 | EXPORT_SYMBOL_GPL(to_nd_desc); |
101 | 203 | ||
102 | struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) | 204 | struct device *to_nvdimm_bus_dev(struct nvdimm_bus *nvdimm_bus) |
103 | { | 205 | { |
104 | struct device *dev; | 206 | /* struct nvdimm_bus definition is private to libnvdimm */ |
105 | 207 | return &nvdimm_bus->dev; | |
106 | for (dev = nd_dev; dev; dev = dev->parent) | ||
107 | if (dev->release == nvdimm_bus_release) | ||
108 | break; | ||
109 | dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); | ||
110 | if (dev) | ||
111 | return to_nvdimm_bus(dev); | ||
112 | return NULL; | ||
113 | } | 208 | } |
209 | EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev); | ||
114 | 210 | ||
115 | static bool is_uuid_sep(char sep) | 211 | static bool is_uuid_sep(char sep) |
116 | { | 212 | { |
@@ -325,51 +421,6 @@ struct attribute_group nvdimm_bus_attribute_group = { | |||
325 | }; | 421 | }; |
326 | EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); | 422 | EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); |
327 | 423 | ||
328 | struct nvdimm_bus *__nvdimm_bus_register(struct device *parent, | ||
329 | struct nvdimm_bus_descriptor *nd_desc, struct module *module) | ||
330 | { | ||
331 | struct nvdimm_bus *nvdimm_bus; | ||
332 | int rc; | ||
333 | |||
334 | nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); | ||
335 | if (!nvdimm_bus) | ||
336 | return NULL; | ||
337 | INIT_LIST_HEAD(&nvdimm_bus->list); | ||
338 | INIT_LIST_HEAD(&nvdimm_bus->poison_list); | ||
339 | init_waitqueue_head(&nvdimm_bus->probe_wait); | ||
340 | nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); | ||
341 | mutex_init(&nvdimm_bus->reconfig_mutex); | ||
342 | if (nvdimm_bus->id < 0) { | ||
343 | kfree(nvdimm_bus); | ||
344 | return NULL; | ||
345 | } | ||
346 | nvdimm_bus->nd_desc = nd_desc; | ||
347 | nvdimm_bus->module = module; | ||
348 | nvdimm_bus->dev.parent = parent; | ||
349 | nvdimm_bus->dev.release = nvdimm_bus_release; | ||
350 | nvdimm_bus->dev.groups = nd_desc->attr_groups; | ||
351 | dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); | ||
352 | rc = device_register(&nvdimm_bus->dev); | ||
353 | if (rc) { | ||
354 | dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); | ||
355 | goto err; | ||
356 | } | ||
357 | |||
358 | rc = nvdimm_bus_create_ndctl(nvdimm_bus); | ||
359 | if (rc) | ||
360 | goto err; | ||
361 | |||
362 | mutex_lock(&nvdimm_bus_list_mutex); | ||
363 | list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list); | ||
364 | mutex_unlock(&nvdimm_bus_list_mutex); | ||
365 | |||
366 | return nvdimm_bus; | ||
367 | err: | ||
368 | put_device(&nvdimm_bus->dev); | ||
369 | return NULL; | ||
370 | } | ||
371 | EXPORT_SYMBOL_GPL(__nvdimm_bus_register); | ||
372 | |||
373 | static void set_badblock(struct badblocks *bb, sector_t s, int num) | 424 | static void set_badblock(struct badblocks *bb, sector_t s, int num) |
374 | { | 425 | { |
375 | dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", | 426 | dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", |
@@ -545,54 +596,6 @@ int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length) | |||
545 | } | 596 | } |
546 | EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); | 597 | EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); |
547 | 598 | ||
548 | static void free_poison_list(struct list_head *poison_list) | ||
549 | { | ||
550 | struct nd_poison *pl, *next; | ||
551 | |||
552 | list_for_each_entry_safe(pl, next, poison_list, list) { | ||
553 | list_del(&pl->list); | ||
554 | kfree(pl); | ||
555 | } | ||
556 | list_del_init(poison_list); | ||
557 | } | ||
558 | |||
559 | static int child_unregister(struct device *dev, void *data) | ||
560 | { | ||
561 | /* | ||
562 | * the singular ndctl class device per bus needs to be | ||
563 | * "device_destroy"ed, so skip it here | ||
564 | * | ||
565 | * i.e. remove classless children | ||
566 | */ | ||
567 | if (dev->class) | ||
568 | /* pass */; | ||
569 | else | ||
570 | nd_device_unregister(dev, ND_SYNC); | ||
571 | return 0; | ||
572 | } | ||
573 | |||
574 | void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) | ||
575 | { | ||
576 | if (!nvdimm_bus) | ||
577 | return; | ||
578 | |||
579 | mutex_lock(&nvdimm_bus_list_mutex); | ||
580 | list_del_init(&nvdimm_bus->list); | ||
581 | mutex_unlock(&nvdimm_bus_list_mutex); | ||
582 | |||
583 | nd_synchronize(); | ||
584 | device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); | ||
585 | |||
586 | nvdimm_bus_lock(&nvdimm_bus->dev); | ||
587 | free_poison_list(&nvdimm_bus->poison_list); | ||
588 | nvdimm_bus_unlock(&nvdimm_bus->dev); | ||
589 | |||
590 | nvdimm_bus_destroy_ndctl(nvdimm_bus); | ||
591 | |||
592 | device_unregister(&nvdimm_bus->dev); | ||
593 | } | ||
594 | EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); | ||
595 | |||
596 | #ifdef CONFIG_BLK_DEV_INTEGRITY | 599 | #ifdef CONFIG_BLK_DEV_INTEGRITY |
597 | int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) | 600 | int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) |
598 | { | 601 | { |
@@ -601,7 +604,8 @@ int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) | |||
601 | if (meta_size == 0) | 604 | if (meta_size == 0) |
602 | return 0; | 605 | return 0; |
603 | 606 | ||
604 | bi.profile = NULL; | 607 | memset(&bi, 0, sizeof(bi)); |
608 | |||
605 | bi.tuple_size = meta_size; | 609 | bi.tuple_size = meta_size; |
606 | bi.tag_size = meta_size; | 610 | bi.tag_size = meta_size; |
607 | 611 | ||
@@ -650,7 +654,6 @@ static __exit void libnvdimm_exit(void) | |||
650 | nvdimm_bus_exit(); | 654 | nvdimm_bus_exit(); |
651 | nd_region_devs_exit(); | 655 | nd_region_devs_exit(); |
652 | nvdimm_devs_exit(); | 656 | nvdimm_devs_exit(); |
653 | ida_destroy(&nd_ida); | ||
654 | } | 657 | } |
655 | 658 | ||
656 | MODULE_LICENSE("GPL v2"); | 659 | MODULE_LICENSE("GPL v2"); |