aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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");