aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-07-23 02:46:08 -0400
committerDan Williams <dan.j.williams@intel.com>2016-07-23 14:06:33 -0400
commit18515942d61bdfd4b31ea13f9fbb9c18650c6818 (patch)
tree6ae9bcd75578fbe43d3c07df9408acf2c78e16a8
parent5bf0b6e1af98a012e8871a89c2082353c3f70fee (diff)
libnvdimm: register nvdimm_bus devices with an nd_bus driver
A recent effort to add a new nvdimm bus provider attribute highlighted a race between interrogating nvdimm_bus->nd_desc and nvdimm_bus tear down. The typical way to handle these races is to take the device_lock() in the attribute method and validate that the device is still active. In order for a device to be 'active' it needs to be associated with a driver. So, we create the small boilerplate for a driver and register nvdimm_bus devices on the 'nvdimm_bus_type' bus. A result of this change is that ndbusX devices now appear under /sys/bus/nd/devices. In fact this makes /sys/class/nd somewhat redundant, but removing that will need to take a long deprecation period given its use by ndctl binaries in the field. This change naturally pulls code from drivers/nvdimm/core.c to drivers/nvdimm/bus.c, so it is a nice code organization clean-up as well. Cc: Vishal Verma <vishal.l.verma@intel.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r--drivers/nvdimm/bus.c188
-rw-r--r--drivers/nvdimm/core.c127
2 files changed, 181 insertions, 134 deletions
diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c
index 275dd5c0a301..46d7e555b044 100644
--- a/drivers/nvdimm/bus.c
+++ b/drivers/nvdimm/bus.c
@@ -31,6 +31,7 @@
31int nvdimm_major; 31int nvdimm_major;
32static int nvdimm_bus_major; 32static int nvdimm_bus_major;
33static struct class *nd_class; 33static struct class *nd_class;
34static DEFINE_IDA(nd_ida);
34 35
35static int to_nd_device_type(struct device *dev) 36static int to_nd_device_type(struct device *dev)
36{ 37{
@@ -60,13 +61,6 @@ static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
60 to_nd_device_type(dev)); 61 to_nd_device_type(dev));
61} 62}
62 63
63static int nvdimm_bus_match(struct device *dev, struct device_driver *drv)
64{
65 struct nd_device_driver *nd_drv = to_nd_device_driver(drv);
66
67 return !!test_bit(to_nd_device_type(dev), &nd_drv->type);
68}
69
70static struct module *to_bus_provider(struct device *dev) 64static struct module *to_bus_provider(struct device *dev)
71{ 65{
72 /* pin bus providers while regions are enabled */ 66 /* pin bus providers while regions are enabled */
@@ -223,6 +217,8 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
223} 217}
224EXPORT_SYMBOL_GPL(nvdimm_clear_poison); 218EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
225 219
220static int nvdimm_bus_match(struct device *dev, struct device_driver *drv);
221
226static struct bus_type nvdimm_bus_type = { 222static struct bus_type nvdimm_bus_type = {
227 .name = "nd", 223 .name = "nd",
228 .uevent = nvdimm_bus_uevent, 224 .uevent = nvdimm_bus_uevent,
@@ -232,6 +228,176 @@ static struct bus_type nvdimm_bus_type = {
232 .shutdown = nvdimm_bus_shutdown, 228 .shutdown = nvdimm_bus_shutdown,
233}; 229};
234 230
231static void nvdimm_bus_release(struct device *dev)
232{
233 struct nvdimm_bus *nvdimm_bus;
234
235 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
236 ida_simple_remove(&nd_ida, nvdimm_bus->id);
237 kfree(nvdimm_bus);
238}
239
240static bool is_nvdimm_bus(struct device *dev)
241{
242 return dev->release == nvdimm_bus_release;
243}
244
245struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
246{
247 struct device *dev;
248
249 for (dev = nd_dev; dev; dev = dev->parent)
250 if (is_nvdimm_bus(dev))
251 break;
252 dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n");
253 if (dev)
254 return to_nvdimm_bus(dev);
255 return NULL;
256}
257
258struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
259{
260 struct nvdimm_bus *nvdimm_bus;
261
262 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
263 WARN_ON(!is_nvdimm_bus(dev));
264 return nvdimm_bus;
265}
266EXPORT_SYMBOL_GPL(to_nvdimm_bus);
267
268struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
269 struct nvdimm_bus_descriptor *nd_desc)
270{
271 struct nvdimm_bus *nvdimm_bus;
272 int rc;
273
274 nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
275 if (!nvdimm_bus)
276 return NULL;
277 INIT_LIST_HEAD(&nvdimm_bus->list);
278 INIT_LIST_HEAD(&nvdimm_bus->mapping_list);
279 INIT_LIST_HEAD(&nvdimm_bus->poison_list);
280 init_waitqueue_head(&nvdimm_bus->probe_wait);
281 nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
282 mutex_init(&nvdimm_bus->reconfig_mutex);
283 if (nvdimm_bus->id < 0) {
284 kfree(nvdimm_bus);
285 return NULL;
286 }
287 nvdimm_bus->nd_desc = nd_desc;
288 nvdimm_bus->dev.parent = parent;
289 nvdimm_bus->dev.release = nvdimm_bus_release;
290 nvdimm_bus->dev.groups = nd_desc->attr_groups;
291 nvdimm_bus->dev.bus = &nvdimm_bus_type;
292 dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
293 rc = device_register(&nvdimm_bus->dev);
294 if (rc) {
295 dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc);
296 goto err;
297 }
298
299 return nvdimm_bus;
300 err:
301 put_device(&nvdimm_bus->dev);
302 return NULL;
303}
304EXPORT_SYMBOL_GPL(nvdimm_bus_register);
305
306void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
307{
308 if (!nvdimm_bus)
309 return;
310 device_unregister(&nvdimm_bus->dev);
311}
312EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
313
314static int child_unregister(struct device *dev, void *data)
315{
316 /*
317 * the singular ndctl class device per bus needs to be
318 * "device_destroy"ed, so skip it here
319 *
320 * i.e. remove classless children
321 */
322 if (dev->class)
323 /* pass */;
324 else
325 nd_device_unregister(dev, ND_SYNC);
326 return 0;
327}
328
329static void free_poison_list(struct list_head *poison_list)
330{
331 struct nd_poison *pl, *next;
332
333 list_for_each_entry_safe(pl, next, poison_list, list) {
334 list_del(&pl->list);
335 kfree(pl);
336 }
337 list_del_init(poison_list);
338}
339
340static int nd_bus_remove(struct device *dev)
341{
342 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
343
344 mutex_lock(&nvdimm_bus_list_mutex);
345 list_del_init(&nvdimm_bus->list);
346 mutex_unlock(&nvdimm_bus_list_mutex);
347
348 nd_synchronize();
349 device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
350
351 nvdimm_bus_lock(&nvdimm_bus->dev);
352 free_poison_list(&nvdimm_bus->poison_list);
353 nvdimm_bus_unlock(&nvdimm_bus->dev);
354
355 nvdimm_bus_destroy_ndctl(nvdimm_bus);
356
357 return 0;
358}
359
360static int nd_bus_probe(struct device *dev)
361{
362 struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
363 int rc;
364
365 rc = nvdimm_bus_create_ndctl(nvdimm_bus);
366 if (rc)
367 return rc;
368
369 mutex_lock(&nvdimm_bus_list_mutex);
370 list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
371 mutex_unlock(&nvdimm_bus_list_mutex);
372
373 /* enable bus provider attributes to look up their local context */
374 dev_set_drvdata(dev, nvdimm_bus->nd_desc);
375
376 return 0;
377}
378
379static struct nd_device_driver nd_bus_driver = {
380 .probe = nd_bus_probe,
381 .remove = nd_bus_remove,
382 .drv = {
383 .name = "nd_bus",
384 .suppress_bind_attrs = true,
385 .bus = &nvdimm_bus_type,
386 .owner = THIS_MODULE,
387 .mod_name = KBUILD_MODNAME,
388 },
389};
390
391static int nvdimm_bus_match(struct device *dev, struct device_driver *drv)
392{
393 struct nd_device_driver *nd_drv = to_nd_device_driver(drv);
394
395 if (is_nvdimm_bus(dev) && nd_drv == &nd_bus_driver)
396 return true;
397
398 return !!test_bit(to_nd_device_type(dev), &nd_drv->type);
399}
400
235static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 401static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain);
236 402
237void nd_synchronize(void) 403void nd_synchronize(void)
@@ -864,8 +1030,14 @@ int __init nvdimm_bus_init(void)
864 goto err_class; 1030 goto err_class;
865 } 1031 }
866 1032
1033 rc = driver_register(&nd_bus_driver.drv);
1034 if (rc)
1035 goto err_nd_bus;
1036
867 return 0; 1037 return 0;
868 1038
1039 err_nd_bus:
1040 class_destroy(nd_class);
869 err_class: 1041 err_class:
870 unregister_chrdev(nvdimm_major, "dimmctl"); 1042 unregister_chrdev(nvdimm_major, "dimmctl");
871 err_dimm_chrdev: 1043 err_dimm_chrdev:
@@ -878,8 +1050,10 @@ int __init nvdimm_bus_init(void)
878 1050
879void nvdimm_bus_exit(void) 1051void nvdimm_bus_exit(void)
880{ 1052{
1053 driver_unregister(&nd_bus_driver.drv);
881 class_destroy(nd_class); 1054 class_destroy(nd_class);
882 unregister_chrdev(nvdimm_bus_major, "ndctl"); 1055 unregister_chrdev(nvdimm_bus_major, "ndctl");
883 unregister_chrdev(nvdimm_major, "dimmctl"); 1056 unregister_chrdev(nvdimm_major, "dimmctl");
884 bus_unregister(&nvdimm_bus_type); 1057 bus_unregister(&nvdimm_bus_type);
1058 ida_destroy(&nd_ida);
885} 1059}
diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c
index e8528756f54f..2c98f958fabb 100644
--- a/drivers/nvdimm/core.c
+++ b/drivers/nvdimm/core.c
@@ -26,7 +26,6 @@
26 26
27LIST_HEAD(nvdimm_bus_list); 27LIST_HEAD(nvdimm_bus_list);
28DEFINE_MUTEX(nvdimm_bus_list_mutex); 28DEFINE_MUTEX(nvdimm_bus_list_mutex);
29static DEFINE_IDA(nd_ida);
30 29
31void nvdimm_bus_lock(struct device *dev) 30void nvdimm_bus_lock(struct device *dev)
32{ 31{
@@ -195,25 +194,6 @@ u64 nd_fletcher64(void *addr, size_t len, bool le)
195} 194}
196EXPORT_SYMBOL_GPL(nd_fletcher64); 195EXPORT_SYMBOL_GPL(nd_fletcher64);
197 196
198static void nvdimm_bus_release(struct device *dev)
199{
200 struct nvdimm_bus *nvdimm_bus;
201
202 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
203 ida_simple_remove(&nd_ida, nvdimm_bus->id);
204 kfree(nvdimm_bus);
205}
206
207struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
208{
209 struct nvdimm_bus *nvdimm_bus;
210
211 nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
212 WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release);
213 return nvdimm_bus;
214}
215EXPORT_SYMBOL_GPL(to_nvdimm_bus);
216
217struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) 197struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
218{ 198{
219 /* struct nvdimm_bus definition is private to libnvdimm */ 199 /* struct nvdimm_bus definition is private to libnvdimm */
@@ -221,19 +201,6 @@ struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
221} 201}
222EXPORT_SYMBOL_GPL(to_nd_desc); 202EXPORT_SYMBOL_GPL(to_nd_desc);
223 203
224struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
225{
226 struct device *dev;
227
228 for (dev = nd_dev; dev; dev = dev->parent)
229 if (dev->release == nvdimm_bus_release)
230 break;
231 dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n");
232 if (dev)
233 return to_nvdimm_bus(dev);
234 return NULL;
235}
236
237static bool is_uuid_sep(char sep) 204static bool is_uuid_sep(char sep)
238{ 205{
239 if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0') 206 if (sep == '\n' || sep == '-' || sep == ':' || sep == '\0')
@@ -447,51 +414,6 @@ struct attribute_group nvdimm_bus_attribute_group = {
447}; 414};
448EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); 415EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
449 416
450struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
451 struct nvdimm_bus_descriptor *nd_desc)
452{
453 struct nvdimm_bus *nvdimm_bus;
454 int rc;
455
456 nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
457 if (!nvdimm_bus)
458 return NULL;
459 INIT_LIST_HEAD(&nvdimm_bus->list);
460 INIT_LIST_HEAD(&nvdimm_bus->mapping_list);
461 INIT_LIST_HEAD(&nvdimm_bus->poison_list);
462 init_waitqueue_head(&nvdimm_bus->probe_wait);
463 nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
464 mutex_init(&nvdimm_bus->reconfig_mutex);
465 if (nvdimm_bus->id < 0) {
466 kfree(nvdimm_bus);
467 return NULL;
468 }
469 nvdimm_bus->nd_desc = nd_desc;
470 nvdimm_bus->dev.parent = parent;
471 nvdimm_bus->dev.release = nvdimm_bus_release;
472 nvdimm_bus->dev.groups = nd_desc->attr_groups;
473 dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
474 rc = device_register(&nvdimm_bus->dev);
475 if (rc) {
476 dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc);
477 goto err;
478 }
479
480 rc = nvdimm_bus_create_ndctl(nvdimm_bus);
481 if (rc)
482 goto err;
483
484 mutex_lock(&nvdimm_bus_list_mutex);
485 list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
486 mutex_unlock(&nvdimm_bus_list_mutex);
487
488 return nvdimm_bus;
489 err:
490 put_device(&nvdimm_bus->dev);
491 return NULL;
492}
493EXPORT_SYMBOL_GPL(nvdimm_bus_register);
494
495static void set_badblock(struct badblocks *bb, sector_t s, int num) 417static void set_badblock(struct badblocks *bb, sector_t s, int num)
496{ 418{
497 dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", 419 dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n",
@@ -667,54 +589,6 @@ int nvdimm_bus_add_poison(struct nvdimm_bus *nvdimm_bus, u64 addr, u64 length)
667} 589}
668EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); 590EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison);
669 591
670static void free_poison_list(struct list_head *poison_list)
671{
672 struct nd_poison *pl, *next;
673
674 list_for_each_entry_safe(pl, next, poison_list, list) {
675 list_del(&pl->list);
676 kfree(pl);
677 }
678 list_del_init(poison_list);
679}
680
681static int child_unregister(struct device *dev, void *data)
682{
683 /*
684 * the singular ndctl class device per bus needs to be
685 * "device_destroy"ed, so skip it here
686 *
687 * i.e. remove classless children
688 */
689 if (dev->class)
690 /* pass */;
691 else
692 nd_device_unregister(dev, ND_SYNC);
693 return 0;
694}
695
696void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
697{
698 if (!nvdimm_bus)
699 return;
700
701 mutex_lock(&nvdimm_bus_list_mutex);
702 list_del_init(&nvdimm_bus->list);
703 mutex_unlock(&nvdimm_bus_list_mutex);
704
705 nd_synchronize();
706 device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
707
708 nvdimm_bus_lock(&nvdimm_bus->dev);
709 free_poison_list(&nvdimm_bus->poison_list);
710 nvdimm_bus_unlock(&nvdimm_bus->dev);
711
712 nvdimm_bus_destroy_ndctl(nvdimm_bus);
713
714 device_unregister(&nvdimm_bus->dev);
715}
716EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
717
718#ifdef CONFIG_BLK_DEV_INTEGRITY 592#ifdef CONFIG_BLK_DEV_INTEGRITY
719int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) 593int nd_integrity_init(struct gendisk *disk, unsigned long meta_size)
720{ 594{
@@ -773,7 +647,6 @@ static __exit void libnvdimm_exit(void)
773 nvdimm_bus_exit(); 647 nvdimm_bus_exit();
774 nd_region_devs_exit(); 648 nd_region_devs_exit();
775 nvdimm_devs_exit(); 649 nvdimm_devs_exit();
776 ida_destroy(&nd_ida);
777} 650}
778 651
779MODULE_LICENSE("GPL v2"); 652MODULE_LICENSE("GPL v2");