Skip to content

Commit e6dfb2d

Browse files
committed
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 <[email protected]> Cc: <[email protected]> Cc: Greg KH <[email protected]> Cc: Robert Moore <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Acked-by: Christoph Hellwig <[email protected]> Acked-by: Rafael J. Wysocki <[email protected]> Tested-by: Toshi Kani <[email protected]> Signed-off-by: Dan Williams <[email protected]>
1 parent 45def22 commit e6dfb2d

File tree

8 files changed

+321
-4
lines changed

8 files changed

+321
-4
lines changed

drivers/acpi/nfit.c

Lines changed: 160 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,165 @@ static const struct attribute_group *acpi_nfit_attribute_groups[] = {
381381
NULL,
382382
};
383383

384+
static struct acpi_nfit_memory_map *to_nfit_memdev(struct device *dev)
385+
{
386+
struct nvdimm *nvdimm = to_nvdimm(dev);
387+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
388+
389+
return __to_nfit_memdev(nfit_mem);
390+
}
391+
392+
static struct acpi_nfit_control_region *to_nfit_dcr(struct device *dev)
393+
{
394+
struct nvdimm *nvdimm = to_nvdimm(dev);
395+
struct nfit_mem *nfit_mem = nvdimm_provider_data(nvdimm);
396+
397+
return nfit_mem->dcr;
398+
}
399+
400+
static ssize_t handle_show(struct device *dev,
401+
struct device_attribute *attr, char *buf)
402+
{
403+
struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
404+
405+
return sprintf(buf, "%#x\n", memdev->device_handle);
406+
}
407+
static DEVICE_ATTR_RO(handle);
408+
409+
static ssize_t phys_id_show(struct device *dev,
410+
struct device_attribute *attr, char *buf)
411+
{
412+
struct acpi_nfit_memory_map *memdev = to_nfit_memdev(dev);
413+
414+
return sprintf(buf, "%#x\n", memdev->physical_id);
415+
}
416+
static DEVICE_ATTR_RO(phys_id);
417+
418+
static ssize_t vendor_show(struct device *dev,
419+
struct device_attribute *attr, char *buf)
420+
{
421+
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
422+
423+
return sprintf(buf, "%#x\n", dcr->vendor_id);
424+
}
425+
static DEVICE_ATTR_RO(vendor);
426+
427+
static ssize_t rev_id_show(struct device *dev,
428+
struct device_attribute *attr, char *buf)
429+
{
430+
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
431+
432+
return sprintf(buf, "%#x\n", dcr->revision_id);
433+
}
434+
static DEVICE_ATTR_RO(rev_id);
435+
436+
static ssize_t device_show(struct device *dev,
437+
struct device_attribute *attr, char *buf)
438+
{
439+
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
440+
441+
return sprintf(buf, "%#x\n", dcr->device_id);
442+
}
443+
static DEVICE_ATTR_RO(device);
444+
445+
static ssize_t format_show(struct device *dev,
446+
struct device_attribute *attr, char *buf)
447+
{
448+
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
449+
450+
return sprintf(buf, "%#x\n", dcr->code);
451+
}
452+
static DEVICE_ATTR_RO(format);
453+
454+
static ssize_t serial_show(struct device *dev,
455+
struct device_attribute *attr, char *buf)
456+
{
457+
struct acpi_nfit_control_region *dcr = to_nfit_dcr(dev);
458+
459+
return sprintf(buf, "%#x\n", dcr->serial_number);
460+
}
461+
static DEVICE_ATTR_RO(serial);
462+
463+
static struct attribute *acpi_nfit_dimm_attributes[] = {
464+
&dev_attr_handle.attr,
465+
&dev_attr_phys_id.attr,
466+
&dev_attr_vendor.attr,
467+
&dev_attr_device.attr,
468+
&dev_attr_format.attr,
469+
&dev_attr_serial.attr,
470+
&dev_attr_rev_id.attr,
471+
NULL,
472+
};
473+
474+
static umode_t acpi_nfit_dimm_attr_visible(struct kobject *kobj,
475+
struct attribute *a, int n)
476+
{
477+
struct device *dev = container_of(kobj, struct device, kobj);
478+
479+
if (to_nfit_dcr(dev))
480+
return a->mode;
481+
else
482+
return 0;
483+
}
484+
485+
static struct attribute_group acpi_nfit_dimm_attribute_group = {
486+
.name = "nfit",
487+
.attrs = acpi_nfit_dimm_attributes,
488+
.is_visible = acpi_nfit_dimm_attr_visible,
489+
};
490+
491+
static const struct attribute_group *acpi_nfit_dimm_attribute_groups[] = {
492+
&acpi_nfit_dimm_attribute_group,
493+
NULL,
494+
};
495+
496+
static struct nvdimm *acpi_nfit_dimm_by_handle(struct acpi_nfit_desc *acpi_desc,
497+
u32 device_handle)
498+
{
499+
struct nfit_mem *nfit_mem;
500+
501+
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list)
502+
if (__to_nfit_memdev(nfit_mem)->device_handle == device_handle)
503+
return nfit_mem->nvdimm;
504+
505+
return NULL;
506+
}
507+
508+
static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc)
509+
{
510+
struct nfit_mem *nfit_mem;
511+
512+
list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) {
513+
struct nvdimm *nvdimm;
514+
unsigned long flags = 0;
515+
u32 device_handle;
516+
517+
device_handle = __to_nfit_memdev(nfit_mem)->device_handle;
518+
nvdimm = acpi_nfit_dimm_by_handle(acpi_desc, device_handle);
519+
if (nvdimm) {
520+
/*
521+
* If for some reason we find multiple DCRs the
522+
* first one wins
523+
*/
524+
dev_err(acpi_desc->dev, "duplicate DCR detected: %s\n",
525+
nvdimm_name(nvdimm));
526+
continue;
527+
}
528+
529+
if (nfit_mem->bdw && nfit_mem->memdev_pmem)
530+
flags |= NDD_ALIASING;
531+
532+
nvdimm = nvdimm_create(acpi_desc->nvdimm_bus, nfit_mem,
533+
acpi_nfit_dimm_attribute_groups, flags);
534+
if (!nvdimm)
535+
return -ENOMEM;
536+
537+
nfit_mem->nvdimm = nvdimm;
538+
}
539+
540+
return 0;
541+
}
542+
384543
static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
385544
{
386545
struct device *dev = acpi_desc->dev;
@@ -408,7 +567,7 @@ static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
408567
if (nfit_mem_init(acpi_desc) != 0)
409568
return -ENOMEM;
410569

411-
return 0;
570+
return acpi_nfit_register_dimms(acpi_desc);
412571
}
413572

414573
static int acpi_nfit_add(struct acpi_device *adev)

drivers/acpi/nfit.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ struct nfit_memdev {
5959

6060
/* assembled tables for a given dimm/memory-device */
6161
struct nfit_mem {
62+
struct nvdimm *nvdimm;
6263
struct acpi_nfit_memory_map *memdev_dcr;
6364
struct acpi_nfit_memory_map *memdev_pmem;
6465
struct acpi_nfit_control_region *dcr;

drivers/nvdimm/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o
22

33
libnvdimm-y := core.o
44
libnvdimm-y += bus.o
5+
libnvdimm-y += dimm_devs.o

drivers/nvdimm/bus.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1414
#include <linux/uaccess.h>
1515
#include <linux/fcntl.h>
16+
#include <linux/async.h>
1617
#include <linux/slab.h>
1718
#include <linux/fs.h>
1819
#include <linux/io.h>
@@ -21,6 +22,10 @@
2122
static int nvdimm_bus_major;
2223
static struct class *nd_class;
2324

25+
struct bus_type nvdimm_bus_type = {
26+
.name = "nd",
27+
};
28+
2429
int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus)
2530
{
2631
dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id);
@@ -59,9 +64,13 @@ int __init nvdimm_bus_init(void)
5964
{
6065
int rc;
6166

67+
rc = bus_register(&nvdimm_bus_type);
68+
if (rc)
69+
return rc;
70+
6271
rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops);
6372
if (rc < 0)
64-
return rc;
73+
goto err_chrdev;
6574
nvdimm_bus_major = rc;
6675

6776
nd_class = class_create(THIS_MODULE, "nd");
@@ -72,6 +81,8 @@ int __init nvdimm_bus_init(void)
7281

7382
err_class:
7483
unregister_chrdev(nvdimm_bus_major, "ndctl");
84+
err_chrdev:
85+
bus_unregister(&nvdimm_bus_type);
7586

7687
return rc;
7788
}
@@ -80,4 +91,5 @@ void __exit nvdimm_bus_exit(void)
8091
{
8192
class_destroy(nd_class);
8293
unregister_chrdev(nvdimm_bus_major, "ndctl");
94+
bus_unregister(&nvdimm_bus_type);
8395
}

drivers/nvdimm/core.c

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
#include <linux/slab.h>
1919
#include "nd-core.h"
2020

21-
static LIST_HEAD(nvdimm_bus_list);
22-
static DEFINE_MUTEX(nvdimm_bus_list_mutex);
21+
LIST_HEAD(nvdimm_bus_list);
22+
DEFINE_MUTEX(nvdimm_bus_list_mutex);
2323
static DEFINE_IDA(nd_ida);
2424

2525
static void nvdimm_bus_release(struct device *dev)
@@ -48,6 +48,19 @@ struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
4848
}
4949
EXPORT_SYMBOL_GPL(to_nd_desc);
5050

51+
struct nvdimm_bus *walk_to_nvdimm_bus(struct device *nd_dev)
52+
{
53+
struct device *dev;
54+
55+
for (dev = nd_dev; dev; dev = dev->parent)
56+
if (dev->release == nvdimm_bus_release)
57+
break;
58+
dev_WARN_ONCE(nd_dev, !dev, "invalid dev, not on nd bus\n");
59+
if (dev)
60+
return to_nvdimm_bus(dev);
61+
return NULL;
62+
}
63+
5164
static const char *nvdimm_bus_provider(struct nvdimm_bus *nvdimm_bus)
5265
{
5366
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
@@ -121,6 +134,21 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
121134
}
122135
EXPORT_SYMBOL_GPL(nvdimm_bus_register);
123136

137+
static int child_unregister(struct device *dev, void *data)
138+
{
139+
/*
140+
* the singular ndctl class device per bus needs to be
141+
* "device_destroy"ed, so skip it here
142+
*
143+
* i.e. remove classless children
144+
*/
145+
if (dev->class)
146+
/* pass */;
147+
else
148+
device_unregister(dev);
149+
return 0;
150+
}
151+
124152
void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
125153
{
126154
if (!nvdimm_bus)
@@ -130,6 +158,7 @@ void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
130158
list_del_init(&nvdimm_bus->list);
131159
mutex_unlock(&nvdimm_bus_list_mutex);
132160

161+
device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister);
133162
nvdimm_bus_destroy_ndctl(nvdimm_bus);
134163

135164
device_unregister(&nvdimm_bus->dev);

drivers/nvdimm/dimm_devs.c

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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+
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
14+
#include <linux/device.h>
15+
#include <linux/slab.h>
16+
#include <linux/io.h>
17+
#include <linux/fs.h>
18+
#include <linux/mm.h>
19+
#include "nd-core.h"
20+
21+
static DEFINE_IDA(dimm_ida);
22+
23+
static void nvdimm_release(struct device *dev)
24+
{
25+
struct nvdimm *nvdimm = to_nvdimm(dev);
26+
27+
ida_simple_remove(&dimm_ida, nvdimm->id);
28+
kfree(nvdimm);
29+
}
30+
31+
static struct device_type nvdimm_device_type = {
32+
.name = "nvdimm",
33+
.release = nvdimm_release,
34+
};
35+
36+
static bool is_nvdimm(struct device *dev)
37+
{
38+
return dev->type == &nvdimm_device_type;
39+
}
40+
41+
struct nvdimm *to_nvdimm(struct device *dev)
42+
{
43+
struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
44+
45+
WARN_ON(!is_nvdimm(dev));
46+
return nvdimm;
47+
}
48+
EXPORT_SYMBOL_GPL(to_nvdimm);
49+
50+
const char *nvdimm_name(struct nvdimm *nvdimm)
51+
{
52+
return dev_name(&nvdimm->dev);
53+
}
54+
EXPORT_SYMBOL_GPL(nvdimm_name);
55+
56+
void *nvdimm_provider_data(struct nvdimm *nvdimm)
57+
{
58+
return nvdimm->provider_data;
59+
}
60+
EXPORT_SYMBOL_GPL(nvdimm_provider_data);
61+
62+
struct nvdimm *nvdimm_create(struct nvdimm_bus *nvdimm_bus, void *provider_data,
63+
const struct attribute_group **groups, unsigned long flags)
64+
{
65+
struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
66+
struct device *dev;
67+
68+
if (!nvdimm)
69+
return NULL;
70+
71+
nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
72+
if (nvdimm->id < 0) {
73+
kfree(nvdimm);
74+
return NULL;
75+
}
76+
nvdimm->provider_data = provider_data;
77+
nvdimm->flags = flags;
78+
79+
dev = &nvdimm->dev;
80+
dev_set_name(dev, "nmem%d", nvdimm->id);
81+
dev->parent = &nvdimm_bus->dev;
82+
dev->type = &nvdimm_device_type;
83+
dev->bus = &nvdimm_bus_type;
84+
dev->groups = groups;
85+
if (device_register(dev) != 0) {
86+
put_device(dev);
87+
return NULL;
88+
}
89+
90+
return nvdimm;
91+
}
92+
EXPORT_SYMBOL_GPL(nvdimm_create);

0 commit comments

Comments
 (0)