xref: /openbmc/linux/drivers/nvdimm/pmem.c (revision 581388209405902b56d055f644b4dd124a206112)
1 /*
2  * Persistent Memory Driver
3  *
4  * Copyright (c) 2014-2015, 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 #include <linux/nd.h>
27 #include "nd.h"
28 
29 struct 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 
39 static int pmem_major;
40 
41 static void pmem_do_bvec(struct pmem_device *pmem, struct page *page,
42 			unsigned int len, unsigned int off, int rw,
43 			sector_t sector)
44 {
45 	void *mem = kmap_atomic(page);
46 	size_t pmem_off = sector << 9;
47 
48 	if (rw == READ) {
49 		memcpy(mem + off, pmem->virt_addr + pmem_off, len);
50 		flush_dcache_page(page);
51 	} else {
52 		flush_dcache_page(page);
53 		memcpy(pmem->virt_addr + pmem_off, mem + off, len);
54 	}
55 
56 	kunmap_atomic(mem);
57 }
58 
59 static void pmem_make_request(struct request_queue *q, struct bio *bio)
60 {
61 	bool do_acct;
62 	unsigned long start;
63 	struct bio_vec bvec;
64 	struct bvec_iter iter;
65 	struct block_device *bdev = bio->bi_bdev;
66 	struct pmem_device *pmem = bdev->bd_disk->private_data;
67 
68 	do_acct = nd_iostat_start(bio, &start);
69 	bio_for_each_segment(bvec, bio, iter)
70 		pmem_do_bvec(pmem, bvec.bv_page, bvec.bv_len, bvec.bv_offset,
71 				bio_data_dir(bio), iter.bi_sector);
72 	if (do_acct)
73 		nd_iostat_end(bio, start);
74 	bio_endio(bio, 0);
75 }
76 
77 static int pmem_rw_page(struct block_device *bdev, sector_t sector,
78 		       struct page *page, int rw)
79 {
80 	struct pmem_device *pmem = bdev->bd_disk->private_data;
81 
82 	pmem_do_bvec(pmem, page, PAGE_CACHE_SIZE, 0, rw, sector);
83 	page_endio(page, rw & WRITE, 0);
84 
85 	return 0;
86 }
87 
88 static long pmem_direct_access(struct block_device *bdev, sector_t sector,
89 			      void **kaddr, unsigned long *pfn, long size)
90 {
91 	struct pmem_device *pmem = bdev->bd_disk->private_data;
92 	size_t offset = sector << 9;
93 
94 	if (!pmem)
95 		return -ENODEV;
96 
97 	*kaddr = pmem->virt_addr + offset;
98 	*pfn = (pmem->phys_addr + offset) >> PAGE_SHIFT;
99 
100 	return pmem->size - offset;
101 }
102 
103 static const struct block_device_operations pmem_fops = {
104 	.owner =		THIS_MODULE,
105 	.rw_page =		pmem_rw_page,
106 	.direct_access =	pmem_direct_access,
107 	.revalidate_disk =	nvdimm_revalidate_disk,
108 };
109 
110 static struct pmem_device *pmem_alloc(struct device *dev,
111 		struct resource *res, int id)
112 {
113 	struct pmem_device *pmem;
114 
115 	pmem = kzalloc(sizeof(*pmem), GFP_KERNEL);
116 	if (!pmem)
117 		return ERR_PTR(-ENOMEM);
118 
119 	pmem->phys_addr = res->start;
120 	pmem->size = resource_size(res);
121 
122 	if (!request_mem_region(pmem->phys_addr, pmem->size, dev_name(dev))) {
123 		dev_warn(dev, "could not reserve region [0x%pa:0x%zx]\n",
124 				&pmem->phys_addr, pmem->size);
125 		kfree(pmem);
126 		return ERR_PTR(-EBUSY);
127 	}
128 
129 	/*
130 	 * Map the memory as non-cachable, as we can't write back the contents
131 	 * of the CPU caches in case of a crash.
132 	 */
133 	pmem->virt_addr = ioremap_nocache(pmem->phys_addr, pmem->size);
134 	if (!pmem->virt_addr) {
135 		release_mem_region(pmem->phys_addr, pmem->size);
136 		kfree(pmem);
137 		return ERR_PTR(-ENXIO);
138 	}
139 
140 	return pmem;
141 }
142 
143 static void pmem_detach_disk(struct pmem_device *pmem)
144 {
145 	del_gendisk(pmem->pmem_disk);
146 	put_disk(pmem->pmem_disk);
147 	blk_cleanup_queue(pmem->pmem_queue);
148 }
149 
150 static int pmem_attach_disk(struct nd_namespace_common *ndns,
151 		struct pmem_device *pmem)
152 {
153 	struct gendisk *disk;
154 
155 	pmem->pmem_queue = blk_alloc_queue(GFP_KERNEL);
156 	if (!pmem->pmem_queue)
157 		return -ENOMEM;
158 
159 	blk_queue_make_request(pmem->pmem_queue, pmem_make_request);
160 	blk_queue_max_hw_sectors(pmem->pmem_queue, UINT_MAX);
161 	blk_queue_bounce_limit(pmem->pmem_queue, BLK_BOUNCE_ANY);
162 	queue_flag_set_unlocked(QUEUE_FLAG_NONROT, pmem->pmem_queue);
163 
164 	disk = alloc_disk(0);
165 	if (!disk) {
166 		blk_cleanup_queue(pmem->pmem_queue);
167 		return -ENOMEM;
168 	}
169 
170 	disk->major		= pmem_major;
171 	disk->first_minor	= 0;
172 	disk->fops		= &pmem_fops;
173 	disk->private_data	= pmem;
174 	disk->queue		= pmem->pmem_queue;
175 	disk->flags		= GENHD_FL_EXT_DEVT;
176 	nvdimm_namespace_disk_name(ndns, disk->disk_name);
177 	disk->driverfs_dev = &ndns->dev;
178 	set_capacity(disk, pmem->size >> 9);
179 	pmem->pmem_disk = disk;
180 
181 	add_disk(disk);
182 	revalidate_disk(disk);
183 
184 	return 0;
185 }
186 
187 static int pmem_rw_bytes(struct nd_namespace_common *ndns,
188 		resource_size_t offset, void *buf, size_t size, int rw)
189 {
190 	struct pmem_device *pmem = dev_get_drvdata(ndns->claim);
191 
192 	if (unlikely(offset + size > pmem->size)) {
193 		dev_WARN_ONCE(&ndns->dev, 1, "request out of range\n");
194 		return -EFAULT;
195 	}
196 
197 	if (rw == READ)
198 		memcpy(buf, pmem->virt_addr + offset, size);
199 	else
200 		memcpy(pmem->virt_addr + offset, buf, size);
201 
202 	return 0;
203 }
204 
205 static void pmem_free(struct pmem_device *pmem)
206 {
207 	iounmap(pmem->virt_addr);
208 	release_mem_region(pmem->phys_addr, pmem->size);
209 	kfree(pmem);
210 }
211 
212 static int nd_pmem_probe(struct device *dev)
213 {
214 	struct nd_region *nd_region = to_nd_region(dev->parent);
215 	struct nd_namespace_common *ndns;
216 	struct nd_namespace_io *nsio;
217 	struct pmem_device *pmem;
218 	int rc;
219 
220 	ndns = nvdimm_namespace_common_probe(dev);
221 	if (IS_ERR(ndns))
222 		return PTR_ERR(ndns);
223 
224 	nsio = to_nd_namespace_io(&ndns->dev);
225 	pmem = pmem_alloc(dev, &nsio->res, nd_region->id);
226 	if (IS_ERR(pmem))
227 		return PTR_ERR(pmem);
228 
229 	dev_set_drvdata(dev, pmem);
230 	ndns->rw_bytes = pmem_rw_bytes;
231 	if (is_nd_btt(dev))
232 		rc = nvdimm_namespace_attach_btt(ndns);
233 	else if (nd_btt_probe(ndns, pmem) == 0) {
234 		/* we'll come back as btt-pmem */
235 		rc = -ENXIO;
236 	} else
237 		rc = pmem_attach_disk(ndns, pmem);
238 	if (rc)
239 		pmem_free(pmem);
240 	return rc;
241 }
242 
243 static int nd_pmem_remove(struct device *dev)
244 {
245 	struct pmem_device *pmem = dev_get_drvdata(dev);
246 
247 	if (is_nd_btt(dev))
248 		nvdimm_namespace_detach_btt(to_nd_btt(dev)->ndns);
249 	else
250 		pmem_detach_disk(pmem);
251 	pmem_free(pmem);
252 
253 	return 0;
254 }
255 
256 MODULE_ALIAS("pmem");
257 MODULE_ALIAS_ND_DEVICE(ND_DEVICE_NAMESPACE_IO);
258 MODULE_ALIAS_ND_DEVICE(ND_DEVICE_NAMESPACE_PMEM);
259 static struct nd_device_driver nd_pmem_driver = {
260 	.probe = nd_pmem_probe,
261 	.remove = nd_pmem_remove,
262 	.drv = {
263 		.name = "nd_pmem",
264 	},
265 	.type = ND_DRIVER_NAMESPACE_IO | ND_DRIVER_NAMESPACE_PMEM,
266 };
267 
268 static int __init pmem_init(void)
269 {
270 	int error;
271 
272 	pmem_major = register_blkdev(0, "pmem");
273 	if (pmem_major < 0)
274 		return pmem_major;
275 
276 	error = nd_driver_register(&nd_pmem_driver);
277 	if (error) {
278 		unregister_blkdev(pmem_major, "pmem");
279 		return error;
280 	}
281 
282 	return 0;
283 }
284 module_init(pmem_init);
285 
286 static void pmem_exit(void)
287 {
288 	driver_unregister(&nd_pmem_driver.drv);
289 	unregister_blkdev(pmem_major, "pmem");
290 }
291 module_exit(pmem_exit);
292 
293 MODULE_AUTHOR("Ross Zwisler <ross.zwisler@linux.intel.com>");
294 MODULE_LICENSE("GPL v2");
295