Skip to content

Commit 1851594

Browse files
committed
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 <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 5bf0b6e commit 1851594

File tree

2 files changed

+181
-134
lines changed

2 files changed

+181
-134
lines changed

drivers/nvdimm/bus.c

Lines changed: 181 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
int nvdimm_major;
3232
static int nvdimm_bus_major;
3333
static struct class *nd_class;
34+
static DEFINE_IDA(nd_ida);
3435

3536
static int to_nd_device_type(struct device *dev)
3637
{
@@ -60,13 +61,6 @@ static int nvdimm_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
6061
to_nd_device_type(dev));
6162
}
6263

63-
static 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-
7064
static struct module *to_bus_provider(struct device *dev)
7165
{
7266
/* pin bus providers while regions are enabled */
@@ -223,6 +217,8 @@ long nvdimm_clear_poison(struct device *dev, phys_addr_t phys,
223217
}
224218
EXPORT_SYMBOL_GPL(nvdimm_clear_poison);
225219

220+
static int nvdimm_bus_match(struct device *dev, struct device_driver *drv);
221+
226222
static struct bus_type nvdimm_bus_type = {
227223
.name = "nd",
228224
.uevent = nvdimm_bus_uevent,
@@ -232,6 +228,176 @@ static struct bus_type nvdimm_bus_type = {
232228
.shutdown = nvdimm_bus_shutdown,
233229
};
234230

231+
static 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+
240+
static bool is_nvdimm_bus(struct device *dev)
241+
{
242+
return dev->release == nvdimm_bus_release;
243+
}
244+
245+
struct 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+
258+
struct 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+
}
266+
EXPORT_SYMBOL_GPL(to_nvdimm_bus);
267+
268+
struct 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+
}
304+
EXPORT_SYMBOL_GPL(nvdimm_bus_register);
305+
306+
void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
307+
{
308+
if (!nvdimm_bus)
309+
return;
310+
device_unregister(&nvdimm_bus->dev);
311+
}
312+
EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
313+
314+
static 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+
329+
static 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+
340+
static 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+
360+
static 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+
379+
static 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+
391+
static 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+
235401
static ASYNC_DOMAIN_EXCLUSIVE(nd_async_domain);
236402

237403
void nd_synchronize(void)
@@ -864,8 +1030,14 @@ int __init nvdimm_bus_init(void)
8641030
goto err_class;
8651031
}
8661032

1033+
rc = driver_register(&nd_bus_driver.drv);
1034+
if (rc)
1035+
goto err_nd_bus;
1036+
8671037
return 0;
8681038

1039+
err_nd_bus:
1040+
class_destroy(nd_class);
8691041
err_class:
8701042
unregister_chrdev(nvdimm_major, "dimmctl");
8711043
err_dimm_chrdev:
@@ -878,8 +1050,10 @@ int __init nvdimm_bus_init(void)
8781050

8791051
void nvdimm_bus_exit(void)
8801052
{
1053+
driver_unregister(&nd_bus_driver.drv);
8811054
class_destroy(nd_class);
8821055
unregister_chrdev(nvdimm_bus_major, "ndctl");
8831056
unregister_chrdev(nvdimm_major, "dimmctl");
8841057
bus_unregister(&nvdimm_bus_type);
1058+
ida_destroy(&nd_ida);
8851059
}

0 commit comments

Comments
 (0)