aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-04-18 11:42:49 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2015-04-18 11:42:49 -0400
commit34a984f7b0cc6355a1e0c184251d0d4cc86f44d2 (patch)
tree5ed28ee6cf191216be394c78234b13edbe69d1f8
parent90d1c087861dcc3d1175993fc03492c137fd21bb (diff)
parent4c1eaa2344fb26bb5e936fb4d8ee307343ea0089 (diff)
Merge branch 'x86-pmem-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull PMEM driver from Ingo Molnar: "This is the initial support for the pmem block device driver: persistent non-volatile memory space mapped into the system's physical memory space as large physical memory regions. The driver is based on Intel code, written by Ross Zwisler, with fixes by Boaz Harrosh, integrated with x86 e820 memory resource management and tidied up by Christoph Hellwig. Note that there were two other separate pmem driver submissions to lkml: but apparently all parties (Ross Zwisler, Boaz Harrosh) are reasonably happy with this initial version. This version enables minimal support that enables persistent memory devices out in the wild to work as block devices, identified through a magic (non-standard) e820 flag and auto-discovered if CONFIG_X86_PMEM_LEGACY=y, or added explicitly through manipulating the memory maps via the "memmap=..." boot option with the new, special '!' modifier character. Limitations: this is a regular block device, and since the pmem areas are not struct page backed, they are invisible to the rest of the system (other than the block IO device), so direct IO to/from pmem areas, direct mmap() or XIP is not possible yet. The page cache will also shadow and double buffer pmem contents, etc. Initial support is for x86" * 'x86-pmem-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: drivers/block/pmem: Fix 32-bit build warning in pmem_alloc() drivers/block/pmem: Add a driver for persistent memory x86/mm: Add support for the non-standard protected e820 type
-rw-r--r--Documentation/kernel-parameters.txt6
-rw-r--r--MAINTAINERS6
-rw-r--r--arch/x86/Kconfig10
-rw-r--r--arch/x86/include/uapi/asm/e820.h10
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/e820.c26
-rw-r--r--arch/x86/kernel/pmem.c53
-rw-r--r--drivers/block/Kconfig11
-rw-r--r--drivers/block/Makefile1
-rw-r--r--drivers/block/pmem.c262
10 files changed, 380 insertions, 6 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 5bc92d2f4716..274252f205b7 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1972,6 +1972,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
1972 or 1972 or
1973 memmap=0x10000$0x18690000 1973 memmap=0x10000$0x18690000
1974 1974
1975 memmap=nn[KMG]!ss[KMG]
1976 [KNL,X86] Mark specific memory as protected.
1977 Region of memory to be used, from ss to ss+nn.
1978 The memory region may be marked as e820 type 12 (0xc)
1979 and is NVDIMM or ADR memory.
1980
1975 memory_corruption_check=0/1 [X86] 1981 memory_corruption_check=0/1 [X86]
1976 Some BIOSes seem to corrupt the first 64k of 1982 Some BIOSes seem to corrupt the first 64k of
1977 memory when doing things like suspend/resume. 1983 memory when doing things like suspend/resume.
diff --git a/MAINTAINERS b/MAINTAINERS
index dcd43465eae1..db335f98cad0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8130,6 +8130,12 @@ S: Maintained
8130F: Documentation/blockdev/ramdisk.txt 8130F: Documentation/blockdev/ramdisk.txt
8131F: drivers/block/brd.c 8131F: drivers/block/brd.c
8132 8132
8133PERSISTENT MEMORY DRIVER
8134M: Ross Zwisler <ross.zwisler@linux.intel.com>
8135L: linux-nvdimm@lists.01.org
8136S: Supported
8137F: drivers/block/pmem.c
8138
8133RANDOM NUMBER DRIVER 8139RANDOM NUMBER DRIVER
8134M: "Theodore Ts'o" <tytso@mit.edu> 8140M: "Theodore Ts'o" <tytso@mit.edu>
8135S: Maintained 8141S: Maintained
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 937812b8e0b9..6049d587599e 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1421,6 +1421,16 @@ config ILLEGAL_POINTER_VALUE
1421 1421
1422source "mm/Kconfig" 1422source "mm/Kconfig"
1423 1423
1424config X86_PMEM_LEGACY
1425 bool "Support non-standard NVDIMMs and ADR protected memory"
1426 help
1427 Treat memory marked using the non-standard e820 type of 12 as used
1428 by the Intel Sandy Bridge-EP reference BIOS as protected memory.
1429 The kernel will offer these regions to the 'pmem' driver so
1430 they can be used for persistent storage.
1431
1432 Say Y if unsure.
1433
1424config HIGHPTE 1434config HIGHPTE
1425 bool "Allocate 3rd-level pagetables from highmem" 1435 bool "Allocate 3rd-level pagetables from highmem"
1426 depends on HIGHMEM 1436 depends on HIGHMEM
diff --git a/arch/x86/include/uapi/asm/e820.h b/arch/x86/include/uapi/asm/e820.h
index d993e33f5236..960a8a9dc4ab 100644
--- a/arch/x86/include/uapi/asm/e820.h
+++ b/arch/x86/include/uapi/asm/e820.h
@@ -33,6 +33,16 @@
33#define E820_NVS 4 33#define E820_NVS 4
34#define E820_UNUSABLE 5 34#define E820_UNUSABLE 5
35 35
36/*
37 * This is a non-standardized way to represent ADR or NVDIMM regions that
38 * persist over a reboot. The kernel will ignore their special capabilities
39 * unless the CONFIG_X86_PMEM_LEGACY=y option is set.
40 *
41 * ( Note that older platforms also used 6 for the same type of memory,
42 * but newer versions switched to 12 as 6 was assigned differently. Some
43 * time they will learn... )
44 */
45#define E820_PRAM 12
36 46
37/* 47/*
38 * reserved RAM used by kernel itself 48 * reserved RAM used by kernel itself
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index c887cd944f0c..9bcd0b56ca17 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -95,6 +95,7 @@ obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o
95obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o 95obj-$(CONFIG_PARAVIRT) += paravirt.o paravirt_patch_$(BITS).o
96obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= paravirt-spinlocks.o 96obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= paravirt-spinlocks.o
97obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o 97obj-$(CONFIG_PARAVIRT_CLOCK) += pvclock.o
98obj-$(CONFIG_X86_PMEM_LEGACY) += pmem.o
98 99
99obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o 100obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o
100 101
diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c
index 7d46bb260334..e2ce85db2283 100644
--- a/arch/x86/kernel/e820.c
+++ b/arch/x86/kernel/e820.c
@@ -149,6 +149,9 @@ static void __init e820_print_type(u32 type)
149 case E820_UNUSABLE: 149 case E820_UNUSABLE:
150 printk(KERN_CONT "unusable"); 150 printk(KERN_CONT "unusable");
151 break; 151 break;
152 case E820_PRAM:
153 printk(KERN_CONT "persistent (type %u)", type);
154 break;
152 default: 155 default:
153 printk(KERN_CONT "type %u", type); 156 printk(KERN_CONT "type %u", type);
154 break; 157 break;
@@ -343,7 +346,7 @@ int __init sanitize_e820_map(struct e820entry *biosmap, int max_nr_map,
343 * continue building up new bios map based on this 346 * continue building up new bios map based on this
344 * information 347 * information
345 */ 348 */
346 if (current_type != last_type) { 349 if (current_type != last_type || current_type == E820_PRAM) {
347 if (last_type != 0) { 350 if (last_type != 0) {
348 new_bios[new_bios_entry].size = 351 new_bios[new_bios_entry].size =
349 change_point[chgidx]->addr - last_addr; 352 change_point[chgidx]->addr - last_addr;
@@ -688,6 +691,7 @@ void __init e820_mark_nosave_regions(unsigned long limit_pfn)
688 register_nosave_region(pfn, PFN_UP(ei->addr)); 691 register_nosave_region(pfn, PFN_UP(ei->addr));
689 692
690 pfn = PFN_DOWN(ei->addr + ei->size); 693 pfn = PFN_DOWN(ei->addr + ei->size);
694
691 if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN) 695 if (ei->type != E820_RAM && ei->type != E820_RESERVED_KERN)
692 register_nosave_region(PFN_UP(ei->addr), pfn); 696 register_nosave_region(PFN_UP(ei->addr), pfn);
693 697
@@ -748,7 +752,7 @@ u64 __init early_reserve_e820(u64 size, u64 align)
748/* 752/*
749 * Find the highest page frame number we have available 753 * Find the highest page frame number we have available
750 */ 754 */
751static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type) 755static unsigned long __init e820_end_pfn(unsigned long limit_pfn)
752{ 756{
753 int i; 757 int i;
754 unsigned long last_pfn = 0; 758 unsigned long last_pfn = 0;
@@ -759,7 +763,11 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
759 unsigned long start_pfn; 763 unsigned long start_pfn;
760 unsigned long end_pfn; 764 unsigned long end_pfn;
761 765
762 if (ei->type != type) 766 /*
767 * Persistent memory is accounted as ram for purposes of
768 * establishing max_pfn and mem_map.
769 */
770 if (ei->type != E820_RAM && ei->type != E820_PRAM)
763 continue; 771 continue;
764 772
765 start_pfn = ei->addr >> PAGE_SHIFT; 773 start_pfn = ei->addr >> PAGE_SHIFT;
@@ -784,12 +792,12 @@ static unsigned long __init e820_end_pfn(unsigned long limit_pfn, unsigned type)
784} 792}
785unsigned long __init e820_end_of_ram_pfn(void) 793unsigned long __init e820_end_of_ram_pfn(void)
786{ 794{
787 return e820_end_pfn(MAX_ARCH_PFN, E820_RAM); 795 return e820_end_pfn(MAX_ARCH_PFN);
788} 796}
789 797
790unsigned long __init e820_end_of_low_ram_pfn(void) 798unsigned long __init e820_end_of_low_ram_pfn(void)
791{ 799{
792 return e820_end_pfn(1UL<<(32 - PAGE_SHIFT), E820_RAM); 800 return e820_end_pfn(1UL << (32-PAGE_SHIFT));
793} 801}
794 802
795static void early_panic(char *msg) 803static void early_panic(char *msg)
@@ -866,6 +874,9 @@ static int __init parse_memmap_one(char *p)
866 } else if (*p == '$') { 874 } else if (*p == '$') {
867 start_at = memparse(p+1, &p); 875 start_at = memparse(p+1, &p);
868 e820_add_region(start_at, mem_size, E820_RESERVED); 876 e820_add_region(start_at, mem_size, E820_RESERVED);
877 } else if (*p == '!') {
878 start_at = memparse(p+1, &p);
879 e820_add_region(start_at, mem_size, E820_PRAM);
869 } else 880 } else
870 e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1); 881 e820_remove_range(mem_size, ULLONG_MAX - mem_size, E820_RAM, 1);
871 882
@@ -907,6 +918,7 @@ static inline const char *e820_type_to_string(int e820_type)
907 case E820_ACPI: return "ACPI Tables"; 918 case E820_ACPI: return "ACPI Tables";
908 case E820_NVS: return "ACPI Non-volatile Storage"; 919 case E820_NVS: return "ACPI Non-volatile Storage";
909 case E820_UNUSABLE: return "Unusable memory"; 920 case E820_UNUSABLE: return "Unusable memory";
921 case E820_PRAM: return "Persistent RAM";
910 default: return "reserved"; 922 default: return "reserved";
911 } 923 }
912} 924}
@@ -940,7 +952,9 @@ void __init e820_reserve_resources(void)
940 * pci device BAR resource and insert them later in 952 * pci device BAR resource and insert them later in
941 * pcibios_resource_survey() 953 * pcibios_resource_survey()
942 */ 954 */
943 if (e820.map[i].type != E820_RESERVED || res->start < (1ULL<<20)) { 955 if (((e820.map[i].type != E820_RESERVED) &&
956 (e820.map[i].type != E820_PRAM)) ||
957 res->start < (1ULL<<20)) {
944 res->flags |= IORESOURCE_BUSY; 958 res->flags |= IORESOURCE_BUSY;
945 insert_resource(&iomem_resource, res); 959 insert_resource(&iomem_resource, res);
946 } 960 }
diff --git a/arch/x86/kernel/pmem.c b/arch/x86/kernel/pmem.c
new file mode 100644
index 000000000000..3420c874ddc5
--- /dev/null
+++ b/arch/x86/kernel/pmem.c
@@ -0,0 +1,53 @@
1/*
2 * Copyright (c) 2015, Christoph Hellwig.
3 */
4#include <linux/memblock.h>
5#include <linux/platform_device.h>
6#include <linux/slab.h>
7#include <asm/e820.h>
8#include <asm/page_types.h>
9#include <asm/setup.h>
10
11static __init void register_pmem_device(struct resource *res)
12{
13 struct platform_device *pdev;
14 int error;
15
16 pdev = platform_device_alloc("pmem", PLATFORM_DEVID_AUTO);
17 if (!pdev)
18 return;
19
20 error = platform_device_add_resources(pdev, res, 1);
21 if (error)
22 goto out_put_pdev;
23
24 error = platform_device_add(pdev);
25 if (error)
26 goto out_put_pdev;
27 return;
28
29out_put_pdev:
30 dev_warn(&pdev->dev, "failed to add 'pmem' (persistent memory) device!\n");
31 platform_device_put(pdev);
32}
33
34static __init int register_pmem_devices(void)
35{
36 int i;
37
38 for (i = 0; i < e820.nr_map; i++) {
39 struct e820entry *ei = &e820.map[i];
40
41 if (ei->type == E820_PRAM) {
42 struct resource res = {
43 .flags = IORESOURCE_MEM,
44 .start = ei->addr,
45 .end = ei->addr + ei->size - 1,
46 };
47 register_pmem_device(&res);
48 }
49 }
50
51 return 0;
52}
53device_initcall(register_pmem_devices);
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 1b8094d4d7af..eb1fed5bd516 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -404,6 +404,17 @@ config BLK_DEV_RAM_DAX
404 and will prevent RAM block device backing store memory from being 404 and will prevent RAM block device backing store memory from being
405 allocated from highmem (only a problem for highmem systems). 405 allocated from highmem (only a problem for highmem systems).
406 406
407config BLK_DEV_PMEM
408 tristate "Persistent memory block device support"
409 help
410 Saying Y here will allow you to use a contiguous range of reserved
411 memory as one or more persistent block devices.
412
413 To compile this driver as a module, choose M here: the module will be
414 called 'pmem'.
415
416 If unsure, say N.
417
407config CDROM_PKTCDVD 418config CDROM_PKTCDVD
408 tristate "Packet writing on CD/DVD media" 419 tristate "Packet writing on CD/DVD media"
409 depends on !UML 420 depends on !UML
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index 02b688d1438d..9cc6c18a1c7e 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PS3_VRAM) += ps3vram.o
14obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o 14obj-$(CONFIG_ATARI_FLOPPY) += ataflop.o
15obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o 15obj-$(CONFIG_AMIGA_Z2RAM) += z2ram.o
16obj-$(CONFIG_BLK_DEV_RAM) += brd.o 16obj-$(CONFIG_BLK_DEV_RAM) += brd.o
17obj-$(CONFIG_BLK_DEV_PMEM) += pmem.o
17obj-$(CONFIG_BLK_DEV_LOOP) += loop.o 18obj-$(CONFIG_BLK_DEV_LOOP) += loop.o
18obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o 19obj-$(CONFIG_BLK_CPQ_DA) += cpqarray.o
19obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o 20obj-$(CONFIG_BLK_CPQ_CISS_DA) += cciss.o
diff --git a/drivers/block/pmem.c b/drivers/block/pmem.c
new file mode 100644
index 000000000000..eabf4a8d0085
--- /dev/null
+++ b/drivers/block/pmem.c
@@ -0,0 +1,262 @@
1/*
2 * Persistent Memory Driver
3 *
4 * Copyright (c) 2014, Intel Corporation.
5 * Copyright (c) 2015, Christoph Hellwig <hch@lst.de>.
6 * Copyright (c) 2015, Boaz Harrosh <boaz@plexistor.com>.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms and conditions of the GNU General Public License,
10 * version 2, as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope it will be useful, but WITHOUT
13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * more details.
16 */
17
18#include <asm/cacheflush.h>
19#include <linux/blkdev.h>
20#include <linux/hdreg.h>
21#include <linux/init.h>
22#include <linux/platform_device.h>
23#include <linux/module.h>
24#include <linux/moduleparam.h>
25#include <linux/slab.h>
26
27#define PMEM_MINORS 16
28
29struct pmem_device {
30 struct request_queue *pmem_queue;
31 struct gendisk *pmem_disk;
32
33 /* One contiguous memory region per device */
34 phys_addr_t phys_addr;
35 void *virt_addr;
36 size_t size;
37};
38
39static int pmem_major;
40static atomic_t pmem_index;
41
42static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
43 unsigned int len, unsigned int off, int rw,
44 sector_t sector)
45{
46 void *mem = kmap_atomic(page);
47 size_t pmem_off = sector << 9;
48
49 if (rw == READ) {
50 memcpy(mem + off, pmem->virt_addr + pmem_off, len);
51 flush_dcache_page(page);
52 } else {
53 flush_dcache_page(page);
54 memcpy(pmem->virt_addr + pmem_off, mem + off, len);
55 }
56
57 kunmap_atomic(mem);
58}
59
60static void pmem_make_request(struct request_queue *q, struct bio *bio)
61{
62 struct block_device *bdev = bio->bi_bdev;
63 struct pmem_device *pmem = bdev->bd_disk->private_data;
64 int rw;
65 struct bio_vec bvec;
66 sector_t sector;
67 struct bvec_iter iter;
68 int err = 0;
69
70 if (bio_end_sector(bio) > get_capacity(bdev->bd_disk)) {
71 err = -EIO;
72 goto out;
73 }
74
75 BUG_ON(bio->bi_rw & REQ_DISCARD);
76
77 rw = bio_data_dir(bio);
78 sector = bio->bi_iter.bi_sector;
79 bio_for_each_segment(bvec, bio, iter) {
80 pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len, bvec.bv_offset,
81 rw, sector);
82 sector += bvec.bv_len >> 9;
83 }
84
85out:
86 bio_endio(bio, err);
87}
88
89static int pmem_rw_page(struct block_device *bdev, sector_t sector,
90 struct page *page, int rw)
91{
92 struct pmem_device *pmem = bdev->bd_disk->private_data;
93
94 pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
95 page_endio(page, rw & WRITE, 0);
96
97 return 0;
98}
99
100static long pmem_direct_access(struct block_device *bdev, sector_t sector,
101 void **kaddr, unsigned long *pfn, long size)
102{
103 struct pmem_device *pmem = bdev->bd_disk->private_data;
104 size_t offset = sector << 9;
105
106 if (!pmem)
107 return -ENODEV;
108
109 *kaddr = pmem->virt_addr + offset;
110 *pfn = (pmem->phys_addr + offset) >> PAGE_SHIFT;
111
112 return pmem->size - offset;
113}
114
115static const struct block_device_operations pmem_fops = {
116 .owner = THIS_MODULE,
117 .rw_page = pmem_rw_page,
118 .direct_access = pmem_direct_access,
119};
120
121static struct pmem_device *pmem_alloc(struct device *dev, struct resource *res)
122{
123 struct pmem_device *pmem;
124 struct gendisk *disk;
125 int idx, err;
126
127 err = -ENOMEM;
128 pmem = kzalloc(sizeof(*pmem), GFP_KERNEL);
129 if (!pmem)
130 goto out;
131
132 pmem->phys_addr = res->start;
133 pmem->size = resource_size(res);
134
135 err = -EINVAL;
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);
138 goto out_free_dev;
139 }
140
141 /*
142 * Map the memory as non-cachable, as we can't write back the contents
143 * of the CPU caches in case of a crash.
144 */
145 err = -ENOMEM;
146 pmem->virt_addr = ioremap_nocache(pmem->phys_addr, pmem->size);
147 if (!pmem->virt_addr)
148 goto out_release_region;
149
150 pmem->pmem_queue = blk_alloc_queue(GFP_KERNEL);
151 if (!pmem->pmem_queue)
152 goto out_unmap;
153
154 blk_queue_make_request(pmem->pmem_queue, pmem_make_request);
155 blk_queue_max_hw_sectors(pmem->pmem_queue, 1024);
156 blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY);
157
158 disk = alloc_disk(PMEM_MINORS);
159 if (!disk)
160 goto out_free_queue;
161
162 idx = atomic_inc_return(&pmem_index) - 1;
163
164 disk->major = pmem_major;
165 disk->first_minor = PMEM_MINORS * idx;
166 disk->fops = &pmem_fops;
167 disk->private_data = pmem;
168 disk->queue = pmem->pmem_queue;
169 disk->flags = GENHD_FL_EXT_DEVT;
170 sprintf(disk->disk_name, "pmem%d", idx);
171 disk->driverfs_dev = dev;
172 set_capacity(disk, pmem->size >> 9);
173 pmem->pmem_disk = disk;
174
175 add_disk(disk);
176
177 return pmem;
178
179out_free_queue:
180 blk_cleanup_queue(pmem->pmem_queue);
181out_unmap:
182 iounmap(pmem->virt_addr);
183out_release_region:
184 release_mem_region(pmem->phys_addr, pmem->size);
185out_free_dev:
186 kfree(pmem);
187out:
188 return ERR_PTR(err);
189}
190
191static void pmem_free(struct pmem_device *pmem)
192{
193 del_gendisk(pmem->pmem_disk);
194 put_disk(pmem->pmem_disk);
195 blk_cleanup_queue(pmem->pmem_queue);
196 iounmap(pmem->virt_addr);
197 release_mem_region(pmem->phys_addr, pmem->size);
198 kfree(pmem);
199}
200
201static int pmem_probe(struct platform_device *pdev)
202{
203 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
213 pmem = pmem_alloc(&pdev->dev, res);
214 if (IS_ERR(pmem))
215 return PTR_ERR(pmem);
216
217 platform_set_drvdata(pdev, pmem);
218
219 return 0;
220}
221
222static int pmem_remove(struct platform_device *pdev)
223{
224 struct pmem_device *pmem = platform_get_drvdata(pdev);
225
226 pmem_free(pmem);
227 return 0;
228}
229
230static struct platform_driver pmem_driver = {
231 .probe = pmem_probe,
232 .remove = pmem_remove,
233 .driver = {
234 .owner = THIS_MODULE,
235 .name = "pmem",
236 },
237};
238
239static int __init pmem_init(void)
240{
241 int error;
242
243 pmem_major = register_blkdev(0, "pmem");
244 if (pmem_major < 0)
245 return pmem_major;
246
247 error = platform_driver_register(&pmem_driver);
248 if (error)
249 unregister_blkdev(pmem_major, "pmem");
250 return error;
251}
252module_init(pmem_init);
253
254static void pmem_exit(void)
255{
256 platform_driver_unregister(&pmem_driver);
257 unregister_blkdev(pmem_major, "pmem");
258}
259module_exit(pmem_exit);
260
261MODULE_AUTHOR("Ross Zwisler <ross.zwisler@linux.intel.com>");
262MODULE_LICENSE("GPL v2");