aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2016-05-18 12:15:08 -0400
committerDan Williams <dan.j.williams@intel.com>2016-05-21 01:02:53 -0400
commitab68f26221366f92611650e8470e6a926801c7d4 (patch)
treea43178bfd901b84c6b65a6b293f03bc4c9d04076
parent6cf9c5babd980ec1959e0dd45e3036474c6a294f (diff)
/dev/dax, pmem: direct access to persistent memory
Device DAX is the device-centric analogue of Filesystem DAX (CONFIG_FS_DAX). It allows memory ranges to be allocated and mapped without need of an intervening file system. Device DAX is strict, precise and predictable. Specifically this interface: 1/ Guarantees fault granularity with respect to a given page size (pte, pmd, or pud) set at configuration time. 2/ Enforces deterministic behavior by being strict about what fault scenarios are supported. For example, by forcing MADV_DONTFORK semantics and omitting MAP_PRIVATE support device-dax guarantees that a mapping always behaves/performs the same once established. It is the "what you see is what you get" access mechanism to differentiated memory vs filesystem DAX which has filesystem specific implementation semantics. Persistent memory is the first target, but the mechanism is also targeted for exclusive allocations of performance differentiated memory ranges. This commit is limited to the base device driver infrastructure to associate a dax device with pmem range. Cc: Jeff Moyer <jmoyer@redhat.com> Cc: Christoph Hellwig <hch@lst.de> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Dave Hansen <dave.hansen@linux.intel.com> Cc: Ross Zwisler <ross.zwisler@linux.intel.com> Reviewed-by: Johannes Thumshirn <jthumshirn@suse.de> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-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}