diff options
author | Dan Williams <dan.j.williams@intel.com> | 2015-04-25 10:56:17 +0300 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2015-06-25 04:24:10 +0300 |
commit | e6dfb2de47768efe8cc37c9a1863d2aff81440fb (patch) | |
tree | a1ad89328d88a37195f23ca9421071fc29e6722f /drivers/nvdimm | |
parent | 45def22c1fab85764646746ce38d45b2f3281fa5 (diff) | |
download | linux-e6dfb2de47768efe8cc37c9a1863d2aff81440fb.tar.xz |
libnvdimm, nfit: dimm/memory-devices
Enable nvdimm devices to be registered on a nvdimm_bus. The kernel
assigned device id for nvdimm devicesis dynamic. If userspace needs a
more static identifier it should consult a provider-specific attribute.
In the case where NFIT is the provider, the 'nmemX/nfit/handle' or
'nmemX/nfit/serial' attributes may be used for this purpose.
Cc: Neil Brown <neilb@suse.de>
Cc: <linux-acpi@vger.kernel.org>
Cc: Greg KH <gregkh@linuxfoundation.org>
Cc: Robert Moore <robert.moore@intel.com>
Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Acked-by: Christoph Hellwig <hch@lst.de>
Acked-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Diffstat (limited to 'drivers/nvdimm')
-rw-r--r-- | drivers/nvdimm/Makefile | 1 | ||||
-rw-r--r-- | drivers/nvdimm/bus.c | 14 | ||||
-rw-r--r-- | drivers/nvdimm/core.c | 33 | ||||
-rw-r--r-- | drivers/nvdimm/dimm_devs.c | 92 | ||||
-rw-r--r-- | drivers/nvdimm/nd-core.h | 12 |
5 files changed, 149 insertions, 3 deletions
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile index 8a8293c72c7c..5b68738ba406 100644 --- a/drivers/nvdimm/Makefile +++ b/drivers/nvdimm/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o libnvdimm-y := core.o libnvdimm-y += bus.o +libnvdimm-y += dimm_devs.o diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 3f7c690a5d0c..a8802577fb55 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -13,6 +13,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/uaccess.h> #include <linux/fcntl.h> +#include <linux/async.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/io.h> @@ -21,6 +22,10 @@ static int nvdimm_bus_major; static struct class *nd_class; +struct bus_type nvdimm_bus_type = { + .name = "nd", +}; + int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus) { dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id); @@ -59,9 +64,13 @@ int __init nvdimm_bus_init(void) { int rc; + rc = bus_register(&nvdimm_bus_type); + if (rc) + return rc; + rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops); if (rc < 0) - return rc; + goto err_chrdev; nvdimm_bus_major = rc; nd_class = class_create(THIS_MODULE, "nd"); @@ -72,6 +81,8 @@ int __init nvdimm_bus_init(void) err_class: unregister_chrdev(nvdimm_bus_major, "ndctl"); + err_chrdev: + bus_unregister(&nvdimm_bus_type); return rc; } @@ -80,4 +91,5 @@ void __exit nvdimm_bus_exit(void) { class_destroy(nd_class); unregister_chrdev(nvdimm_bus_major, "ndctl"); + bus_unregister(&nvdimm_bus_type); } diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index fa7ab5ad0318..ef957eb37c90 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -18,8 +18,8 @@ #include <linux/slab.h> #include "nd-core.h" -static LIST_HEAD(nvdimm_bus_list); -static DEFINE_MUTEX(nvdimm_bus_list_mutex); +LIST_HEAD(nvdimm_bus_list); +DEFINE_MUTEX(nvdimm_bus_list_mutex); static DEFINE_IDA(nd_ida); static void nvdimm_bus_release(struct device *dev) @@ -48,6 +48,19 @@ struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus) } EXPORT_SYMBOL_GPL(to_nd_desc); +struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev) +{ + struct device *dev; + + for (dev = nd_dev; dev; dev = dev->parent) + if (dev->release == nvdimm_bus_release) + break; + dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n"); + if (dev) + return to_nvdimm_bus(dev); + return NULL; +} + static const char *nvdimm_bus_provider(struct nvdimm_bus *nvdimm_bus) { struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; @@ -121,6 +134,21 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent, } EXPORT_SYMBOL_GPL(nvdimm_bus_register); +static int child_unregister(struct device *dev, void *data) +{ + /* + * the singular ndctl class device per bus needs to be + * "device_destroy"ed, so skip it here + * + * i.e. remove classless children + */ + if (dev->class) + /* pass */; + else + device_unregister(dev); + return 0; +} + void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) { if (!nvdimm_bus) @@ -130,6 +158,7 @@ void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus) list_del_init(&nvdimm_bus->list); mutex_unlock(&nvdimm_bus_list_mutex); + device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); nvdimm_bus_destroy_ndctl(nvdimm_bus); device_unregister(&nvdimm_bus->dev); diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c new file mode 100644 index 000000000000..51ea52cc2079 --- /dev/null +++ b/drivers/nvdimm/dimm_devs.c @@ -0,0 +1,92 @@ +/* + * Copyright(c) 2013-2015 Intel Corporation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include "nd-core.h" + +static DEFINE_IDA(dimm_ida); + +static void nvdimm_release(struct device *dev) +{ + struct nvdimm *nvdimm = to_nvdimm(dev); + + ida_simple_remove(&dimm_ida, nvdimm->id); + kfree(nvdimm); +} + +static struct device_type nvdimm_device_type = { + .name = "nvdimm", + .release = nvdimm_release, +}; + +static bool is_nvdimm(struct device *dev) +{ + return dev->type == &nvdimm_device_type; +} + +struct nvdimm *to_nvdimm(struct device *dev) +{ + struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev); + + WARN_ON(!is_nvdimm(dev)); + return nvdimm; +} +EXPORT_SYMBOL_GPL(to_nvdimm); + +const char *nvdimm_name(struct nvdimm *nvdimm) +{ + return dev_name(&nvdimm->dev); +} +EXPORT_SYMBOL_GPL(nvdimm_name); + +void *nvdimm_provider_data(struct nvdimm *nvdimm) +{ + return nvdimm->provider_data; +} +EXPORT_SYMBOL_GPL(nvdimm_provider_data); + +struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data, + const struct attribute_group **groups, unsigned long flags) +{ + struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL); + struct device *dev; + + if (!nvdimm) + return NULL; + + nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL); + if (nvdimm->id < 0) { + kfree(nvdimm); + return NULL; + } + nvdimm->provider_data = provider_data; + nvdimm->flags = flags; + + dev = &nvdimm->dev; + dev_set_name(dev, "nmem%d", nvdimm->id); + dev->parent = &nvdimm_bus->dev; + dev->type = &nvdimm_device_type; + dev->bus = &nvdimm_bus_type; + dev->groups = groups; + if (device_register(dev) != 0) { + put_device(dev); + return NULL; + } + + return nvdimm; +} +EXPORT_SYMBOL_GPL(nvdimm_create); diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 0b0ff2423161..9b8303413b60 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -15,6 +15,10 @@ #include <linux/libnvdimm.h> #include <linux/device.h> +extern struct list_head nvdimm_bus_list; +extern struct mutex nvdimm_bus_list_mutex; +extern struct bus_type nvdimm_bus_type; + struct nvdimm_bus { struct nvdimm_bus_descriptor *nd_desc; struct list_head list; @@ -22,6 +26,14 @@ struct nvdimm_bus { int id; }; +struct nvdimm { + unsigned long flags; + void *provider_data; + struct device dev; + int id; +}; + +struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev); int __init nvdimm_bus_init(void); void __exit nvdimm_bus_exit(void); int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus); |