summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/dax/Kconfig25
-rw-r--r--drivers/dax/Makefile4
-rw-r--r--drivers/dax/dax.c253
-rw-r--r--drivers/dax/dax.h24
-rw-r--r--drivers/dax/pmem.c158
-rw-r--r--tools/testing/nvdimm/Kbuild9
-rw-r--r--tools/testing/nvdimm/config_check.c2
9 files changed, 478 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index d2ac339de85f..8298eab84a6f 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -190,6 +190,8 @@ source "drivers/android/Kconfig"
190 190
191source "drivers/nvdimm/Kconfig" 191source "drivers/nvdimm/Kconfig"
192 192
193source "drivers/dax/Kconfig"
194
193source "drivers/nvmem/Kconfig" 195source "drivers/nvmem/Kconfig"
194 196
195source "drivers/hwtracing/stm/Kconfig" 197source "drivers/hwtracing/stm/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 8f5d076baeb0..0b6f3d60193d 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -66,6 +66,7 @@ obj-$(CONFIG_PARPORT) += parport/
66obj-$(CONFIG_NVM) += lightnvm/ 66obj-$(CONFIG_NVM) += lightnvm/
67obj-y += base/ block/ misc/ mfd/ nfc/ 67obj-y += base/ block/ misc/ mfd/ nfc/
68obj-$(CONFIG_LIBNVDIMM) += nvdimm/ 68obj-$(CONFIG_LIBNVDIMM) += nvdimm/
69obj-$(CONFIG_DEV_DAX) += dax/
69obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/ 70obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
70obj-$(CONFIG_NUBUS) += nubus/ 71obj-$(CONFIG_NUBUS) += nubus/
71obj-y += macintosh/ 72obj-y += macintosh/
diff --git a/drivers/dax/Kconfig b/drivers/dax/Kconfig
new file mode 100644
index 000000000000..86ffbaa891ad
--- /dev/null
+++ b/drivers/dax/Kconfig
@@ -0,0 +1,25 @@
1menuconfig DEV_DAX
2 tristate "DAX: direct access to differentiated memory"
3 default m if NVDIMM_DAX
4 help
5 Support raw access to differentiated (persistence, bandwidth,
6 latency...) memory via an mmap(2) capable character
7 device. Platform firmware or a device driver may identify a
8 platform memory resource that is differentiated from the
9 baseline memory pool. Mappings of a /dev/daxX.Y device impose
10 restrictions that make the mapping behavior deterministic.
11
12if DEV_DAX
13
14config DEV_DAX_PMEM
15 tristate "PMEM DAX: direct access to persistent memory"
16 depends on NVDIMM_DAX
17 default DEV_DAX
18 help
19 Support raw access to persistent memory. Note that this
20 driver consumes memory ranges allocated and exported by the
21 libnvdimm sub-system.
22
23 Say Y if unsure
24
25endif
diff --git a/drivers/dax/Makefile b/drivers/dax/Makefile
new file mode 100644
index 000000000000..27c54e38478a
--- /dev/null
+++ b/drivers/dax/Makefile
@@ -0,0 +1,4 @@
1obj-$(CONFIG_DEV_DAX) += dax.o
2obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
3
4dax_pmem-y := pmem.o
diff --git a/drivers/dax/dax.c b/drivers/dax/dax.c
new file mode 100644
index 000000000000..4c22a40f2335
--- /dev/null
+++ b/drivers/dax/dax.c
@@ -0,0 +1,253 @@
1/*
2 * Copyright(c) 2016 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#include <linux/pagemap.h>
14#include <linux/module.h>
15#include <linux/device.h>
16#include <linux/pfn_t.h>
17#include <linux/slab.h>
18#include <linux/dax.h>
19#include <linux/fs.h>
20#include <linux/mm.h>
21
22static int dax_major;
23static struct class *dax_class;
24static DEFINE_IDA(dax_minor_ida);
25
26/**
27 * struct dax_region - mapping infrastructure for dax devices
28 * @id: kernel-wide unique region for a memory range
29 * @base: linear address corresponding to @res
30 * @kref: to pin while other agents have a need to do lookups
31 * @dev: parent device backing this region
32 * @align: allocation and mapping alignment for child dax devices
33 * @res: physical address range of the region
34 * @pfn_flags: identify whether the pfns are paged back or not
35 */
36struct dax_region {
37 int id;
38 struct ida ida;
39 void *base;
40 struct kref kref;
41 struct device *dev;
42 unsigned int align;
43 struct resource res;
44 unsigned long pfn_flags;
45};
46
47/**
48 * struct dax_dev - subdivision of a dax region
49 * @region - parent region
50 * @dev - device backing the character device
51 * @kref - enable this data to be tracked in filp->private_data
52 * @id - child id in the region
53 * @num_resources - number of physical address extents in this device
54 * @res - array of physical address ranges
55 */
56struct dax_dev {
57 struct dax_region *region;
58 struct device *dev;
59 struct kref kref;
60 int id;
61 int num_resources;
62 struct resource res[0];
63};
64
65static void dax_region_free(struct kref *kref)
66{
67 struct dax_region *dax_region;
68
69 dax_region = container_of(kref, struct dax_region, kref);
70 kfree(dax_region);
71}
72
73void dax_region_put(struct dax_region *dax_region)
74{
75 kref_put(&dax_region->kref, dax_region_free);
76}
77EXPORT_SYMBOL_GPL(dax_region_put);
78
79static void dax_dev_free(struct kref *kref)
80{
81 struct dax_dev *dax_dev;
82
83 dax_dev = container_of(kref, struct dax_dev, kref);
84 dax_region_put(dax_dev->region);
85 kfree(dax_dev);
86}
87
88static void dax_dev_put(struct dax_dev *dax_dev)
89{
90 kref_put(&dax_dev->kref, dax_dev_free);
91}
92
93struct dax_region *alloc_dax_region(struct device *parent, int region_id,
94 struct resource *res, unsigned int align, void *addr,
95 unsigned long pfn_flags)
96{
97 struct dax_region *dax_region;
98
99 dax_region = kzalloc(sizeof(*dax_region), GFP_KERNEL);
100
101 if (!dax_region)
102 return NULL;
103
104 memcpy(&dax_region->res, res, sizeof(*res));
105 dax_region->pfn_flags = pfn_flags;
106 kref_init(&dax_region->kref);
107 dax_region->id = region_id;
108 ida_init(&dax_region->ida);
109 dax_region->align = align;
110 dax_region->dev = parent;
111 dax_region->base = addr;
112
113 return dax_region;
114}
115EXPORT_SYMBOL_GPL(alloc_dax_region);
116
117static ssize_t size_show(struct device *dev,
118 struct device_attribute *attr, char *buf)
119{
120 struct dax_dev *dax_dev = dev_get_drvdata(dev);
121 unsigned long long size = 0;
122 int i;
123
124 for (i = 0; i < dax_dev->num_resources; i++)
125 size += resource_size(&dax_dev->res[i]);
126
127 return sprintf(buf, "%llu\n", size);
128}
129static DEVICE_ATTR_RO(size);
130
131static struct attribute *dax_device_attributes[] = {
132 &dev_attr_size.attr,
133 NULL,
134};
135
136static const struct attribute_group dax_device_attribute_group = {
137 .attrs = dax_device_attributes,
138};
139
140static const struct attribute_group *dax_attribute_groups[] = {
141 &dax_device_attribute_group,
142 NULL,
143};
144
145static void unregister_dax_dev(void *_dev)
146{
147 struct device *dev = _dev;
148 struct dax_dev *dax_dev = dev_get_drvdata(dev);
149 struct dax_region *dax_region = dax_dev->region;
150
151 dev_dbg(dev, "%s\n", __func__);
152
153 get_device(dev);
154 device_unregister(dev);
155 ida_simple_remove(&dax_region->ida, dax_dev->id);
156 ida_simple_remove(&dax_minor_ida, MINOR(dev->devt));
157 put_device(dev);
158 dax_dev_put(dax_dev);
159}
160
161int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res,
162 int count)
163{
164 struct device *parent = dax_region->dev;
165 struct dax_dev *dax_dev;
166 struct device *dev;
167 int rc, minor;
168 dev_t dev_t;
169
170 dax_dev = kzalloc(sizeof(*dax_dev) + sizeof(*res) * count, GFP_KERNEL);
171 if (!dax_dev)
172 return -ENOMEM;
173 memcpy(dax_dev->res, res, sizeof(*res) * count);
174 dax_dev->num_resources = count;
175 kref_init(&dax_dev->kref);
176 dax_dev->region = dax_region;
177 kref_get(&dax_region->kref);
178
179 dax_dev->id = ida_simple_get(&dax_region->ida, 0, 0, GFP_KERNEL);
180 if (dax_dev->id < 0) {
181 rc = dax_dev->id;
182 goto err_id;
183 }
184
185 minor = ida_simple_get(&dax_minor_ida, 0, 0, GFP_KERNEL);
186 if (minor < 0) {
187 rc = minor;
188 goto err_minor;
189 }
190
191 dev_t = MKDEV(dax_major, minor);
192 dev = device_create_with_groups(dax_class, parent, dev_t, dax_dev,
193 dax_attribute_groups, "dax%d.%d", dax_region->id,
194 dax_dev->id);
195 if (IS_ERR(dev)) {
196 rc = PTR_ERR(dev);
197 goto err_create;
198 }
199 dax_dev->dev = dev;
200
201 rc = devm_add_action(dax_region->dev, unregister_dax_dev, dev);
202 if (rc) {
203 unregister_dax_dev(dev);
204 return rc;
205 }
206
207 return 0;
208
209 err_create:
210 ida_simple_remove(&dax_minor_ida, minor);
211 err_minor:
212 ida_simple_remove(&dax_region->ida, dax_dev->id);
213 err_id:
214 dax_dev_put(dax_dev);
215
216 return rc;
217}
218EXPORT_SYMBOL_GPL(devm_create_dax_dev);
219
220static const struct file_operations dax_fops = {
221 .llseek = noop_llseek,
222 .owner = THIS_MODULE,
223};
224
225static int __init dax_init(void)
226{
227 int rc;
228
229 rc = register_chrdev(0, "dax", &dax_fops);
230 if (rc < 0)
231 return rc;
232 dax_major = rc;
233
234 dax_class = class_create(THIS_MODULE, "dax");
235 if (IS_ERR(dax_class)) {
236 unregister_chrdev(dax_major, "dax");
237 return PTR_ERR(dax_class);
238 }
239
240 return 0;
241}
242
243static void __exit dax_exit(void)
244{
245 class_destroy(dax_class);
246 unregister_chrdev(dax_major, "dax");
247 ida_destroy(&dax_minor_ida);
248}
249
250MODULE_AUTHOR("Intel Corporation");
251MODULE_LICENSE("GPL v2");
252subsys_initcall(dax_init);
253module_exit(dax_exit);
diff --git a/drivers/dax/dax.h b/drivers/dax/dax.h
new file mode 100644
index 000000000000..d8b8f1f25054
--- /dev/null
+++ b/drivers/dax/dax.h
@@ -0,0 +1,24 @@
1/*
2 * Copyright(c) 2016 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#ifndef __DAX_H__
14#define __DAX_H__
15struct device;
16struct resource;
17struct dax_region;
18void dax_region_put(struct dax_region *dax_region);
19struct dax_region *alloc_dax_region(struct device *parent,
20 int region_id, struct resource *res, unsigned int align,
21 void *addr, unsigned long flags);
22int devm_create_dax_dev(struct dax_region *dax_region, struct resource *res,
23 int count);
24#endif /* __DAX_H__ */
diff --git a/drivers/dax/pmem.c b/drivers/dax/pmem.c
new file mode 100644
index 000000000000..55d510e36cd1
--- /dev/null
+++ b/drivers/dax/pmem.c
@@ -0,0 +1,158 @@
1/*
2 * Copyright(c) 2016 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#include <linux/percpu-refcount.h>
14#include <linux/memremap.h>
15#include <linux/module.h>
16#include <linux/pfn_t.h>
17#include "../nvdimm/pfn.h"
18#include "../nvdimm/nd.h"
19#include "dax.h"
20
21struct dax_pmem {
22 struct device *dev;
23 struct percpu_ref ref;
24 struct completion cmp;
25};
26
27struct dax_pmem *to_dax_pmem(struct percpu_ref *ref)
28{
29 return container_of(ref, struct dax_pmem, ref);
30}
31
32static void dax_pmem_percpu_release(struct percpu_ref *ref)
33{
34 struct dax_pmem *dax_pmem = to_dax_pmem(ref);
35
36 dev_dbg(dax_pmem->dev, "%s\n", __func__);
37 complete(&dax_pmem->cmp);
38}
39
40static void dax_pmem_percpu_exit(void *data)
41{
42 struct percpu_ref *ref = data;
43 struct dax_pmem *dax_pmem = to_dax_pmem(ref);
44
45 dev_dbg(dax_pmem->dev, "%s\n", __func__);
46 percpu_ref_exit(ref);
47 wait_for_completion(&dax_pmem->cmp);
48}
49
50static void dax_pmem_percpu_kill(void *data)
51{
52 struct percpu_ref *ref = data;
53 struct dax_pmem *dax_pmem = to_dax_pmem(ref);
54
55 dev_dbg(dax_pmem->dev, "%s\n", __func__);
56 percpu_ref_kill(ref);
57}
58
59static int dax_pmem_probe(struct device *dev)
60{
61 int rc;
62 void *addr;
63 struct resource res;
64 struct nd_pfn_sb *pfn_sb;
65 struct dax_pmem *dax_pmem;
66 struct nd_region *nd_region;
67 struct nd_namespace_io *nsio;
68 struct dax_region *dax_region;
69 struct nd_namespace_common *ndns;
70 struct nd_dax *nd_dax = to_nd_dax(dev);
71 struct nd_pfn *nd_pfn = &nd_dax->nd_pfn;
72 struct vmem_altmap __altmap, *altmap = NULL;
73
74 ndns = nvdimm_namespace_common_probe(dev);
75 if (IS_ERR(ndns))
76 return PTR_ERR(ndns);
77 nsio = to_nd_namespace_io(&ndns->dev);
78
79 /* parse the 'pfn' info block via ->rw_bytes */
80 devm_nsio_enable(dev, nsio);
81 altmap = nvdimm_setup_pfn(nd_pfn, &res, &__altmap);
82 if (IS_ERR(altmap))
83 return PTR_ERR(altmap);
84 devm_nsio_disable(dev, nsio);
85
86 pfn_sb = nd_pfn->pfn_sb;
87
88 if (!devm_request_mem_region(dev, nsio->res.start,
89 resource_size(&nsio->res), dev_name(dev))) {
90 dev_warn(dev, "could not reserve region %pR\n", &nsio->res);
91 return -EBUSY;
92 }
93
94 dax_pmem = devm_kzalloc(dev, sizeof(*dax_pmem), GFP_KERNEL);
95 if (!dax_pmem)
96 return -ENOMEM;
97
98 dax_pmem->dev = dev;
99 init_completion(&dax_pmem->cmp);
100 rc = percpu_ref_init(&dax_pmem->ref, dax_pmem_percpu_release, 0,
101 GFP_KERNEL);
102 if (rc)
103 return rc;
104
105 rc = devm_add_action(dev, dax_pmem_percpu_exit, &dax_pmem->ref);
106 if (rc) {
107 dax_pmem_percpu_exit(&dax_pmem->ref);
108 return rc;
109 }
110
111 addr = devm_memremap_pages(dev, &res, &dax_pmem->ref, altmap);
112 if (IS_ERR(addr))
113 return PTR_ERR(addr);
114
115 rc = devm_add_action(dev, dax_pmem_percpu_kill, &dax_pmem->ref);
116 if (rc) {
117 dax_pmem_percpu_kill(&dax_pmem->ref);
118 return rc;
119 }
120
121 nd_region = to_nd_region(dev->parent);
122 dax_region = alloc_dax_region(dev, nd_region->id, &res,
123 le32_to_cpu(pfn_sb->align), addr, PFN_DEV|PFN_MAP);
124 if (!dax_region)
125 return -ENOMEM;
126
127 /* TODO: support for subdividing a dax region... */
128 rc = devm_create_dax_dev(dax_region, &res, 1);
129
130 /* child dax_dev instances now own the lifetime of the dax_region */
131 dax_region_put(dax_region);
132
133 return rc;
134}
135
136static struct nd_device_driver dax_pmem_driver = {
137 .probe = dax_pmem_probe,
138 .drv = {
139 .name = "dax_pmem",
140 },
141 .type = ND_DRIVER_DAX_PMEM,
142};
143
144static int __init dax_pmem_init(void)
145{
146 return nd_driver_register(&dax_pmem_driver);
147}
148module_init(dax_pmem_init);
149
150static void __exit dax_pmem_exit(void)
151{
152 driver_unregister(&dax_pmem_driver.drv);
153}
154module_exit(dax_pmem_exit);
155
156MODULE_LICENSE("GPL v2");
157MODULE_AUTHOR("Intel Corporation");
158MODULE_ALIAS_ND_DEVICE(ND_DEVICE_DAX_PMEM);
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild
index 5ff6d3c126a9..785985677159 100644
--- a/tools/testing/nvdimm/Kbuild
+++ b/tools/testing/nvdimm/Kbuild
@@ -16,6 +16,7 @@ ldflags-y += --wrap=phys_to_pfn_t
16DRIVERS := ../../../drivers 16DRIVERS := ../../../drivers
17NVDIMM_SRC := $(DRIVERS)/nvdimm 17NVDIMM_SRC := $(DRIVERS)/nvdimm
18ACPI_SRC := $(DRIVERS)/acpi 18ACPI_SRC := $(DRIVERS)/acpi
19DAX_SRC := $(DRIVERS)/dax
19 20
20obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o 21obj-$(CONFIG_LIBNVDIMM) += libnvdimm.o
21obj-$(CONFIG_BLK_DEV_PMEM) += nd_pmem.o 22obj-$(CONFIG_BLK_DEV_PMEM) += nd_pmem.o
@@ -23,6 +24,8 @@ obj-$(CONFIG_ND_BTT) += nd_btt.o
23obj-$(CONFIG_ND_BLK) += nd_blk.o 24obj-$(CONFIG_ND_BLK) += nd_blk.o
24obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o 25obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
25obj-$(CONFIG_ACPI_NFIT) += nfit.o 26obj-$(CONFIG_ACPI_NFIT) += nfit.o
27obj-$(CONFIG_DEV_DAX) += dax.o
28obj-$(CONFIG_DEV_DAX_PMEM) += dax_pmem.o
26 29
27nfit-y := $(ACPI_SRC)/nfit.o 30nfit-y := $(ACPI_SRC)/nfit.o
28nfit-y += config_check.o 31nfit-y += config_check.o
@@ -39,6 +42,12 @@ nd_blk-y += config_check.o
39nd_e820-y := $(NVDIMM_SRC)/e820.o 42nd_e820-y := $(NVDIMM_SRC)/e820.o
40nd_e820-y += config_check.o 43nd_e820-y += config_check.o
41 44
45dax-y := $(DAX_SRC)/dax.o
46dax-y += config_check.o
47
48dax_pmem-y := $(DAX_SRC)/pmem.o
49dax_pmem-y += config_check.o
50
42libnvdimm-y := $(NVDIMM_SRC)/core.o 51libnvdimm-y := $(NVDIMM_SRC)/core.o
43libnvdimm-y += $(NVDIMM_SRC)/bus.o 52libnvdimm-y += $(NVDIMM_SRC)/bus.o
44libnvdimm-y += $(NVDIMM_SRC)/dimm_devs.o 53libnvdimm-y += $(NVDIMM_SRC)/dimm_devs.o
diff --git a/tools/testing/nvdimm/config_check.c b/tools/testing/nvdimm/config_check.c
index f2c7615554eb..adf18bfeca00 100644
--- a/tools/testing/nvdimm/config_check.c
+++ b/tools/testing/nvdimm/config_check.c
@@ -12,4 +12,6 @@ void check(void)
12 BUILD_BUG_ON(!IS_MODULE(CONFIG_ND_BTT)); 12 BUILD_BUG_ON(!IS_MODULE(CONFIG_ND_BTT));
13 BUILD_BUG_ON(!IS_MODULE(CONFIG_ND_BLK)); 13 BUILD_BUG_ON(!IS_MODULE(CONFIG_ND_BLK));
14 BUILD_BUG_ON(!IS_MODULE(CONFIG_ACPI_NFIT)); 14 BUILD_BUG_ON(!IS_MODULE(CONFIG_ACPI_NFIT));
15 BUILD_BUG_ON(!IS_MODULE(CONFIG_DEV_DAX));
16 BUILD_BUG_ON(!IS_MODULE(CONFIG_DEV_DAX_PMEM));
15} 17}