Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+181 -134
+181 -7
drivers/nvdimm/bus.c
··· 31 31 int nvdimm_major; 32 32 static int nvdimm_bus_major; 33 33 static struct class *nd_class; 34 + static DEFINE_IDA(nd_ida); 34 35 35 36 static int to_nd_device_type(struct device *dev) 36 37 { ··· 59 58 set_dev_node(dev, to_nd_region(dev)->numa_node); 60 59 return add_uevent_var(env, "MODALIAS=" ND_DEVICE_MODALIAS_FMT, 61 60 to_nd_device_type(dev)); 62 - } 63 - 64 - static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 65 - { 66 - struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 67 - 68 - return !!test_bit(to_nd_device_type(dev), &nd_drv->type); 69 61 } 70 62 71 63 static struct module *to_bus_provider(struct device *dev) ··· 217 223 } 218 224 EXPORT_SYMBOL_GPL(nvdimm_clear_poison); 219 225 226 + static int nvdimm_bus_match(struct device *dev, struct device_driver *drv); 227 + 220 228 static struct bus_type nvdimm_bus_type = { 221 229 .name = "nd", 222 230 .uevent = nvdimm_bus_uevent, ··· 227 231 .remove = nvdimm_bus_remove, 228 232 .shutdown = nvdimm_bus_shutdown, 229 233 }; 234 + 235 + static void nvdimm_bus_release(struct device *dev) 236 + { 237 + struct nvdimm_bus *nvdimm_bus; 238 + 239 + nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 240 + ida_simple_remove(&nd_ida, nvdimm_bus->id); 241 + kfree(nvdimm_bus); 242 + } 243 + 244 + static bool is_nvdimm_bus(struct device *dev) 245 + { 246 + return dev->release == nvdimm_bus_release; 247 + } 248 + 249 + struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) 250 + { 251 + struct device *dev; 252 + 253 + for (dev = nd_dev; dev; dev = dev->parent) 254 + if (is_nvdimm_bus(dev)) 255 + break; 256 + dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); 257 + if (dev) 258 + return to_nvdimm_bus(dev); 259 + return NULL; 260 + } 261 + 262 + struct nvdimm_bus *to_nvdimm_bus(struct device *dev) 263 + { 264 + struct nvdimm_bus *nvdimm_bus; 265 + 266 + nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 267 + WARN_ON(!is_nvdimm_bus(dev)); 268 + return nvdimm_bus; 269 + } 270 + EXPORT_SYMBOL_GPL(to_nvdimm_bus); 271 + 272 + struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 273 + struct nvdimm_bus_descriptor *nd_desc) 274 + { 275 + struct nvdimm_bus *nvdimm_bus; 276 + int rc; 277 + 278 + nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); 279 + if (!nvdimm_bus) 280 + return NULL; 281 + INIT_LIST_HEAD(&nvdimm_bus->list); 282 + INIT_LIST_HEAD(&nvdimm_bus->mapping_list); 283 + INIT_LIST_HEAD(&nvdimm_bus->poison_list); 284 + init_waitqueue_head(&nvdimm_bus->probe_wait); 285 + nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 286 + mutex_init(&nvdimm_bus->reconfig_mutex); 287 + if (nvdimm_bus->id < 0) { 288 + kfree(nvdimm_bus); 289 + return NULL; 290 + } 291 + nvdimm_bus->nd_desc = nd_desc; 292 + nvdimm_bus->dev.parent = parent; 293 + nvdimm_bus->dev.release = nvdimm_bus_release; 294 + nvdimm_bus->dev.groups = nd_desc->attr_groups; 295 + nvdimm_bus->dev.bus = &nvdimm_bus_type; 296 + dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); 297 + rc = device_register(&nvdimm_bus->dev); 298 + if (rc) { 299 + dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); 300 + goto err; 301 + } 302 + 303 + return nvdimm_bus; 304 + err: 305 + put_device(&nvdimm_bus->dev); 306 + return NULL; 307 + } 308 + EXPORT_SYMBOL_GPL(nvdimm_bus_register); 309 + 310 + void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) 311 + { 312 + if (!nvdimm_bus) 313 + return; 314 + device_unregister(&nvdimm_bus->dev); 315 + } 316 + EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); 317 + 318 + static int child_unregister(struct device *dev, void *data) 319 + { 320 + /* 321 + * the singular ndctl class device per bus needs to be 322 + * "device_destroy"ed, so skip it here 323 + * 324 + * i.e. remove classless children 325 + */ 326 + if (dev->class) 327 + /* pass */; 328 + else 329 + nd_device_unregister(dev, ND_SYNC); 330 + return 0; 331 + } 332 + 333 + static void free_poison_list(struct list_head *poison_list) 334 + { 335 + struct nd_poison *pl, *next; 336 + 337 + list_for_each_entry_safe(pl, next, poison_list, list) { 338 + list_del(&pl->list); 339 + kfree(pl); 340 + } 341 + list_del_init(poison_list); 342 + } 343 + 344 + static int nd_bus_remove(struct device *dev) 345 + { 346 + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 347 + 348 + mutex_lock(&nvdimm_bus_list_mutex); 349 + list_del_init(&nvdimm_bus->list); 350 + mutex_unlock(&nvdimm_bus_list_mutex); 351 + 352 + nd_synchronize(); 353 + device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); 354 + 355 + nvdimm_bus_lock(&nvdimm_bus->dev); 356 + free_poison_list(&nvdimm_bus->poison_list); 357 + nvdimm_bus_unlock(&nvdimm_bus->dev); 358 + 359 + nvdimm_bus_destroy_ndctl(nvdimm_bus); 360 + 361 + return 0; 362 + } 363 + 364 + static int nd_bus_probe(struct device *dev) 365 + { 366 + struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev); 367 + int rc; 368 + 369 + rc = nvdimm_bus_create_ndctl(nvdimm_bus); 370 + if (rc) 371 + return rc; 372 + 373 + mutex_lock(&nvdimm_bus_list_mutex); 374 + list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list); 375 + mutex_unlock(&nvdimm_bus_list_mutex); 376 + 377 + /* enable bus provider attributes to look up their local context */ 378 + dev_set_drvdata(dev, nvdimm_bus->nd_desc); 379 + 380 + return 0; 381 + } 382 + 383 + static struct nd_device_driver nd_bus_driver = { 384 + .probe = nd_bus_probe, 385 + .remove = nd_bus_remove, 386 + .drv = { 387 + .name = "nd_bus", 388 + .suppress_bind_attrs = true, 389 + .bus = &nvdimm_bus_type, 390 + .owner = THIS_MODULE, 391 + .mod_name = KBUILD_MODNAME, 392 + }, 393 + }; 394 + 395 + static int nvdimm_bus_match(struct device *dev, struct device_driver *drv) 396 + { 397 + struct nd_device_driver *nd_drv = to_nd_device_driver(drv); 398 + 399 + if (is_nvdimm_bus(dev) && nd_drv == &nd_bus_driver) 400 + return true; 401 + 402 + return !!test_bit(to_nd_device_type(dev), &nd_drv->type); 403 + } 230 404 231 405 static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain); 232 406 ··· 1030 864 goto err_class; 1031 865 } 1032 866 867 + rc = driver_register(&nd_bus_driver.drv); 868 + if (rc) 869 + goto err_nd_bus; 870 + 1033 871 return 0; 1034 872 873 + err_nd_bus: 874 + class_destroy(nd_class); 1035 875 err_class: 1036 876 unregister_chrdev(nvdimm_major, "dimmctl"); 1037 877 err_dimm_chrdev: ··· 1050 878 1051 879 void nvdimm_bus_exit(void) 1052 880 { 881 + driver_unregister(&nd_bus_driver.drv); 1053 882 class_destroy(nd_class); 1054 883 unregister_chrdev(nvdimm_bus_major, "ndctl"); 1055 884 unregister_chrdev(nvdimm_major, "dimmctl"); 1056 885 bus_unregister(&nvdimm_bus_type); 886 + ida_destroy(&nd_ida); 1057 887 }
-127
drivers/nvdimm/core.c
··· 26 26 27 27 LIST_HEAD(nvdimm_bus_list); 28 28 DEFINE_MUTEX(nvdimm_bus_list_mutex); 29 - static DEFINE_IDA(nd_ida); 30 29 31 30 void nvdimm_bus_lock(struct device *dev) 32 31 { ··· 194 195 } 195 196 EXPORT_SYMBOL_GPL(nd_fletcher64); 196 197 197 - static void nvdimm_bus_release(struct device *dev) 198 - { 199 - struct nvdimm_bus *nvdimm_bus; 200 - 201 - nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 202 - ida_simple_remove(&nd_ida, nvdimm_bus->id); 203 - kfree(nvdimm_bus); 204 - } 205 - 206 - struct nvdimm_bus *to_nvdimm_bus(struct device *dev) 207 - { 208 - struct nvdimm_bus *nvdimm_bus; 209 - 210 - nvdimm_bus = container_of(dev, struct nvdimm_bus, dev); 211 - WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release); 212 - return nvdimm_bus; 213 - } 214 - EXPORT_SYMBOL_GPL(to_nvdimm_bus); 215 - 216 198 struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) 217 199 { 218 200 /* struct nvdimm_bus definition is private to libnvdimm */ 219 201 return nvdimm_bus->nd_desc; 220 202 } 221 203 EXPORT_SYMBOL_GPL(to_nd_desc); 222 - 223 - struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) 224 - { 225 - struct device *dev; 226 - 227 - for (dev = nd_dev; dev; dev = dev->parent) 228 - if (dev->release == nvdimm_bus_release) 229 - break; 230 - dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); 231 - if (dev) 232 - return to_nvdimm_bus(dev); 233 - return NULL; 234 - } 235 204 236 205 static bool is_uuid_sep(char sep) 237 206 { ··· 414 447 }; 415 448 EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group); 416 449 417 - struct nvdimm_bus *nvdimm_bus_register(struct device *parent, 418 - struct nvdimm_bus_descriptor *nd_desc) 419 - { 420 - struct nvdimm_bus *nvdimm_bus; 421 - int rc; 422 - 423 - nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL); 424 - if (!nvdimm_bus) 425 - return NULL; 426 - INIT_LIST_HEAD(&nvdimm_bus->list); 427 - INIT_LIST_HEAD(&nvdimm_bus->mapping_list); 428 - INIT_LIST_HEAD(&nvdimm_bus->poison_list); 429 - init_waitqueue_head(&nvdimm_bus->probe_wait); 430 - nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL); 431 - mutex_init(&nvdimm_bus->reconfig_mutex); 432 - if (nvdimm_bus->id < 0) { 433 - kfree(nvdimm_bus); 434 - return NULL; 435 - } 436 - nvdimm_bus->nd_desc = nd_desc; 437 - nvdimm_bus->dev.parent = parent; 438 - nvdimm_bus->dev.release = nvdimm_bus_release; 439 - nvdimm_bus->dev.groups = nd_desc->attr_groups; 440 - dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id); 441 - rc = device_register(&nvdimm_bus->dev); 442 - if (rc) { 443 - dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc); 444 - goto err; 445 - } 446 - 447 - rc = nvdimm_bus_create_ndctl(nvdimm_bus); 448 - if (rc) 449 - goto err; 450 - 451 - mutex_lock(&nvdimm_bus_list_mutex); 452 - list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list); 453 - mutex_unlock(&nvdimm_bus_list_mutex); 454 - 455 - return nvdimm_bus; 456 - err: 457 - put_device(&nvdimm_bus->dev); 458 - return NULL; 459 - } 460 - EXPORT_SYMBOL_GPL(nvdimm_bus_register); 461 - 462 450 static void set_badblock(struct badblocks *bb, sector_t s, int num) 463 451 { 464 452 dev_dbg(bb->dev, "Found a poison range (0x%llx, 0x%llx)\n", ··· 589 667 } 590 668 EXPORT_SYMBOL_GPL(nvdimm_bus_add_poison); 591 669 592 - static void free_poison_list(struct list_head *poison_list) 593 - { 594 - struct nd_poison *pl, *next; 595 - 596 - list_for_each_entry_safe(pl, next, poison_list, list) { 597 - list_del(&pl->list); 598 - kfree(pl); 599 - } 600 - list_del_init(poison_list); 601 - } 602 - 603 - static int child_unregister(struct device *dev, void *data) 604 - { 605 - /* 606 - * the singular ndctl class device per bus needs to be 607 - * "device_destroy"ed, so skip it here 608 - * 609 - * i.e. remove classless children 610 - */ 611 - if (dev->class) 612 - /* pass */; 613 - else 614 - nd_device_unregister(dev, ND_SYNC); 615 - return 0; 616 - } 617 - 618 - void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) 619 - { 620 - if (!nvdimm_bus) 621 - return; 622 - 623 - mutex_lock(&nvdimm_bus_list_mutex); 624 - list_del_init(&nvdimm_bus->list); 625 - mutex_unlock(&nvdimm_bus_list_mutex); 626 - 627 - nd_synchronize(); 628 - device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); 629 - 630 - nvdimm_bus_lock(&nvdimm_bus->dev); 631 - free_poison_list(&nvdimm_bus->poison_list); 632 - nvdimm_bus_unlock(&nvdimm_bus->dev); 633 - 634 - nvdimm_bus_destroy_ndctl(nvdimm_bus); 635 - 636 - device_unregister(&nvdimm_bus->dev); 637 - } 638 - EXPORT_SYMBOL_GPL(nvdimm_bus_unregister); 639 - 640 670 #ifdef CONFIG_BLK_DEV_INTEGRITY 641 671 int nd_integrity_init(struct gendisk *disk, unsigned long meta_size) 642 672 { ··· 647 773 nvdimm_bus_exit(); 648 774 nd_region_devs_exit(); 649 775 nvdimm_devs_exit(); 650 - ida_destroy(&nd_ida); 651 776 } 652 777 653 778 MODULE_LICENSE("GPL v2");