Skip to content

Commit 45def22

Browse files
committed
libnvdimm: control character device and nvdimm_bus sysfs attributes
The control device for a nvdimm_bus is registered as an "nd" class device. The expectation is that there will usually only be one "nd" bus registered under /sys/class/nd. However, we allow for the possibility of multiple buses and they will listed in discovery order as ndctl0...ndctlN. This character device hosts the ioctl for passing control messages. The initial command set has a 1:1 correlation with the commands listed in the by the "NFIT DSM Example" document [1], but this scheme is extensible to future command sets. Note, nd_ioctl() and the backing ->ndctl() implementation are defined in a subsequent patch. This is simply the initial registrations and sysfs attributes. [1]: http://pmem.io/documents/NVDIMM_DSM_Interface_Example.pdf Cc: Neil Brown <[email protected]> Cc: Greg KH <[email protected]> Cc: <[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 b94d523 commit 45def22

File tree

7 files changed

+215
-3
lines changed

7 files changed

+215
-3
lines changed

drivers/acpi/nfit.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,33 @@ static int nfit_mem_init(struct acpi_nfit_desc *acpi_desc)
354354
return 0;
355355
}
356356

357+
static ssize_t revision_show(struct device *dev,
358+
struct device_attribute *attr, char *buf)
359+
{
360+
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
361+
struct nvdimm_bus_descriptor *nd_desc = to_nd_desc(nvdimm_bus);
362+
struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
363+
364+
return sprintf(buf, "%d\n", acpi_desc->nfit->header.revision);
365+
}
366+
static DEVICE_ATTR_RO(revision);
367+
368+
static struct attribute *acpi_nfit_attributes[] = {
369+
&dev_attr_revision.attr,
370+
NULL,
371+
};
372+
373+
static struct attribute_group acpi_nfit_attribute_group = {
374+
.name = "nfit",
375+
.attrs = acpi_nfit_attributes,
376+
};
377+
378+
static const struct attribute_group *acpi_nfit_attribute_groups[] = {
379+
&nvdimm_bus_attribute_group,
380+
&acpi_nfit_attribute_group,
381+
NULL,
382+
};
383+
357384
static int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, acpi_size sz)
358385
{
359386
struct device *dev = acpi_desc->dev;
@@ -410,6 +437,7 @@ static int acpi_nfit_add(struct acpi_device *adev)
410437
nd_desc = &acpi_desc->nd_desc;
411438
nd_desc->provider_name = "ACPI.NFIT";
412439
nd_desc->ndctl = acpi_nfit_ctl;
440+
nd_desc->attr_groups = acpi_nfit_attribute_groups;
413441

414442
acpi_desc->nvdimm_bus = nvdimm_bus_register(dev, nd_desc);
415443
if (!acpi_desc->nvdimm_bus)

drivers/acpi/nfit.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,10 @@ static inline struct acpi_nfit_memory_map *__to_nfit_memdev(
8787
return nfit_mem->memdev_dcr;
8888
return nfit_mem->memdev_pmem;
8989
}
90+
91+
static inline struct acpi_nfit_desc *to_acpi_desc(
92+
struct nvdimm_bus_descriptor *nd_desc)
93+
{
94+
return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
95+
}
9096
#endif /* __NFIT_H__ */

drivers/nvdimm/Makefile

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

33
libnvdimm-y := core.o
4+
libnvdimm-y += bus.o

drivers/nvdimm/bus.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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/uaccess.h>
15+
#include <linux/fcntl.h>
16+
#include <linux/slab.h>
17+
#include <linux/fs.h>
18+
#include <linux/io.h>
19+
#include "nd-core.h"
20+
21+
static int nvdimm_bus_major;
22+
static struct class *nd_class;
23+
24+
int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus)
25+
{
26+
dev_t devt = MKDEV(nvdimm_bus_major, nvdimm_bus->id);
27+
struct device *dev;
28+
29+
dev = device_create(nd_class, &nvdimm_bus->dev, devt, nvdimm_bus,
30+
"ndctl%d", nvdimm_bus->id);
31+
32+
if (IS_ERR(dev)) {
33+
dev_dbg(&nvdimm_bus->dev, "failed to register ndctl%d: %ld\n",
34+
nvdimm_bus->id, PTR_ERR(dev));
35+
return PTR_ERR(dev);
36+
}
37+
return 0;
38+
}
39+
40+
void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus)
41+
{
42+
device_destroy(nd_class, MKDEV(nvdimm_bus_major, nvdimm_bus->id));
43+
}
44+
45+
static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
46+
{
47+
return -ENXIO;
48+
}
49+
50+
static const struct file_operations nvdimm_bus_fops = {
51+
.owner = THIS_MODULE,
52+
.open = nonseekable_open,
53+
.unlocked_ioctl = nd_ioctl,
54+
.compat_ioctl = nd_ioctl,
55+
.llseek = noop_llseek,
56+
};
57+
58+
int __init nvdimm_bus_init(void)
59+
{
60+
int rc;
61+
62+
rc = register_chrdev(0, "ndctl", &nvdimm_bus_fops);
63+
if (rc < 0)
64+
return rc;
65+
nvdimm_bus_major = rc;
66+
67+
nd_class = class_create(THIS_MODULE, "nd");
68+
if (IS_ERR(nd_class))
69+
goto err_class;
70+
71+
return 0;
72+
73+
err_class:
74+
unregister_chrdev(nvdimm_bus_major, "ndctl");
75+
76+
return rc;
77+
}
78+
79+
void __exit nvdimm_bus_exit(void)
80+
{
81+
class_destroy(nd_class);
82+
unregister_chrdev(nvdimm_bus_major, "ndctl");
83+
}

drivers/nvdimm/core.c

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
#include <linux/export.h>
1515
#include <linux/module.h>
1616
#include <linux/device.h>
17+
#include <linux/mutex.h>
1718
#include <linux/slab.h>
1819
#include "nd-core.h"
1920

21+
static LIST_HEAD(nvdimm_bus_list);
22+
static DEFINE_MUTEX(nvdimm_bus_list_mutex);
2023
static DEFINE_IDA(nd_ida);
2124

2225
static void nvdimm_bus_release(struct device *dev)
@@ -28,6 +31,55 @@ static void nvdimm_bus_release(struct device *dev)
2831
kfree(nvdimm_bus);
2932
}
3033

34+
struct nvdimm_bus *to_nvdimm_bus(struct device *dev)
35+
{
36+
struct nvdimm_bus *nvdimm_bus;
37+
38+
nvdimm_bus = container_of(dev, struct nvdimm_bus, dev);
39+
WARN_ON(nvdimm_bus->dev.release != nvdimm_bus_release);
40+
return nvdimm_bus;
41+
}
42+
EXPORT_SYMBOL_GPL(to_nvdimm_bus);
43+
44+
struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus)
45+
{
46+
/* struct nvdimm_bus definition is private to libnvdimm */
47+
return nvdimm_bus->nd_desc;
48+
}
49+
EXPORT_SYMBOL_GPL(to_nd_desc);
50+
51+
static const char *nvdimm_bus_provider(struct nvdimm_bus *nvdimm_bus)
52+
{
53+
struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
54+
struct device *parent = nvdimm_bus->dev.parent;
55+
56+
if (nd_desc->provider_name)
57+
return nd_desc->provider_name;
58+
else if (parent)
59+
return dev_name(parent);
60+
else
61+
return "unknown";
62+
}
63+
64+
static ssize_t provider_show(struct device *dev,
65+
struct device_attribute *attr, char *buf)
66+
{
67+
struct nvdimm_bus *nvdimm_bus = to_nvdimm_bus(dev);
68+
69+
return sprintf(buf, "%s\n", nvdimm_bus_provider(nvdimm_bus));
70+
}
71+
static DEVICE_ATTR_RO(provider);
72+
73+
static struct attribute *nvdimm_bus_attributes[] = {
74+
&dev_attr_provider.attr,
75+
NULL,
76+
};
77+
78+
struct attribute_group nvdimm_bus_attribute_group = {
79+
.attrs = nvdimm_bus_attributes,
80+
};
81+
EXPORT_SYMBOL_GPL(nvdimm_bus_attribute_group);
82+
3183
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
3284
struct nvdimm_bus_descriptor *nd_desc)
3385
{
@@ -37,6 +89,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
3789
nvdimm_bus = kzalloc(sizeof(*nvdimm_bus), GFP_KERNEL);
3890
if (!nvdimm_bus)
3991
return NULL;
92+
INIT_LIST_HEAD(&nvdimm_bus->list);
4093
nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);
4194
if (nvdimm_bus->id < 0) {
4295
kfree(nvdimm_bus);
@@ -45,25 +98,56 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
4598
nvdimm_bus->nd_desc = nd_desc;
4699
nvdimm_bus->dev.parent = parent;
47100
nvdimm_bus->dev.release = nvdimm_bus_release;
101+
nvdimm_bus->dev.groups = nd_desc->attr_groups;
48102
dev_set_name(&nvdimm_bus->dev, "ndbus%d", nvdimm_bus->id);
49103
rc = device_register(&nvdimm_bus->dev);
50104
if (rc) {
51105
dev_dbg(&nvdimm_bus->dev, "registration failed: %d\n", rc);
52-
put_device(&nvdimm_bus->dev);
53-
return NULL;
106+
goto err;
54107
}
55108

109+
rc = nvdimm_bus_create_ndctl(nvdimm_bus);
110+
if (rc)
111+
goto err;
112+
113+
mutex_lock(&nvdimm_bus_list_mutex);
114+
list_add_tail(&nvdimm_bus->list, &nvdimm_bus_list);
115+
mutex_unlock(&nvdimm_bus_list_mutex);
116+
56117
return nvdimm_bus;
118+
err:
119+
put_device(&nvdimm_bus->dev);
120+
return NULL;
57121
}
58122
EXPORT_SYMBOL_GPL(nvdimm_bus_register);
59123

60124
void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus)
61125
{
62126
if (!nvdimm_bus)
63127
return;
128+
129+
mutex_lock(&nvdimm_bus_list_mutex);
130+
list_del_init(&nvdimm_bus->list);
131+
mutex_unlock(&nvdimm_bus_list_mutex);
132+
133+
nvdimm_bus_destroy_ndctl(nvdimm_bus);
134+
64135
device_unregister(&nvdimm_bus->dev);
65136
}
66137
EXPORT_SYMBOL_GPL(nvdimm_bus_unregister);
67138

139+
static __init int libnvdimm_init(void)
140+
{
141+
return nvdimm_bus_init();
142+
}
143+
144+
static __exit void libnvdimm_exit(void)
145+
{
146+
WARN_ON(!list_empty(&nvdimm_bus_list));
147+
nvdimm_bus_exit();
148+
}
149+
68150
MODULE_LICENSE("GPL v2");
69151
MODULE_AUTHOR("Intel Corporation");
152+
subsys_initcall(libnvdimm_init);
153+
module_exit(libnvdimm_exit);

drivers/nvdimm/nd-core.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,13 @@
1717

1818
struct nvdimm_bus {
1919
struct nvdimm_bus_descriptor *nd_desc;
20+
struct list_head list;
2021
struct device dev;
2122
int id;
2223
};
24+
25+
int __init nvdimm_bus_init(void);
26+
void __exit nvdimm_bus_exit(void);
27+
int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
28+
void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
2329
#endif /* __ND_CORE_H__ */

include/linux/libnvdimm.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,25 @@
1414
*/
1515
#ifndef __LIBNVDIMM_H__
1616
#define __LIBNVDIMM_H__
17+
extern struct attribute_group nvdimm_bus_attribute_group;
18+
1719
struct nvdimm;
1820
struct nvdimm_bus_descriptor;
1921
typedef int (*ndctl_fn)(struct nvdimm_bus_descriptor *nd_desc,
2022
struct nvdimm *nvdimm, unsigned int cmd, void *buf,
2123
unsigned int buf_len);
2224

2325
struct nvdimm_bus_descriptor {
26+
const struct attribute_group **attr_groups;
2427
unsigned long dsm_mask;
2528
char *provider_name;
2629
ndctl_fn ndctl;
2730
};
2831

2932
struct device;
30-
struct nvdimm_bus;
3133
struct nvdimm_bus *nvdimm_bus_register(struct device *parent,
3234
struct nvdimm_bus_descriptor *nfit_desc);
3335
void nvdimm_bus_unregister(struct nvdimm_bus *nvdimm_bus);
36+
struct nvdimm_bus *to_nvdimm_bus(struct device *dev);
37+
struct nvdimm_bus_descriptor *to_nd_desc(struct nvdimm_bus *nvdimm_bus);
3438
#endif /* __LIBNVDIMM_H__ */

0 commit comments

Comments
 (0)