aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/nvdimm/core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/nvdimm/core.c')
-rw-r--r--drivers/nvdimm/core.c253
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
26LIST_HEAD(nvdimm_bus_list); 27LIST_HEAD(nvdimm_bus_list);
27DEFINE_MUTEX(nvdimm_bus_list_mutex); 28DEFINE_MUTEX(nvdimm_bus_list_mutex);
28static DEFINE_IDA(nd_ida);
29 29
30void nvdimm_bus_lock(struct device *dev) 30void nvdimm_bus_lock(struct device *dev)
31{ 31{
@@ -57,6 +57,127 @@ bool is_nvdimm_bus_locked(struct device *dev)
57} 57}
58EXPORT_SYMBOL(is_nvdimm_bus_locked); 58EXPORT_SYMBOL(is_nvdimm_bus_locked);
59 59
60struct 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
73static 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
85static 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
126static 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
144static 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 */
161void *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}
179EXPORT_SYMBOL_GPL(devm_nvdimm_memremap);
180
60u64 nd_fletcher64(void *addr, size_t len, bool le) 181u64 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}
74EXPORT_SYMBOL_GPL(nd_fletcher64); 195EXPORT_SYMBOL_GPL(nd_fletcher64);
75 196
76static 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
85struct 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}
93EXPORT_SYMBOL_GPL(to_nvdimm_bus);
94
95struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) 197struct 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}
100EXPORT_SYMBOL_GPL(to_nd_desc); 202EXPORT_SYMBOL_GPL(to_nd_desc);
101 203
102struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) 204struct 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}
209EXPORT_SYMBOL_GPL(to_nvdimm_bus_dev);
114 210
115static bool is_uuid_sep(char sep) 211static bool is_uuid_sep(char sep)
116{ 212{
@@ -325,51 +421,6 @@ struct attribute_group nvdimm_bus_attribute_group = {
325}; 421};
326EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); 422EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
327 423
328struct 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}
371EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
372
373static void set_badblock(struct badblocks *bb, sector_t s, int num) 424static 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}
546EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); 597EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison);
547 598
548static 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
559static 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
574void 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}
594EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
595
596#ifdef CONFIG_BLK_DEV_INTEGRITY 599#ifdef CONFIG_BLK_DEV_INTEGRITY
597int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) 600int 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
656MODULE_LICENSE("GPL v2"); 659MODULE_LICENSE("GPL v2");