diff options
author | Dan Williams <dan.j.williams@intel.com> | 2015-06-09 15:33:45 -0400 |
---|---|---|
committer | Dan Williams <dan.j.williams@intel.com> | 2015-06-24 21:24:10 -0400 |
commit | 9f53f9fa4ad1d8bddd4d14359cdabc531aedffe8 (patch) | |
tree | aa2a412de2b39b8b042e5cd9ae5a223b79ff8640 | |
parent | 18da2c9ee41a036bf470dbad73c18a815725d36e (diff) |
libnvdimm, pmem: add libnvdimm support to the pmem driver
nd_pmem attaches to persistent memory regions and namespaces emitted by
the libnvdimm subsystem, and, same as the original pmem driver, presents
the system-physical-address range as a block device.
The existing e820-type-12 to pmem setup is converted to an nvdimm_bus
that emits an nd_namespace_io device.
Note that the X in 'pmemX' is now derived from the parent region. This
provides some stability to the pmem devices names from boot-to-boot.
The minor numbers are also more predictable by passing 0 to
alloc_disk().
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: Boaz Harrosh <boaz@plexistor.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Jens Axboe <axboe@fb.com>
Cc: Ingo Molnar <mingo@kernel.org>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Acked-by: Christoph Hellwig <hch@lst.de>
Tested-by: Toshi Kani <toshi.kani@hp.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
-rw-r--r-- | arch/x86/Kconfig | 3 | ||||
-rw-r--r-- | arch/x86/kernel/pmem.c | 92 | ||||
-rw-r--r-- | drivers/nvdimm/pmem.c | 68 |
3 files changed, 96 insertions, 67 deletions
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 226d5696e1d1..1a2cbf641667 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig | |||
@@ -1424,6 +1424,9 @@ source "mm/Kconfig" | |||
1424 | 1424 | ||
1425 | config X86_PMEM_LEGACY | 1425 | config X86_PMEM_LEGACY |
1426 | bool "Support non-standard NVDIMMs and ADR protected memory" | 1426 | bool "Support non-standard NVDIMMs and ADR protected memory" |
1427 | depends on PHYS_ADDR_T_64BIT | ||
1428 | depends on BLK_DEV | ||
1429 | select LIBNVDIMM | ||
1427 | help | 1430 | help |
1428 | Treat memory marked using the non-standard e820 type of 12 as used | 1431 | Treat memory marked using the non-standard e820 type of 12 as used |
1429 | by the Intel Sandy Bridge-EP reference BIOS as protected memory. | 1432 | by the Intel Sandy Bridge-EP reference BIOS as protected memory. |
diff --git a/arch/x86/kernel/pmem.c b/arch/x86/kernel/pmem.c index 3420c874ddc5..0f4ef472ab9e 100644 --- a/arch/x86/kernel/pmem.c +++ b/arch/x86/kernel/pmem.c | |||
@@ -1,53 +1,81 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (c) 2015, Christoph Hellwig. | 2 | * Copyright (c) 2015, Christoph Hellwig. |
3 | * Copyright (c) 2015, Intel Corporation. | ||
3 | */ | 4 | */ |
4 | #include <linux/memblock.h> | ||
5 | #include <linux/platform_device.h> | 5 | #include <linux/platform_device.h> |
6 | #include <linux/slab.h> | 6 | #include <linux/libnvdimm.h> |
7 | #include <linux/module.h> | ||
7 | #include <asm/e820.h> | 8 | #include <asm/e820.h> |
8 | #include <asm/page_types.h> | ||
9 | #include <asm/setup.h> | ||
10 | 9 | ||
11 | static __init void register_pmem_device(struct resource *res) | 10 | static void e820_pmem_release(struct device *dev) |
12 | { | 11 | { |
13 | struct platform_device *pdev; | 12 | struct nvdimm_bus *nvdimm_bus = dev->platform_data; |
14 | int error; | ||
15 | 13 | ||
16 | pdev = platform_device_alloc("pmem", PLATFORM_DEVID_AUTO); | 14 | if (nvdimm_bus) |
17 | if (!pdev) | 15 | nvdimm_bus_unregister(nvdimm_bus); |
18 | return; | 16 | } |
19 | 17 | ||
20 | error = platform_device_add_resources(pdev, res, 1); | 18 | static struct platform_device e820_pmem = { |
21 | if (error) | 19 | .name = "e820_pmem", |
22 | goto out_put_pdev; | 20 | .id = -1, |
21 | .dev = { | ||
22 | .release = e820_pmem_release, | ||
23 | }, | ||
24 | }; | ||
23 | 25 | ||
24 | error = platform_device_add(pdev); | 26 | static const struct attribute_group *e820_pmem_attribute_groups[] = { |
25 | if (error) | 27 | &nvdimm_bus_attribute_group, |
26 | goto out_put_pdev; | 28 | NULL, |
27 | return; | 29 | }; |
28 | 30 | ||
29 | out_put_pdev: | 31 | static const struct attribute_group *e820_pmem_region_attribute_groups[] = { |
30 | dev_warn(&pdev->dev, "failed to add 'pmem' (persistent memory) device!\n"); | 32 | &nd_region_attribute_group, |
31 | platform_device_put(pdev); | 33 | &nd_device_attribute_group, |
32 | } | 34 | NULL, |
35 | }; | ||
33 | 36 | ||
34 | static __init int register_pmem_devices(void) | 37 | static __init int register_e820_pmem(void) |
35 | { | 38 | { |
36 | int i; | 39 | static struct nvdimm_bus_descriptor nd_desc; |
40 | struct device *dev = &e820_pmem.dev; | ||
41 | struct nvdimm_bus *nvdimm_bus; | ||
42 | int rc, i; | ||
43 | |||
44 | rc = platform_device_register(&e820_pmem); | ||
45 | if (rc) | ||
46 | return rc; | ||
47 | |||
48 | nd_desc.attr_groups = e820_pmem_attribute_groups; | ||
49 | nd_desc.provider_name = "e820"; | ||
50 | nvdimm_bus = nvdimm_bus_register(dev, &nd_desc); | ||
51 | if (!nvdimm_bus) | ||
52 | goto err; | ||
53 | dev->platform_data = nvdimm_bus; | ||
37 | 54 | ||
38 | for (i = 0; i < e820.nr_map; i++) { | 55 | for (i = 0; i < e820.nr_map; i++) { |
39 | struct e820entry *ei = &e820.map[i]; | 56 | struct e820entry *ei = &e820.map[i]; |
57 | struct resource res = { | ||
58 | .flags = IORESOURCE_MEM, | ||
59 | .start = ei->addr, | ||
60 | .end = ei->addr + ei->size - 1, | ||
61 | }; | ||
62 | struct nd_region_desc ndr_desc; | ||
63 | |||
64 | if (ei->type != E820_PRAM) | ||
65 | continue; | ||
40 | 66 | ||
41 | if (ei->type == E820_PRAM) { | 67 | memset(&ndr_desc, 0, sizeof(ndr_desc)); |
42 | struct resource res = { | 68 | ndr_desc.res = &res; |
43 | .flags = IORESOURCE_MEM, | 69 | ndr_desc.attr_groups = e820_pmem_region_attribute_groups; |
44 | .start = ei->addr, | 70 | if (!nvdimm_pmem_region_create(nvdimm_bus, &ndr_desc)) |
45 | .end = ei->addr + ei->size - 1, | 71 | goto err; |
46 | }; | ||
47 | register_pmem_device(&res); | ||
48 | } | ||
49 | } | 72 | } |
50 | 73 | ||
51 | return 0; | 74 | return 0; |
75 | |||
76 | err: | ||
77 | dev_err(dev, "failed to register legacy persistent memory ranges\n"); | ||
78 | platform_device_unregister(&e820_pmem); | ||
79 | return -ENXIO; | ||
52 | } | 80 | } |
53 | device_initcall(register_pmem_devices); | 81 | device_initcall(register_e820_pmem); |
diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index eabf4a8d0085..d46975ed9e40 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c | |||
@@ -1,7 +1,7 @@ | |||
1 | /* | 1 | /* |
2 | * Persistent Memory Driver | 2 | * Persistent Memory Driver |
3 | * | 3 | * |
4 | * Copyright (c) 2014, Intel Corporation. | 4 | * Copyright (c) 2014-2015, Intel Corporation. |
5 | * Copyright (c) 2015, Christoph Hellwig <hch@lst.de>. | 5 | * Copyright (c) 2015, Christoph Hellwig <hch@lst.de>. |
6 | * Copyright (c) 2015, Boaz Harrosh <boaz@plexistor.com>. | 6 | * Copyright (c) 2015, Boaz Harrosh <boaz@plexistor.com>. |
7 | * | 7 | * |
@@ -23,8 +23,8 @@ | |||
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/moduleparam.h> | 24 | #include <linux/moduleparam.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | 26 | #include <linux/nd.h> | |
27 | #define PMEM_MINORS 16 | 27 | #include "nd.h" |
28 | 28 | ||
29 | struct pmem_device { | 29 | struct pmem_device { |
30 | struct request_queue *pmem_queue; | 30 | struct request_queue *pmem_queue; |
@@ -37,7 +37,6 @@ struct pmem_device { | |||
37 | }; | 37 | }; |
38 | 38 | ||
39 | static int pmem_major; | 39 | static int pmem_major; |
40 | static atomic_t pmem_index; | ||
41 | 40 | ||
42 | static void pmem_do_bvec(struct pmem_device *pmem, struct page *page, | 41 | static void pmem_do_bvec(struct pmem_device *pmem, struct page *page, |
43 | unsigned int len, unsigned int off, int rw, | 42 | unsigned int len, unsigned int off, int rw, |
@@ -118,11 +117,12 @@ static const struct block_device_operations pmem_fops = { | |||
118 | .direct_access = pmem_direct_access, | 117 | .direct_access = pmem_direct_access, |
119 | }; | 118 | }; |
120 | 119 | ||
121 | static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res) | 120 | static struct pmem_device *pmem_alloc(struct device *dev, |
121 | struct resource *res, int id) | ||
122 | { | 122 | { |
123 | struct pmem_device *pmem; | 123 | struct pmem_device *pmem; |
124 | struct gendisk *disk; | 124 | struct gendisk *disk; |
125 | int idx, err; | 125 | int err; |
126 | 126 | ||
127 | err = -ENOMEM; | 127 | err = -ENOMEM; |
128 | pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); | 128 | pmem = kzalloc(sizeof(*pmem), GFP_KERNEL); |
@@ -134,7 +134,8 @@ static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res) | |||
134 | 134 | ||
135 | err = -EINVAL; | 135 | err = -EINVAL; |
136 | if (!request_mem_region(pmem->phys_addr, pmem->size, "pmem")) { | 136 | if (!request_mem_region(pmem->phys_addr, pmem->size, "pmem")) { |
137 | dev_warn(dev, "could not reserve region [0x%pa:0x%zx]\n", &pmem->phys_addr, pmem->size); | 137 | dev_warn(dev, "could not reserve region [0x%pa:0x%zx]\n", |
138 | &pmem->phys_addr, pmem->size); | ||
138 | goto out_free_dev; | 139 | goto out_free_dev; |
139 | } | 140 | } |
140 | 141 | ||
@@ -155,19 +156,17 @@ static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res) | |||
155 | blk_queue_max_hw_sectors(pmem->pmem_queue, 1024); | 156 | blk_queue_max_hw_sectors(pmem->pmem_queue, 1024); |
156 | blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY); | 157 | blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY); |
157 | 158 | ||
158 | disk = alloc_disk(PMEM_MINORS); | 159 | disk = alloc_disk(0); |
159 | if (!disk) | 160 | if (!disk) |
160 | goto out_free_queue; | 161 | goto out_free_queue; |
161 | 162 | ||
162 | idx = atomic_inc_return(&pmem_index) - 1; | ||
163 | |||
164 | disk->major = pmem_major; | 163 | disk->major = pmem_major; |
165 | disk->first_minor = PMEM_MINORS * idx; | 164 | disk->first_minor = 0; |
166 | disk->fops = &pmem_fops; | 165 | disk->fops = &pmem_fops; |
167 | disk->private_data = pmem; | 166 | disk->private_data = pmem; |
168 | disk->queue = pmem->pmem_queue; | 167 | disk->queue = pmem->pmem_queue; |
169 | disk->flags = GENHD_FL_EXT_DEVT; | 168 | disk->flags = GENHD_FL_EXT_DEVT; |
170 | sprintf(disk->disk_name, "pmem%d", idx); | 169 | sprintf(disk->disk_name, "pmem%d", id); |
171 | disk->driverfs_dev = dev; | 170 | disk->driverfs_dev = dev; |
172 | set_capacity(disk, pmem->size >> 9); | 171 | set_capacity(disk, pmem->size >> 9); |
173 | pmem->pmem_disk = disk; | 172 | pmem->pmem_disk = disk; |
@@ -198,42 +197,38 @@ static void pmem_free(struct pmem_device *pmem) | |||
198 | kfree(pmem); | 197 | kfree(pmem); |
199 | } | 198 | } |
200 | 199 | ||
201 | static int pmem_probe(struct platform_device *pdev) | 200 | static int nd_pmem_probe(struct device *dev) |
202 | { | 201 | { |
202 | struct nd_region *nd_region = to_nd_region(dev->parent); | ||
203 | struct nd_namespace_io *nsio = to_nd_namespace_io(dev); | ||
203 | struct pmem_device *pmem; | 204 | struct pmem_device *pmem; |
204 | struct resource *res; | ||
205 | |||
206 | if (WARN_ON(pdev->num_resources > 1)) | ||
207 | return -ENXIO; | ||
208 | |||
209 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
210 | if (!res) | ||
211 | return -ENXIO; | ||
212 | 205 | ||
213 | pmem = pmem_alloc(&pdev->dev, res); | 206 | pmem = pmem_alloc(dev, &nsio->res, nd_region->id); |
214 | if (IS_ERR(pmem)) | 207 | if (IS_ERR(pmem)) |
215 | return PTR_ERR(pmem); | 208 | return PTR_ERR(pmem); |
216 | 209 | ||
217 | platform_set_drvdata(pdev, pmem); | 210 | dev_set_drvdata(dev, pmem); |
218 | 211 | ||
219 | return 0; | 212 | return 0; |
220 | } | 213 | } |
221 | 214 | ||
222 | static int pmem_remove(struct platform_device *pdev) | 215 | static int nd_pmem_remove(struct device *dev) |
223 | { | 216 | { |
224 | struct pmem_device *pmem = platform_get_drvdata(pdev); | 217 | struct pmem_device *pmem = dev_get_drvdata(dev); |
225 | 218 | ||
226 | pmem_free(pmem); | 219 | pmem_free(pmem); |
227 | return 0; | 220 | return 0; |
228 | } | 221 | } |
229 | 222 | ||
230 | static struct platform_driver pmem_driver = { | 223 | MODULE_ALIAS("pmem"); |
231 | .probe = pmem_probe, | 224 | MODULE_ALIAS_ND_DEVICE(ND_DEVICE_NAMESPACE_IO); |
232 | .remove = pmem_remove, | 225 | static struct nd_device_driver nd_pmem_driver = { |
233 | .driver = { | 226 | .probe = nd_pmem_probe, |
234 | .owner = THIS_MODULE, | 227 | .remove = nd_pmem_remove, |
235 | .name = "pmem", | 228 | .drv = { |
229 | .name = "nd_pmem", | ||
236 | }, | 230 | }, |
231 | .type = ND_DRIVER_NAMESPACE_IO, | ||
237 | }; | 232 | }; |
238 | 233 | ||
239 | static int __init pmem_init(void) | 234 | static int __init pmem_init(void) |
@@ -244,16 +239,19 @@ static int __init pmem_init(void) | |||
244 | if (pmem_major < 0) | 239 | if (pmem_major < 0) |
245 | return pmem_major; | 240 | return pmem_major; |
246 | 241 | ||
247 | error = platform_driver_register(&pmem_driver); | 242 | error = nd_driver_register(&nd_pmem_driver); |
248 | if (error) | 243 | if (error) { |
249 | unregister_blkdev(pmem_major, "pmem"); | 244 | unregister_blkdev(pmem_major, "pmem"); |
250 | return error; | 245 | return error; |
246 | } | ||
247 | |||
248 | return 0; | ||
251 | } | 249 | } |
252 | module_init(pmem_init); | 250 | module_init(pmem_init); |
253 | 251 | ||
254 | static void pmem_exit(void) | 252 | static void pmem_exit(void) |
255 | { | 253 | { |
256 | platform_driver_unregister(&pmem_driver); | 254 | driver_unregister(&nd_pmem_driver.drv); |
257 | unregister_blkdev(pmem_major, "pmem"); | 255 | unregister_blkdev(pmem_major, "pmem"); |
258 | } | 256 | } |
259 | module_exit(pmem_exit); | 257 | module_exit(pmem_exit); |