libnvdimm: support for legacy (non-aliasing) nvdimms
[firefly-linux-kernel-4.4.55.git] / drivers / nvdimm / core.c
1 /*
2  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of version 2 of the GNU General Public License as
6  * published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * General Public License for more details.
12  */
13 #include <linux/libnvdimm.h>
14 #include <linux/export.h>
15 #include <linux/module.h>
16 #include <linux/device.h>
17 #include <linux/ndctl.h>
18 #include <linux/mutex.h>
19 #include <linux/slab.h>
20 #include "nd-core.h"
21 #include "nd.h"
22
23 LIST_HEAD(nvdimm_bus_list);
24 DEFINE_MUTEX(nvdimm_bus_list_mutex);
25 static DEFINE_IDA(nd_ida);
26
27 void nvdimm_bus_lock(struct device *dev)
28 {
29         struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
30
31         if (!nvdimm_bus)
32                 return;
33         mutex_lock(&nvdimm_bus->reconfig_mutex);
34 }
35 EXPORT_SYMBOL(nvdimm_bus_lock);
36
37 void nvdimm_bus_unlock(struct device *dev)
38 {
39         struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
40
41         if (!nvdimm_bus)
42                 return;
43         mutex_unlock(&nvdimm_bus->reconfig_mutex);
44 }
45 EXPORT_SYMBOL(nvdimm_bus_unlock);
46
47 bool is_nvdimm_bus_locked(struct device *dev)
48 {
49         struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
50
51         if (!nvdimm_bus)
52                 return false;
53         return mutex_is_locked(&nvdimm_bus->reconfig_mutex);
54 }
55 EXPORT_SYMBOL(is_nvdimm_bus_locked);
56
57 static void nvdimm_bus_release(struct device *dev)
58 {
59         struct nvdimm_bus *nvdimm_bus;
60
61         nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
62         ida_simple_remove(&nd_ida, nvdimm_bus->id);
63         kfree(nvdimm_bus);
64 }
65
66 struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
67 {
68         struct nvdimm_bus *nvdimm_bus;
69
70         nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
71         WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release);
72         return nvdimm_bus;
73 }
74 EXPORT_SYMBOL_GPL(to_nvdimm_bus);
75
76 struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
77 {
78         /* struct nvdimm_bus definition is private to libnvdimm */
79         return nvdimm_bus->nd_desc;
80 }
81 EXPORT_SYMBOL_GPL(to_nd_desc);
82
83 struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
84 {
85         struct device *dev;
86
87         for (dev = nd_dev; dev; dev = dev->parent)
88                 if (dev->release == nvdimm_bus_release)
89                         break;
90         dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n");
91         if (dev)
92                 return to_nvdimm_bus(dev);
93         return NULL;
94 }
95
96 static ssize_t commands_show(struct device *dev,
97                 struct device_attribute *attr, char *buf)
98 {
99         int cmd, len = 0;
100         struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
101         struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
102
103         for_each_set_bit(cmd, &nd_desc->dsm_mask, BITS_PER_LONG)
104                 len += sprintf(buf + len, "%s ", nvdimm_bus_cmd_name(cmd));
105         len += sprintf(buf + len, "\n");
106         return len;
107 }
108 static DEVICE_ATTR_RO(commands);
109
110 static const char *nvdimm_bus_provider(struct nvdimm_bus *nvdimm_bus)
111 {
112         struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
113         struct device *parent = nvdimm_bus->dev.parent;
114
115         if (nd_desc->provider_name)
116                 return nd_desc->provider_name;
117         else if (parent)
118                 return dev_name(parent);
119         else
120                 return "unknown";
121 }
122
123 static ssize_t provider_show(struct device *dev,
124                 struct device_attribute *attr, char *buf)
125 {
126         struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
127
128         return sprintf(buf, "%s\n", nvdimm_bus_provider(nvdimm_bus));
129 }
130 static DEVICE_ATTR_RO(provider);
131
132 static int flush_namespaces(struct device *dev, void *data)
133 {
134         device_lock(dev);
135         device_unlock(dev);
136         return 0;
137 }
138
139 static int flush_regions_dimms(struct device *dev, void *data)
140 {
141         device_lock(dev);
142         device_unlock(dev);
143         device_for_each_child(dev, NULL, flush_namespaces);
144         return 0;
145 }
146
147 static ssize_t wait_probe_show(struct device *dev,
148                 struct device_attribute *attr, char *buf)
149 {
150         nd_synchronize();
151         device_for_each_child(dev, NULL, flush_regions_dimms);
152         return sprintf(buf, "1\n");
153 }
154 static DEVICE_ATTR_RO(wait_probe);
155
156 static struct attribute *nvdimm_bus_attributes[] = {
157         &dev_attr_commands.attr,
158         &dev_attr_wait_probe.attr,
159         &dev_attr_provider.attr,
160         NULL,
161 };
162
163 struct attribute_group nvdimm_bus_attribute_group = {
164         .attrs = nvdimm_bus_attributes,
165 };
166 EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
167
168 struct nvdimm_bus *__nvdimm_bus_register(struct device *parent,
169                 struct nvdimm_bus_descriptor *nd_desc, struct module *module)
170 {
171         struct nvdimm_bus *nvdimm_bus;
172         int rc;
173
174         nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
175         if (!nvdimm_bus)
176                 return NULL;
177         INIT_LIST_HEAD(&nvdimm_bus->list);
178         nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
179         mutex_init(&nvdimm_bus->reconfig_mutex);
180         if (nvdimm_bus->id < 0) {
181                 kfree(nvdimm_bus);
182                 return NULL;
183         }
184         nvdimm_bus->nd_desc = nd_desc;
185         nvdimm_bus->module = module;
186         nvdimm_bus->dev.parent = parent;
187         nvdimm_bus->dev.release = nvdimm_bus_release;
188         nvdimm_bus->dev.groups = nd_desc->attr_groups;
189         dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
190         rc = device_register(&nvdimm_bus->dev);
191         if (rc) {
192                 dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc);
193                 goto err;
194         }
195
196         rc = nvdimm_bus_create_ndctl(nvdimm_bus);
197         if (rc)
198                 goto err;
199
200         mutex_lock(&nvdimm_bus_list_mutex);
201         list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
202         mutex_unlock(&nvdimm_bus_list_mutex);
203
204         return nvdimm_bus;
205  err:
206         put_device(&nvdimm_bus->dev);
207         return NULL;
208 }
209 EXPORT_SYMBOL_GPL(__nvdimm_bus_register);
210
211 static int child_unregister(struct device *dev, void *data)
212 {
213         /*
214          * the singular ndctl class device per bus needs to be
215          * "device_destroy"ed, so skip it here
216          *
217          * i.e. remove classless children
218          */
219         if (dev->class)
220                 /* pass */;
221         else
222                 nd_device_unregister(dev, ND_SYNC);
223         return 0;
224 }
225
226 void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
227 {
228         if (!nvdimm_bus)
229                 return;
230
231         mutex_lock(&nvdimm_bus_list_mutex);
232         list_del_init(&nvdimm_bus->list);
233         mutex_unlock(&nvdimm_bus_list_mutex);
234
235         nd_synchronize();
236         device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
237         nvdimm_bus_destroy_ndctl(nvdimm_bus);
238
239         device_unregister(&nvdimm_bus->dev);
240 }
241 EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
242
243 static __init int libnvdimm_init(void)
244 {
245         int rc;
246
247         rc = nvdimm_bus_init();
248         if (rc)
249                 return rc;
250         rc = nvdimm_init();
251         if (rc)
252                 goto err_dimm;
253         rc = nd_region_init();
254         if (rc)
255                 goto err_region;
256         return 0;
257  err_region:
258         nvdimm_exit();
259  err_dimm:
260         nvdimm_bus_exit();
261         return rc;
262 }
263
264 static __exit void libnvdimm_exit(void)
265 {
266         WARN_ON(!list_empty(&nvdimm_bus_list));
267         nd_region_exit();
268         nvdimm_exit();
269         nvdimm_bus_exit();
270 }
271
272 MODULE_LICENSE("GPL v2");
273 MODULE_AUTHOR("Intel Corporation");
274 subsys_initcall(libnvdimm_init);
275 module_exit(libnvdimm_exit);