xref: /openbmc/linux/drivers/nvdimm/dimm_devs.c (revision 2522afb8)
15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2e6dfb2deSDan Williams /*
3e6dfb2deSDan Williams  * Copyright(c) 2013-2015 Intel Corporation. All rights reserved.
4e6dfb2deSDan Williams  */
5e6dfb2deSDan Williams #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
6d5d30d5aSDan Williams #include <linux/moduleparam.h>
74d88a97aSDan Williams #include <linux/vmalloc.h>
8e6dfb2deSDan Williams #include <linux/device.h>
962232e45SDan Williams #include <linux/ndctl.h>
10e6dfb2deSDan Williams #include <linux/slab.h>
11e6dfb2deSDan Williams #include <linux/io.h>
12e6dfb2deSDan Williams #include <linux/fs.h>
13e6dfb2deSDan Williams #include <linux/mm.h>
14e6dfb2deSDan Williams #include "nd-core.h"
150ba1c634SDan Williams #include "label.h"
16ca6a4657SDan Williams #include "pmem.h"
174d88a97aSDan Williams #include "nd.h"
18e6dfb2deSDan Williams 
19e6dfb2deSDan Williams static DEFINE_IDA(dimm_ida);
20e6dfb2deSDan Williams 
21d5d30d5aSDan Williams static bool noblk;
22d5d30d5aSDan Williams module_param(noblk, bool, 0444);
23d5d30d5aSDan Williams MODULE_PARM_DESC(noblk, "force disable BLK / local alias support");
24d5d30d5aSDan Williams 
254d88a97aSDan Williams /*
264d88a97aSDan Williams  * Retrieve bus and dimm handle and return if this bus supports
274d88a97aSDan Williams  * get_config_data commands
284d88a97aSDan Williams  */
29aee65987SToshi Kani int nvdimm_check_config_data(struct device *dev)
304d88a97aSDan Williams {
31aee65987SToshi Kani 	struct nvdimm *nvdimm = to_nvdimm(dev);
324d88a97aSDan Williams 
33aee65987SToshi Kani 	if (!nvdimm->cmd_mask ||
34aee65987SToshi Kani 	    !test_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm->cmd_mask)) {
35a0e37452SDan Williams 		if (test_bit(NDD_LABELING, &nvdimm->flags))
364d88a97aSDan Williams 			return -ENXIO;
37aee65987SToshi Kani 		else
38aee65987SToshi Kani 			return -ENOTTY;
39aee65987SToshi Kani 	}
404d88a97aSDan Williams 
414d88a97aSDan Williams 	return 0;
424d88a97aSDan Williams }
434d88a97aSDan Williams 
444d88a97aSDan Williams static int validate_dimm(struct nvdimm_drvdata *ndd)
454d88a97aSDan Williams {
46aee65987SToshi Kani 	int rc;
474d88a97aSDan Williams 
48aee65987SToshi Kani 	if (!ndd)
49aee65987SToshi Kani 		return -EINVAL;
50aee65987SToshi Kani 
51aee65987SToshi Kani 	rc = nvdimm_check_config_data(ndd->dev);
52aee65987SToshi Kani 	if (rc)
53d75f773cSSakari Ailus 		dev_dbg(ndd->dev, "%ps: %s error: %d\n",
544d88a97aSDan Williams 				__builtin_return_address(0), __func__, rc);
554d88a97aSDan Williams 	return rc;
564d88a97aSDan Williams }
574d88a97aSDan Williams 
584d88a97aSDan Williams /**
594d88a97aSDan Williams  * nvdimm_init_nsarea - determine the geometry of a dimm's namespace area
604d88a97aSDan Williams  * @nvdimm: dimm to initialize
614d88a97aSDan Williams  */
624d88a97aSDan Williams int nvdimm_init_nsarea(struct nvdimm_drvdata *ndd)
634d88a97aSDan Williams {
644d88a97aSDan Williams 	struct nd_cmd_get_config_size *cmd = &ndd->nsarea;
654d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
664d88a97aSDan Williams 	struct nvdimm_bus_descriptor *nd_desc;
674d88a97aSDan Williams 	int rc = validate_dimm(ndd);
689d62ed96SDan Williams 	int cmd_rc = 0;
694d88a97aSDan Williams 
704d88a97aSDan Williams 	if (rc)
714d88a97aSDan Williams 		return rc;
724d88a97aSDan Williams 
734d88a97aSDan Williams 	if (cmd->config_size)
744d88a97aSDan Williams 		return 0; /* already valid */
754d88a97aSDan Williams 
764d88a97aSDan Williams 	memset(cmd, 0, sizeof(*cmd));
774d88a97aSDan Williams 	nd_desc = nvdimm_bus->nd_desc;
789d62ed96SDan Williams 	rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
799d62ed96SDan Williams 			ND_CMD_GET_CONFIG_SIZE, cmd, sizeof(*cmd), &cmd_rc);
809d62ed96SDan Williams 	if (rc < 0)
819d62ed96SDan Williams 		return rc;
829d62ed96SDan Williams 	return cmd_rc;
834d88a97aSDan Williams }
844d88a97aSDan Williams 
852d657d17SAlexander Duyck int nvdimm_get_config_data(struct nvdimm_drvdata *ndd, void *buf,
862d657d17SAlexander Duyck 			   size_t offset, size_t len)
874d88a97aSDan Williams {
884d88a97aSDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
892d657d17SAlexander Duyck 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
90e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
914d88a97aSDan Williams 	struct nd_cmd_get_config_data_hdr *cmd;
922d657d17SAlexander Duyck 	size_t max_cmd_size, buf_offset;
934d88a97aSDan Williams 
944d88a97aSDan Williams 	if (rc)
954d88a97aSDan Williams 		return rc;
964d88a97aSDan Williams 
972d657d17SAlexander Duyck 	if (offset + len > ndd->nsarea.config_size)
984d88a97aSDan Williams 		return -ENXIO;
994d88a97aSDan Williams 
1002d657d17SAlexander Duyck 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
101d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd), GFP_KERNEL);
1024d88a97aSDan Williams 	if (!cmd)
1034d88a97aSDan Williams 		return -ENOMEM;
1044d88a97aSDan Williams 
1052d657d17SAlexander Duyck 	for (buf_offset = 0; len;
1062d657d17SAlexander Duyck 	     len -= cmd->in_length, buf_offset += cmd->in_length) {
1072d657d17SAlexander Duyck 		size_t cmd_size;
1082d657d17SAlexander Duyck 
1092d657d17SAlexander Duyck 		cmd->in_offset = offset + buf_offset;
1102d657d17SAlexander Duyck 		cmd->in_length = min(max_cmd_size, len);
1112d657d17SAlexander Duyck 
1122d657d17SAlexander Duyck 		cmd_size = sizeof(*cmd) + cmd->in_length;
1132d657d17SAlexander Duyck 
1144d88a97aSDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
1152d657d17SAlexander Duyck 				ND_CMD_GET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
116e7c5a571SDan Williams 		if (rc < 0)
117e7c5a571SDan Williams 			break;
118e7c5a571SDan Williams 		if (cmd_rc < 0) {
119e7c5a571SDan Williams 			rc = cmd_rc;
1204d88a97aSDan Williams 			break;
1214d88a97aSDan Williams 		}
1222d657d17SAlexander Duyck 
1232d657d17SAlexander Duyck 		/* out_buf should be valid, copy it into our output buffer */
1242d657d17SAlexander Duyck 		memcpy(buf + buf_offset, cmd->out_buf, cmd->in_length);
1254d88a97aSDan Williams 	}
126d11cf4a7SDan Williams 	kvfree(cmd);
1274d88a97aSDan Williams 
1284d88a97aSDan Williams 	return rc;
1294d88a97aSDan Williams }
1304d88a97aSDan Williams 
131f524bf27SDan Williams int nvdimm_set_config_data(struct nvdimm_drvdata *ndd, size_t offset,
132f524bf27SDan Williams 		void *buf, size_t len)
133f524bf27SDan Williams {
134f524bf27SDan Williams 	size_t max_cmd_size, buf_offset;
135f524bf27SDan Williams 	struct nd_cmd_set_config_hdr *cmd;
136e7c5a571SDan Williams 	int rc = validate_dimm(ndd), cmd_rc = 0;
137f524bf27SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
138f524bf27SDan Williams 	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc;
139f524bf27SDan Williams 
140f524bf27SDan Williams 	if (rc)
141f524bf27SDan Williams 		return rc;
142f524bf27SDan Williams 
143f524bf27SDan Williams 	if (offset + len > ndd->nsarea.config_size)
144f524bf27SDan Williams 		return -ENXIO;
145f524bf27SDan Williams 
146d11cf4a7SDan Williams 	max_cmd_size = min_t(u32, len, ndd->nsarea.max_xfer);
147d11cf4a7SDan Williams 	cmd = kvzalloc(max_cmd_size + sizeof(*cmd) + sizeof(u32), GFP_KERNEL);
148f524bf27SDan Williams 	if (!cmd)
149f524bf27SDan Williams 		return -ENOMEM;
150f524bf27SDan Williams 
151f524bf27SDan Williams 	for (buf_offset = 0; len; len -= cmd->in_length,
152f524bf27SDan Williams 			buf_offset += cmd->in_length) {
153f524bf27SDan Williams 		size_t cmd_size;
154f524bf27SDan Williams 
155f524bf27SDan Williams 		cmd->in_offset = offset + buf_offset;
156f524bf27SDan Williams 		cmd->in_length = min(max_cmd_size, len);
157f524bf27SDan Williams 		memcpy(cmd->in_buf, buf + buf_offset, cmd->in_length);
158f524bf27SDan Williams 
159f524bf27SDan Williams 		/* status is output in the last 4-bytes of the command buffer */
160f524bf27SDan Williams 		cmd_size = sizeof(*cmd) + cmd->in_length + sizeof(u32);
161f524bf27SDan Williams 
162f524bf27SDan Williams 		rc = nd_desc->ndctl(nd_desc, to_nvdimm(ndd->dev),
163e7c5a571SDan Williams 				ND_CMD_SET_CONFIG_DATA, cmd, cmd_size, &cmd_rc);
164e7c5a571SDan Williams 		if (rc < 0)
165e7c5a571SDan Williams 			break;
166e7c5a571SDan Williams 		if (cmd_rc < 0) {
167e7c5a571SDan Williams 			rc = cmd_rc;
168f524bf27SDan Williams 			break;
169f524bf27SDan Williams 		}
170f524bf27SDan Williams 	}
171d11cf4a7SDan Williams 	kvfree(cmd);
172f524bf27SDan Williams 
173f524bf27SDan Williams 	return rc;
174f524bf27SDan Williams }
175f524bf27SDan Williams 
176a0e37452SDan Williams void nvdimm_set_labeling(struct device *dev)
17742237e39SDan Williams {
17842237e39SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
17942237e39SDan Williams 
180a0e37452SDan Williams 	set_bit(NDD_LABELING, &nvdimm->flags);
1818f078b38SDan Williams }
1828f078b38SDan Williams 
1838f078b38SDan Williams void nvdimm_set_locked(struct device *dev)
1848f078b38SDan Williams {
1858f078b38SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
1868f078b38SDan Williams 
1878f078b38SDan Williams 	set_bit(NDD_LOCKED, &nvdimm->flags);
18842237e39SDan Williams }
18942237e39SDan Williams 
190d34cb808SDan Williams void nvdimm_clear_locked(struct device *dev)
191d34cb808SDan Williams {
192d34cb808SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
193d34cb808SDan Williams 
194d34cb808SDan Williams 	clear_bit(NDD_LOCKED, &nvdimm->flags);
195d34cb808SDan Williams }
196d34cb808SDan Williams 
197e6dfb2deSDan Williams static void nvdimm_release(struct device *dev)
198e6dfb2deSDan Williams {
199e6dfb2deSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
200e6dfb2deSDan Williams 
201e6dfb2deSDan Williams 	ida_simple_remove(&dimm_ida, nvdimm->id);
202e6dfb2deSDan Williams 	kfree(nvdimm);
203e6dfb2deSDan Williams }
204e6dfb2deSDan Williams 
205e6dfb2deSDan Williams struct nvdimm *to_nvdimm(struct device *dev)
206e6dfb2deSDan Williams {
207e6dfb2deSDan Williams 	struct nvdimm *nvdimm = container_of(dev, struct nvdimm, dev);
208e6dfb2deSDan Williams 
209e6dfb2deSDan Williams 	WARN_ON(!is_nvdimm(dev));
210e6dfb2deSDan Williams 	return nvdimm;
211e6dfb2deSDan Williams }
212e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(to_nvdimm);
213e6dfb2deSDan Williams 
214047fc8a1SRoss Zwisler struct nvdimm *nd_blk_region_to_dimm(struct nd_blk_region *ndbr)
215047fc8a1SRoss Zwisler {
216047fc8a1SRoss Zwisler 	struct nd_region *nd_region = &ndbr->nd_region;
217047fc8a1SRoss Zwisler 	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
218047fc8a1SRoss Zwisler 
219047fc8a1SRoss Zwisler 	return nd_mapping->nvdimm;
220047fc8a1SRoss Zwisler }
221047fc8a1SRoss Zwisler EXPORT_SYMBOL_GPL(nd_blk_region_to_dimm);
222047fc8a1SRoss Zwisler 
223ca6a4657SDan Williams unsigned long nd_blk_memremap_flags(struct nd_blk_region *ndbr)
224ca6a4657SDan Williams {
225ca6a4657SDan Williams 	/* pmem mapping properties are private to libnvdimm */
226ca6a4657SDan Williams 	return ARCH_MEMREMAP_PMEM;
227ca6a4657SDan Williams }
228ca6a4657SDan Williams EXPORT_SYMBOL_GPL(nd_blk_memremap_flags);
229ca6a4657SDan Williams 
230bf9bccc1SDan Williams struct nvdimm_drvdata *to_ndd(struct nd_mapping *nd_mapping)
231bf9bccc1SDan Williams {
232bf9bccc1SDan Williams 	struct nvdimm *nvdimm = nd_mapping->nvdimm;
233bf9bccc1SDan Williams 
234bf9bccc1SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
235bf9bccc1SDan Williams 
236bf9bccc1SDan Williams 	return dev_get_drvdata(&nvdimm->dev);
237bf9bccc1SDan Williams }
238bf9bccc1SDan Williams EXPORT_SYMBOL(to_ndd);
239bf9bccc1SDan Williams 
240bf9bccc1SDan Williams void nvdimm_drvdata_release(struct kref *kref)
241bf9bccc1SDan Williams {
242bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = container_of(kref, typeof(*ndd), kref);
243bf9bccc1SDan Williams 	struct device *dev = ndd->dev;
244bf9bccc1SDan Williams 	struct resource *res, *_r;
245bf9bccc1SDan Williams 
246426824d6SDan Williams 	dev_dbg(dev, "trace\n");
247bf9bccc1SDan Williams 	nvdimm_bus_lock(dev);
248bf9bccc1SDan Williams 	for_each_dpa_resource_safe(ndd, res, _r)
249bf9bccc1SDan Williams 		nvdimm_free_dpa(ndd, res);
250bf9bccc1SDan Williams 	nvdimm_bus_unlock(dev);
251bf9bccc1SDan Williams 
252a06a7576Syalin wang 	kvfree(ndd->data);
253bf9bccc1SDan Williams 	kfree(ndd);
254bf9bccc1SDan Williams 	put_device(dev);
255bf9bccc1SDan Williams }
256bf9bccc1SDan Williams 
257bf9bccc1SDan Williams void get_ndd(struct nvdimm_drvdata *ndd)
258bf9bccc1SDan Williams {
259bf9bccc1SDan Williams 	kref_get(&ndd->kref);
260bf9bccc1SDan Williams }
261bf9bccc1SDan Williams 
262bf9bccc1SDan Williams void put_ndd(struct nvdimm_drvdata *ndd)
263bf9bccc1SDan Williams {
264bf9bccc1SDan Williams 	if (ndd)
265bf9bccc1SDan Williams 		kref_put(&ndd->kref, nvdimm_drvdata_release);
266bf9bccc1SDan Williams }
267bf9bccc1SDan Williams 
268e6dfb2deSDan Williams const char *nvdimm_name(struct nvdimm *nvdimm)
269e6dfb2deSDan Williams {
270e6dfb2deSDan Williams 	return dev_name(&nvdimm->dev);
271e6dfb2deSDan Williams }
272e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_name);
273e6dfb2deSDan Williams 
274ba9c8dd3SDan Williams struct kobject *nvdimm_kobj(struct nvdimm *nvdimm)
275ba9c8dd3SDan Williams {
276ba9c8dd3SDan Williams 	return &nvdimm->dev.kobj;
277ba9c8dd3SDan Williams }
278ba9c8dd3SDan Williams EXPORT_SYMBOL_GPL(nvdimm_kobj);
279ba9c8dd3SDan Williams 
280e3654ecaSDan Williams unsigned long nvdimm_cmd_mask(struct nvdimm *nvdimm)
281e3654ecaSDan Williams {
282e3654ecaSDan Williams 	return nvdimm->cmd_mask;
283e3654ecaSDan Williams }
284e3654ecaSDan Williams EXPORT_SYMBOL_GPL(nvdimm_cmd_mask);
285e3654ecaSDan Williams 
286e6dfb2deSDan Williams void *nvdimm_provider_data(struct nvdimm *nvdimm)
287e6dfb2deSDan Williams {
28862232e45SDan Williams 	if (nvdimm)
289e6dfb2deSDan Williams 		return nvdimm->provider_data;
29062232e45SDan Williams 	return NULL;
291e6dfb2deSDan Williams }
292e6dfb2deSDan Williams EXPORT_SYMBOL_GPL(nvdimm_provider_data);
293e6dfb2deSDan Williams 
29462232e45SDan Williams static ssize_t commands_show(struct device *dev,
29562232e45SDan Williams 		struct device_attribute *attr, char *buf)
29662232e45SDan Williams {
29762232e45SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
29862232e45SDan Williams 	int cmd, len = 0;
29962232e45SDan Williams 
300e3654ecaSDan Williams 	if (!nvdimm->cmd_mask)
30162232e45SDan Williams 		return sprintf(buf, "\n");
30262232e45SDan Williams 
303e3654ecaSDan Williams 	for_each_set_bit(cmd, &nvdimm->cmd_mask, BITS_PER_LONG)
30462232e45SDan Williams 		len += sprintf(buf + len, "%s ", nvdimm_cmd_name(cmd));
30562232e45SDan Williams 	len += sprintf(buf + len, "\n");
30662232e45SDan Williams 	return len;
30762232e45SDan Williams }
30862232e45SDan Williams static DEVICE_ATTR_RO(commands);
30962232e45SDan Williams 
310efbf6f50SDan Williams static ssize_t flags_show(struct device *dev,
311efbf6f50SDan Williams 		struct device_attribute *attr, char *buf)
312efbf6f50SDan Williams {
313efbf6f50SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
314efbf6f50SDan Williams 
315a0e37452SDan Williams 	return sprintf(buf, "%s%s%s\n",
316efbf6f50SDan Williams 			test_bit(NDD_ALIASING, &nvdimm->flags) ? "alias " : "",
317a0e37452SDan Williams 			test_bit(NDD_LABELING, &nvdimm->flags) ? "label " : "",
318efbf6f50SDan Williams 			test_bit(NDD_LOCKED, &nvdimm->flags) ? "lock " : "");
319efbf6f50SDan Williams }
320efbf6f50SDan Williams static DEVICE_ATTR_RO(flags);
321efbf6f50SDan Williams 
322eaf96153SDan Williams static ssize_t state_show(struct device *dev, struct device_attribute *attr,
323eaf96153SDan Williams 		char *buf)
324eaf96153SDan Williams {
325eaf96153SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
326eaf96153SDan Williams 
327eaf96153SDan Williams 	/*
328eaf96153SDan Williams 	 * The state may be in the process of changing, userspace should
329eaf96153SDan Williams 	 * quiesce probing if it wants a static answer
330eaf96153SDan Williams 	 */
331eaf96153SDan Williams 	nvdimm_bus_lock(dev);
332eaf96153SDan Williams 	nvdimm_bus_unlock(dev);
333eaf96153SDan Williams 	return sprintf(buf, "%s\n", atomic_read(&nvdimm->busy)
334eaf96153SDan Williams 			? "active" : "idle");
335eaf96153SDan Williams }
336eaf96153SDan Williams static DEVICE_ATTR_RO(state);
337eaf96153SDan Williams 
3380ba1c634SDan Williams static ssize_t available_slots_show(struct device *dev,
3390ba1c634SDan Williams 		struct device_attribute *attr, char *buf)
3400ba1c634SDan Williams {
3410ba1c634SDan Williams 	struct nvdimm_drvdata *ndd = dev_get_drvdata(dev);
3420ba1c634SDan Williams 	ssize_t rc;
3430ba1c634SDan Williams 	u32 nfree;
3440ba1c634SDan Williams 
3450ba1c634SDan Williams 	if (!ndd)
3460ba1c634SDan Williams 		return -ENXIO;
3470ba1c634SDan Williams 
3480ba1c634SDan Williams 	nvdimm_bus_lock(dev);
3490ba1c634SDan Williams 	nfree = nd_label_nfree(ndd);
3500ba1c634SDan Williams 	if (nfree - 1 > nfree) {
3510ba1c634SDan Williams 		dev_WARN_ONCE(dev, 1, "we ate our last label?\n");
3520ba1c634SDan Williams 		nfree = 0;
3530ba1c634SDan Williams 	} else
3540ba1c634SDan Williams 		nfree--;
3550ba1c634SDan Williams 	rc = sprintf(buf, "%d\n", nfree);
3560ba1c634SDan Williams 	nvdimm_bus_unlock(dev);
3570ba1c634SDan Williams 	return rc;
3580ba1c634SDan Williams }
3590ba1c634SDan Williams static DEVICE_ATTR_RO(available_slots);
3600ba1c634SDan Williams 
3613c13e2acSDave Jiang __weak ssize_t security_show(struct device *dev,
362f2989396SDave Jiang 		struct device_attribute *attr, char *buf)
363f2989396SDave Jiang {
364f2989396SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
365f2989396SDave Jiang 
366d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_DISABLED, &nvdimm->sec.flags))
367f2989396SDave Jiang 		return sprintf(buf, "disabled\n");
368d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_UNLOCKED, &nvdimm->sec.flags))
369f2989396SDave Jiang 		return sprintf(buf, "unlocked\n");
370d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_LOCKED, &nvdimm->sec.flags))
371f2989396SDave Jiang 		return sprintf(buf, "locked\n");
372d78c620aSDan Williams 	if (test_bit(NVDIMM_SECURITY_OVERWRITE, &nvdimm->sec.flags))
373f2989396SDave Jiang 		return sprintf(buf, "overwrite\n");
37489fa9d8eSDave Jiang 	return -ENOTTY;
375f2989396SDave Jiang }
376f2989396SDave Jiang 
377d78c620aSDan Williams static ssize_t frozen_show(struct device *dev,
378d78c620aSDan Williams 		struct device_attribute *attr, char *buf)
379d78c620aSDan Williams {
380d78c620aSDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
381d78c620aSDan Williams 
382d78c620aSDan Williams 	return sprintf(buf, "%d\n", test_bit(NVDIMM_SECURITY_FROZEN,
383d78c620aSDan Williams 				&nvdimm->sec.flags));
384f2989396SDave Jiang }
385d78c620aSDan Williams static DEVICE_ATTR_RO(frozen);
38637833fb7SDave Jiang 
38737833fb7SDave Jiang static ssize_t security_store(struct device *dev,
38837833fb7SDave Jiang 		struct device_attribute *attr, const char *buf, size_t len)
38937833fb7SDave Jiang 
39037833fb7SDave Jiang {
39137833fb7SDave Jiang 	ssize_t rc;
39237833fb7SDave Jiang 
39337833fb7SDave Jiang 	/*
39437833fb7SDave Jiang 	 * Require all userspace triggered security management to be
39537833fb7SDave Jiang 	 * done while probing is idle and the DIMM is not in active use
39637833fb7SDave Jiang 	 * in any region.
39737833fb7SDave Jiang 	 */
39887a30e1fSDan Williams 	nd_device_lock(dev);
39937833fb7SDave Jiang 	nvdimm_bus_lock(dev);
40037833fb7SDave Jiang 	wait_nvdimm_bus_probe_idle(dev);
4017b60422cSDan Williams 	rc = nvdimm_security_store(dev, buf, len);
40237833fb7SDave Jiang 	nvdimm_bus_unlock(dev);
40387a30e1fSDan Williams 	nd_device_unlock(dev);
40437833fb7SDave Jiang 
40537833fb7SDave Jiang 	return rc;
40637833fb7SDave Jiang }
40737833fb7SDave Jiang static DEVICE_ATTR_RW(security);
408f2989396SDave Jiang 
40962232e45SDan Williams static struct attribute *nvdimm_attributes[] = {
410eaf96153SDan Williams 	&dev_attr_state.attr,
411efbf6f50SDan Williams 	&dev_attr_flags.attr,
41262232e45SDan Williams 	&dev_attr_commands.attr,
4130ba1c634SDan Williams 	&dev_attr_available_slots.attr,
414f2989396SDave Jiang 	&dev_attr_security.attr,
415d78c620aSDan Williams 	&dev_attr_frozen.attr,
41662232e45SDan Williams 	NULL,
41762232e45SDan Williams };
41862232e45SDan Williams 
419f2989396SDave Jiang static umode_t nvdimm_visible(struct kobject *kobj, struct attribute *a, int n)
420f2989396SDave Jiang {
421f2989396SDave Jiang 	struct device *dev = container_of(kobj, typeof(*dev), kobj);
422f2989396SDave Jiang 	struct nvdimm *nvdimm = to_nvdimm(dev);
423f2989396SDave Jiang 
424d78c620aSDan Williams 	if (a != &dev_attr_security.attr && a != &dev_attr_frozen.attr)
425f2989396SDave Jiang 		return a->mode;
426d78c620aSDan Williams 	if (!nvdimm->sec.flags)
427f2989396SDave Jiang 		return 0;
428d78c620aSDan Williams 
429d78c620aSDan Williams 	if (a == &dev_attr_security.attr) {
430d78c620aSDan Williams 		/* Are there any state mutation ops (make writable)? */
431d2a4ac73SDave Jiang 		if (nvdimm->sec.ops->freeze || nvdimm->sec.ops->disable
43264e77c8cSDave Jiang 				|| nvdimm->sec.ops->change_key
4337d988097SDave Jiang 				|| nvdimm->sec.ops->erase
4347d988097SDave Jiang 				|| nvdimm->sec.ops->overwrite)
435f2989396SDave Jiang 			return a->mode;
43637833fb7SDave Jiang 		return 0444;
437f2989396SDave Jiang 	}
438f2989396SDave Jiang 
439d78c620aSDan Williams 	if (nvdimm->sec.ops->freeze)
440d78c620aSDan Williams 		return a->mode;
441d78c620aSDan Williams 	return 0;
442d78c620aSDan Williams }
443d78c620aSDan Williams 
444360eba7eSDan Williams static const struct attribute_group nvdimm_attribute_group = {
44562232e45SDan Williams 	.attrs = nvdimm_attributes,
446f2989396SDave Jiang 	.is_visible = nvdimm_visible,
44762232e45SDan Williams };
448360eba7eSDan Williams 
449360eba7eSDan Williams static const struct attribute_group *nvdimm_attribute_groups[] = {
450360eba7eSDan Williams 	&nd_device_attribute_group,
451360eba7eSDan Williams 	&nvdimm_attribute_group,
452360eba7eSDan Williams 	NULL,
453360eba7eSDan Williams };
454360eba7eSDan Williams 
455360eba7eSDan Williams static const struct device_type nvdimm_device_type = {
456360eba7eSDan Williams 	.name = "nvdimm",
457360eba7eSDan Williams 	.release = nvdimm_release,
458360eba7eSDan Williams 	.groups = nvdimm_attribute_groups,
459360eba7eSDan Williams };
460360eba7eSDan Williams 
461360eba7eSDan Williams bool is_nvdimm(struct device *dev)
462360eba7eSDan Williams {
463360eba7eSDan Williams 	return dev->type == &nvdimm_device_type;
464360eba7eSDan Williams }
46562232e45SDan Williams 
466d6548ae4SDave Jiang struct nvdimm *__nvdimm_create(struct nvdimm_bus *nvdimm_bus,
467d6548ae4SDave Jiang 		void *provider_data, const struct attribute_group **groups,
468d6548ae4SDave Jiang 		unsigned long flags, unsigned long cmd_mask, int num_flush,
469f2989396SDave Jiang 		struct resource *flush_wpq, const char *dimm_id,
470f2989396SDave Jiang 		const struct nvdimm_security_ops *sec_ops)
471e6dfb2deSDan Williams {
472e6dfb2deSDan Williams 	struct nvdimm *nvdimm = kzalloc(sizeof(*nvdimm), GFP_KERNEL);
473e6dfb2deSDan Williams 	struct device *dev;
474e6dfb2deSDan Williams 
475e6dfb2deSDan Williams 	if (!nvdimm)
476e6dfb2deSDan Williams 		return NULL;
477e6dfb2deSDan Williams 
478e6dfb2deSDan Williams 	nvdimm->id = ida_simple_get(&dimm_ida, 0, 0, GFP_KERNEL);
479e6dfb2deSDan Williams 	if (nvdimm->id < 0) {
480e6dfb2deSDan Williams 		kfree(nvdimm);
481e6dfb2deSDan Williams 		return NULL;
482e6dfb2deSDan Williams 	}
483d6548ae4SDave Jiang 
484d6548ae4SDave Jiang 	nvdimm->dimm_id = dimm_id;
485e6dfb2deSDan Williams 	nvdimm->provider_data = provider_data;
486d5d30d5aSDan Williams 	if (noblk)
487d5d30d5aSDan Williams 		flags |= 1 << NDD_NOBLK;
488e6dfb2deSDan Williams 	nvdimm->flags = flags;
489e3654ecaSDan Williams 	nvdimm->cmd_mask = cmd_mask;
490e5ae3b25SDan Williams 	nvdimm->num_flush = num_flush;
491e5ae3b25SDan Williams 	nvdimm->flush_wpq = flush_wpq;
492eaf96153SDan Williams 	atomic_set(&nvdimm->busy, 0);
493e6dfb2deSDan Williams 	dev = &nvdimm->dev;
494e6dfb2deSDan Williams 	dev_set_name(dev, "nmem%d", nvdimm->id);
495e6dfb2deSDan Williams 	dev->parent = &nvdimm_bus->dev;
496e6dfb2deSDan Williams 	dev->type = &nvdimm_device_type;
49762232e45SDan Williams 	dev->devt = MKDEV(nvdimm_major, nvdimm->id);
498e6dfb2deSDan Williams 	dev->groups = groups;
499f2989396SDave Jiang 	nvdimm->sec.ops = sec_ops;
5007d988097SDave Jiang 	nvdimm->sec.overwrite_tmo = 0;
5017d988097SDave Jiang 	INIT_DELAYED_WORK(&nvdimm->dwork, nvdimm_security_overwrite_query);
502f2989396SDave Jiang 	/*
503f2989396SDave Jiang 	 * Security state must be initialized before device_add() for
504f2989396SDave Jiang 	 * attribute visibility.
505f2989396SDave Jiang 	 */
50689fa9d8eSDave Jiang 	/* get security state and extended (master) state */
507d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
508d78c620aSDan Williams 	nvdimm->sec.ext_flags = nvdimm_security_flags(nvdimm, NVDIMM_MASTER);
5094d88a97aSDan Williams 	nd_device_register(dev);
510e6dfb2deSDan Williams 
511e6dfb2deSDan Williams 	return nvdimm;
512e6dfb2deSDan Williams }
513d6548ae4SDave Jiang EXPORT_SYMBOL_GPL(__nvdimm_create);
5144d88a97aSDan Williams 
5151cd73865SDan Williams static void shutdown_security_notify(void *data)
5167d988097SDave Jiang {
5171cd73865SDan Williams 	struct nvdimm *nvdimm = data;
5181cd73865SDan Williams 
5191cd73865SDan Williams 	sysfs_put(nvdimm->sec.overwrite_state);
5201cd73865SDan Williams }
5211cd73865SDan Williams 
5221cd73865SDan Williams int nvdimm_security_setup_events(struct device *dev)
5231cd73865SDan Williams {
5241cd73865SDan Williams 	struct nvdimm *nvdimm = to_nvdimm(dev);
5251cd73865SDan Williams 
526d78c620aSDan Williams 	if (!nvdimm->sec.flags || !nvdimm->sec.ops
5271cd73865SDan Williams 			|| !nvdimm->sec.ops->overwrite)
5287d988097SDave Jiang 		return 0;
5291cd73865SDan Williams 	nvdimm->sec.overwrite_state = sysfs_get_dirent(dev->kobj.sd, "security");
5301cd73865SDan Williams 	if (!nvdimm->sec.overwrite_state)
5311cd73865SDan Williams 		return -ENOMEM;
5321cd73865SDan Williams 
5331cd73865SDan Williams 	return devm_add_action_or_reset(dev, shutdown_security_notify, nvdimm);
5347d988097SDave Jiang }
5357d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_security_setup_events);
5367d988097SDave Jiang 
5377d988097SDave Jiang int nvdimm_in_overwrite(struct nvdimm *nvdimm)
5387d988097SDave Jiang {
5397d988097SDave Jiang 	return test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags);
5407d988097SDave Jiang }
5417d988097SDave Jiang EXPORT_SYMBOL_GPL(nvdimm_in_overwrite);
5427d988097SDave Jiang 
54337833fb7SDave Jiang int nvdimm_security_freeze(struct nvdimm *nvdimm)
54437833fb7SDave Jiang {
54537833fb7SDave Jiang 	int rc;
54637833fb7SDave Jiang 
54737833fb7SDave Jiang 	WARN_ON_ONCE(!is_nvdimm_bus_locked(&nvdimm->dev));
54837833fb7SDave Jiang 
54937833fb7SDave Jiang 	if (!nvdimm->sec.ops || !nvdimm->sec.ops->freeze)
55037833fb7SDave Jiang 		return -EOPNOTSUPP;
55137833fb7SDave Jiang 
552d78c620aSDan Williams 	if (!nvdimm->sec.flags)
55337833fb7SDave Jiang 		return -EIO;
55437833fb7SDave Jiang 
5557d988097SDave Jiang 	if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) {
5567d988097SDave Jiang 		dev_warn(&nvdimm->dev, "Overwrite operation in progress.\n");
5577d988097SDave Jiang 		return -EBUSY;
5587d988097SDave Jiang 	}
5597d988097SDave Jiang 
56037833fb7SDave Jiang 	rc = nvdimm->sec.ops->freeze(nvdimm);
561d78c620aSDan Williams 	nvdimm->sec.flags = nvdimm_security_flags(nvdimm, NVDIMM_USER);
56237833fb7SDave Jiang 
56337833fb7SDave Jiang 	return rc;
56437833fb7SDave Jiang }
56537833fb7SDave Jiang 
5662522afb8SDan Williams static unsigned long dpa_align(struct nd_region *nd_region)
5672522afb8SDan Williams {
5682522afb8SDan Williams 	struct device *dev = &nd_region->dev;
5692522afb8SDan Williams 
5702522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !is_nvdimm_bus_locked(dev),
5712522afb8SDan Williams 				"bus lock required for capacity provision\n"))
5722522afb8SDan Williams 		return 0;
5732522afb8SDan Williams 	if (dev_WARN_ONCE(dev, !nd_region->ndr_mappings || nd_region->align
5742522afb8SDan Williams 				% nd_region->ndr_mappings,
5752522afb8SDan Williams 				"invalid region align %#lx mappings: %d\n",
5762522afb8SDan Williams 				nd_region->align, nd_region->ndr_mappings))
5772522afb8SDan Williams 		return 0;
5782522afb8SDan Williams 	return nd_region->align / nd_region->ndr_mappings;
5792522afb8SDan Williams }
5802522afb8SDan Williams 
581762d067dSDan Williams int alias_dpa_busy(struct device *dev, void *data)
582a1f3e4d6SDan Williams {
583fe514739SDan Williams 	resource_size_t map_end, blk_start, new;
584a1f3e4d6SDan Williams 	struct blk_alloc_info *info = data;
585a1f3e4d6SDan Williams 	struct nd_mapping *nd_mapping;
586a1f3e4d6SDan Williams 	struct nd_region *nd_region;
587a1f3e4d6SDan Williams 	struct nvdimm_drvdata *ndd;
588a1f3e4d6SDan Williams 	struct resource *res;
5892522afb8SDan Williams 	unsigned long align;
590a1f3e4d6SDan Williams 	int i;
591a1f3e4d6SDan Williams 
592c9e582aaSDan Williams 	if (!is_memory(dev))
593a1f3e4d6SDan Williams 		return 0;
594a1f3e4d6SDan Williams 
595a1f3e4d6SDan Williams 	nd_region = to_nd_region(dev);
596a1f3e4d6SDan Williams 	for (i = 0; i < nd_region->ndr_mappings; i++) {
597a1f3e4d6SDan Williams 		nd_mapping  = &nd_region->mapping[i];
598a1f3e4d6SDan Williams 		if (nd_mapping->nvdimm == info->nd_mapping->nvdimm)
599a1f3e4d6SDan Williams 			break;
600a1f3e4d6SDan Williams 	}
601a1f3e4d6SDan Williams 
602a1f3e4d6SDan Williams 	if (i >= nd_region->ndr_mappings)
603a1f3e4d6SDan Williams 		return 0;
604a1f3e4d6SDan Williams 
605a1f3e4d6SDan Williams 	ndd = to_ndd(nd_mapping);
606a1f3e4d6SDan Williams 	map_end = nd_mapping->start + nd_mapping->size - 1;
607a1f3e4d6SDan Williams 	blk_start = nd_mapping->start;
608762d067dSDan Williams 
609762d067dSDan Williams 	/*
610762d067dSDan Williams 	 * In the allocation case ->res is set to free space that we are
611762d067dSDan Williams 	 * looking to validate against PMEM aliasing collision rules
612762d067dSDan Williams 	 * (i.e. BLK is allocated after all aliased PMEM).
613762d067dSDan Williams 	 */
614762d067dSDan Williams 	if (info->res) {
615762d067dSDan Williams 		if (info->res->start >= nd_mapping->start
616762d067dSDan Williams 				&& info->res->start < map_end)
617762d067dSDan Williams 			/* pass */;
618762d067dSDan Williams 		else
619762d067dSDan Williams 			return 0;
620762d067dSDan Williams 	}
621762d067dSDan Williams 
622a1f3e4d6SDan Williams  retry:
623a1f3e4d6SDan Williams 	/*
624a1f3e4d6SDan Williams 	 * Find the free dpa from the end of the last pmem allocation to
625fe514739SDan Williams 	 * the end of the interleave-set mapping.
626a1f3e4d6SDan Williams 	 */
6272522afb8SDan Williams 	align = dpa_align(nd_region);
6282522afb8SDan Williams 	if (!align)
6292522afb8SDan Williams 		return 0;
6302522afb8SDan Williams 
631a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
6322522afb8SDan Williams 		resource_size_t start, end;
6332522afb8SDan Williams 
634fe514739SDan Williams 		if (strncmp(res->name, "pmem", 4) != 0)
635fe514739SDan Williams 			continue;
6362522afb8SDan Williams 
6372522afb8SDan Williams 		start = ALIGN_DOWN(res->start, align);
6382522afb8SDan Williams 		end = ALIGN(res->end + 1, align) - 1;
6392522afb8SDan Williams 		if ((start >= blk_start && start < map_end)
6402522afb8SDan Williams 				|| (end >= blk_start && end <= map_end)) {
6412522afb8SDan Williams 			new = max(blk_start, min(map_end, end) + 1);
642a1f3e4d6SDan Williams 			if (new != blk_start) {
643a1f3e4d6SDan Williams 				blk_start = new;
644a1f3e4d6SDan Williams 				goto retry;
645a1f3e4d6SDan Williams 			}
646a1f3e4d6SDan Williams 		}
647a1f3e4d6SDan Williams 	}
648a1f3e4d6SDan Williams 
649762d067dSDan Williams 	/* update the free space range with the probed blk_start */
650762d067dSDan Williams 	if (info->res && blk_start > info->res->start) {
651762d067dSDan Williams 		info->res->start = max(info->res->start, blk_start);
652762d067dSDan Williams 		if (info->res->start > info->res->end)
653762d067dSDan Williams 			info->res->end = info->res->start - 1;
654762d067dSDan Williams 		return 1;
655762d067dSDan Williams 	}
656762d067dSDan Williams 
657fe514739SDan Williams 	info->available -= blk_start - nd_mapping->start;
658762d067dSDan Williams 
659a1f3e4d6SDan Williams 	return 0;
660a1f3e4d6SDan Williams }
661a1f3e4d6SDan Williams 
662bf9bccc1SDan Williams /**
6631b40e09aSDan Williams  * nd_blk_available_dpa - account the unused dpa of BLK region
6641b40e09aSDan Williams  * @nd_mapping: container of dpa-resource-root + labels
6651b40e09aSDan Williams  *
666a1f3e4d6SDan Williams  * Unlike PMEM, BLK namespaces can occupy discontiguous DPA ranges, but
667a1f3e4d6SDan Williams  * we arrange for them to never start at an lower dpa than the last
668a1f3e4d6SDan Williams  * PMEM allocation in an aliased region.
6691b40e09aSDan Williams  */
670a1f3e4d6SDan Williams resource_size_t nd_blk_available_dpa(struct nd_region *nd_region)
6711b40e09aSDan Williams {
672a1f3e4d6SDan Williams 	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev);
673a1f3e4d6SDan Williams 	struct nd_mapping *nd_mapping = &nd_region->mapping[0];
6741b40e09aSDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
675a1f3e4d6SDan Williams 	struct blk_alloc_info info = {
676a1f3e4d6SDan Williams 		.nd_mapping = nd_mapping,
677a1f3e4d6SDan Williams 		.available = nd_mapping->size,
678762d067dSDan Williams 		.res = NULL,
679a1f3e4d6SDan Williams 	};
6801b40e09aSDan Williams 	struct resource *res;
6812522afb8SDan Williams 	unsigned long align;
6821b40e09aSDan Williams 
6831b40e09aSDan Williams 	if (!ndd)
6841b40e09aSDan Williams 		return 0;
6851b40e09aSDan Williams 
686a1f3e4d6SDan Williams 	device_for_each_child(&nvdimm_bus->dev, &info, alias_dpa_busy);
6871b40e09aSDan Williams 
688a1f3e4d6SDan Williams 	/* now account for busy blk allocations in unaliased dpa */
6892522afb8SDan Williams 	align = dpa_align(nd_region);
6902522afb8SDan Williams 	if (!align)
6912522afb8SDan Williams 		return 0;
692a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
6932522afb8SDan Williams 		resource_size_t start, end, size;
6942522afb8SDan Williams 
695a1f3e4d6SDan Williams 		if (strncmp(res->name, "blk", 3) != 0)
696a1f3e4d6SDan Williams 			continue;
6972522afb8SDan Williams 		start = ALIGN_DOWN(res->start, align);
6982522afb8SDan Williams 		end = ALIGN(res->end + 1, align) - 1;
6992522afb8SDan Williams 		size = end - start + 1;
7002522afb8SDan Williams 		if (size >= info.available)
7012522afb8SDan Williams 			return 0;
7022522afb8SDan Williams 		info.available -= size;
7031b40e09aSDan Williams 	}
7041b40e09aSDan Williams 
705a1f3e4d6SDan Williams 	return info.available;
7061b40e09aSDan Williams }
7071b40e09aSDan Williams 
7081b40e09aSDan Williams /**
70912e3129eSKeith Busch  * nd_pmem_max_contiguous_dpa - For the given dimm+region, return the max
71012e3129eSKeith Busch  *			   contiguous unallocated dpa range.
71112e3129eSKeith Busch  * @nd_region: constrain available space check to this reference region
71212e3129eSKeith Busch  * @nd_mapping: container of dpa-resource-root + labels
71312e3129eSKeith Busch  */
71412e3129eSKeith Busch resource_size_t nd_pmem_max_contiguous_dpa(struct nd_region *nd_region,
71512e3129eSKeith Busch 					   struct nd_mapping *nd_mapping)
71612e3129eSKeith Busch {
71712e3129eSKeith Busch 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
71812e3129eSKeith Busch 	struct nvdimm_bus *nvdimm_bus;
71912e3129eSKeith Busch 	resource_size_t max = 0;
72012e3129eSKeith Busch 	struct resource *res;
7212522afb8SDan Williams 	unsigned long align;
72212e3129eSKeith Busch 
72312e3129eSKeith Busch 	/* if a dimm is disabled the available capacity is zero */
72412e3129eSKeith Busch 	if (!ndd)
72512e3129eSKeith Busch 		return 0;
72612e3129eSKeith Busch 
7272522afb8SDan Williams 	align = dpa_align(nd_region);
7282522afb8SDan Williams 	if (!align)
7292522afb8SDan Williams 		return 0;
7302522afb8SDan Williams 
73112e3129eSKeith Busch 	nvdimm_bus = walk_to_nvdimm_bus(ndd->dev);
73212e3129eSKeith Busch 	if (__reserve_free_pmem(&nd_region->dev, nd_mapping->nvdimm))
73312e3129eSKeith Busch 		return 0;
73412e3129eSKeith Busch 	for_each_dpa_resource(ndd, res) {
7352522afb8SDan Williams 		resource_size_t start, end;
7362522afb8SDan Williams 
73712e3129eSKeith Busch 		if (strcmp(res->name, "pmem-reserve") != 0)
73812e3129eSKeith Busch 			continue;
7392522afb8SDan Williams 		/* trim free space relative to current alignment setting */
7402522afb8SDan Williams 		start = ALIGN(res->start, align);
7412522afb8SDan Williams 		end = ALIGN_DOWN(res->end + 1, align) - 1;
7422522afb8SDan Williams 		if (end < start)
7432522afb8SDan Williams 			continue;
7442522afb8SDan Williams 		if (end - start + 1 > max)
7452522afb8SDan Williams 			max = end - start + 1;
74612e3129eSKeith Busch 	}
74712e3129eSKeith Busch 	release_free_pmem(nvdimm_bus, nd_mapping);
74812e3129eSKeith Busch 	return max;
74912e3129eSKeith Busch }
75012e3129eSKeith Busch 
75112e3129eSKeith Busch /**
752bf9bccc1SDan Williams  * nd_pmem_available_dpa - for the given dimm+region account unallocated dpa
753bf9bccc1SDan Williams  * @nd_mapping: container of dpa-resource-root + labels
754bf9bccc1SDan Williams  * @nd_region: constrain available space check to this reference region
755bf9bccc1SDan Williams  * @overlap: calculate available space assuming this level of overlap
756bf9bccc1SDan Williams  *
757bf9bccc1SDan Williams  * Validate that a PMEM label, if present, aligns with the start of an
758bf9bccc1SDan Williams  * interleave set and truncate the available size at the lowest BLK
759bf9bccc1SDan Williams  * overlap point.
760bf9bccc1SDan Williams  *
761bf9bccc1SDan Williams  * The expectation is that this routine is called multiple times as it
762bf9bccc1SDan Williams  * probes for the largest BLK encroachment for any single member DIMM of
763bf9bccc1SDan Williams  * the interleave set.  Once that value is determined the PMEM-limit for
764bf9bccc1SDan Williams  * the set can be established.
765bf9bccc1SDan Williams  */
766bf9bccc1SDan Williams resource_size_t nd_pmem_available_dpa(struct nd_region *nd_region,
767bf9bccc1SDan Williams 		struct nd_mapping *nd_mapping, resource_size_t *overlap)
768bf9bccc1SDan Williams {
769bf9bccc1SDan Williams 	resource_size_t map_start, map_end, busy = 0, available, blk_start;
770bf9bccc1SDan Williams 	struct nvdimm_drvdata *ndd = to_ndd(nd_mapping);
771bf9bccc1SDan Williams 	struct resource *res;
772bf9bccc1SDan Williams 	const char *reason;
7732522afb8SDan Williams 	unsigned long align;
774bf9bccc1SDan Williams 
775bf9bccc1SDan Williams 	if (!ndd)
776bf9bccc1SDan Williams 		return 0;
777bf9bccc1SDan Williams 
7782522afb8SDan Williams 	align = dpa_align(nd_region);
7792522afb8SDan Williams 	if (!align)
7802522afb8SDan Williams 		return 0;
7812522afb8SDan Williams 
782bf9bccc1SDan Williams 	map_start = nd_mapping->start;
783bf9bccc1SDan Williams 	map_end = map_start + nd_mapping->size - 1;
784bf9bccc1SDan Williams 	blk_start = max(map_start, map_end + 1 - *overlap);
785a1f3e4d6SDan Williams 	for_each_dpa_resource(ndd, res) {
7862522afb8SDan Williams 		resource_size_t start, end;
7872522afb8SDan Williams 
7882522afb8SDan Williams 		start = ALIGN_DOWN(res->start, align);
7892522afb8SDan Williams 		end = ALIGN(res->end + 1, align) - 1;
7902522afb8SDan Williams 		if (start >= map_start && start < map_end) {
791bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0)
792a1f3e4d6SDan Williams 				blk_start = min(blk_start,
7932522afb8SDan Williams 						max(map_start, start));
7942522afb8SDan Williams 			else if (end > map_end) {
795bf9bccc1SDan Williams 				reason = "misaligned to iset";
796bf9bccc1SDan Williams 				goto err;
797a1f3e4d6SDan Williams 			} else
7982522afb8SDan Williams 				busy += end - start + 1;
7992522afb8SDan Williams 		} else if (end >= map_start && end <= map_end) {
800bf9bccc1SDan Williams 			if (strncmp(res->name, "blk", 3) == 0) {
801bf9bccc1SDan Williams 				/*
802bf9bccc1SDan Williams 				 * If a BLK allocation overlaps the start of
803bf9bccc1SDan Williams 				 * PMEM the entire interleave set may now only
804bf9bccc1SDan Williams 				 * be used for BLK.
805bf9bccc1SDan Williams 				 */
806bf9bccc1SDan Williams 				blk_start = map_start;
807a1f3e4d6SDan Williams 			} else
8082522afb8SDan Williams 				busy += end - start + 1;
8092522afb8SDan Williams 		} else if (map_start > start && map_start < end) {
810bf9bccc1SDan Williams 			/* total eclipse of the mapping */
811bf9bccc1SDan Williams 			busy += nd_mapping->size;
812bf9bccc1SDan Williams 			blk_start = map_start;
813bf9bccc1SDan Williams 		}
814a1f3e4d6SDan Williams 	}
815bf9bccc1SDan Williams 
816bf9bccc1SDan Williams 	*overlap = map_end + 1 - blk_start;
817bf9bccc1SDan Williams 	available = blk_start - map_start;
818bf9bccc1SDan Williams 	if (busy < available)
8192522afb8SDan Williams 		return ALIGN_DOWN(available - busy, align);
820bf9bccc1SDan Williams 	return 0;
821bf9bccc1SDan Williams 
822bf9bccc1SDan Williams  err:
823bf9bccc1SDan Williams 	nd_dbg_dpa(nd_region, ndd, res, "%s\n", reason);
824bf9bccc1SDan Williams 	return 0;
825bf9bccc1SDan Williams }
826bf9bccc1SDan Williams 
8274a826c83SDan Williams void nvdimm_free_dpa(struct nvdimm_drvdata *ndd, struct resource *res)
8284a826c83SDan Williams {
8294a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8304a826c83SDan Williams 	kfree(res->name);
8314a826c83SDan Williams 	__release_region(&ndd->dpa, res->start, resource_size(res));
8324a826c83SDan Williams }
8334a826c83SDan Williams 
8344a826c83SDan Williams struct resource *nvdimm_allocate_dpa(struct nvdimm_drvdata *ndd,
8354a826c83SDan Williams 		struct nd_label_id *label_id, resource_size_t start,
8364a826c83SDan Williams 		resource_size_t n)
8374a826c83SDan Williams {
8384a826c83SDan Williams 	char *name = kmemdup(label_id, sizeof(*label_id), GFP_KERNEL);
8394a826c83SDan Williams 	struct resource *res;
8404a826c83SDan Williams 
8414a826c83SDan Williams 	if (!name)
8424a826c83SDan Williams 		return NULL;
8434a826c83SDan Williams 
8444a826c83SDan Williams 	WARN_ON_ONCE(!is_nvdimm_bus_locked(ndd->dev));
8454a826c83SDan Williams 	res = __request_region(&ndd->dpa, start, n, name, 0);
8464a826c83SDan Williams 	if (!res)
8474a826c83SDan Williams 		kfree(name);
8484a826c83SDan Williams 	return res;
8494a826c83SDan Williams }
8504a826c83SDan Williams 
851bf9bccc1SDan Williams /**
852bf9bccc1SDan Williams  * nvdimm_allocated_dpa - sum up the dpa currently allocated to this label_id
853bf9bccc1SDan Williams  * @nvdimm: container of dpa-resource-root + labels
854bf9bccc1SDan Williams  * @label_id: dpa resource name of the form {pmem|blk}-<human readable uuid>
855bf9bccc1SDan Williams  */
856bf9bccc1SDan Williams resource_size_t nvdimm_allocated_dpa(struct nvdimm_drvdata *ndd,
857bf9bccc1SDan Williams 		struct nd_label_id *label_id)
858bf9bccc1SDan Williams {
859bf9bccc1SDan Williams 	resource_size_t allocated = 0;
860bf9bccc1SDan Williams 	struct resource *res;
861bf9bccc1SDan Williams 
862bf9bccc1SDan Williams 	for_each_dpa_resource(ndd, res)
863bf9bccc1SDan Williams 		if (strcmp(res->name, label_id->id) == 0)
864bf9bccc1SDan Williams 			allocated += resource_size(res);
865bf9bccc1SDan Williams 
866bf9bccc1SDan Williams 	return allocated;
867bf9bccc1SDan Williams }
868bf9bccc1SDan Williams 
8694d88a97aSDan Williams static int count_dimms(struct device *dev, void *c)
8704d88a97aSDan Williams {
8714d88a97aSDan Williams 	int *count = c;
8724d88a97aSDan Williams 
8734d88a97aSDan Williams 	if (is_nvdimm(dev))
8744d88a97aSDan Williams 		(*count)++;
8754d88a97aSDan Williams 	return 0;
8764d88a97aSDan Williams }
8774d88a97aSDan Williams 
8784d88a97aSDan Williams int nvdimm_bus_check_dimm_count(struct nvdimm_bus *nvdimm_bus, int dimm_count)
8794d88a97aSDan Williams {
8804d88a97aSDan Williams 	int count = 0;
8814d88a97aSDan Williams 	/* Flush any possible dimm registration failures */
8824d88a97aSDan Williams 	nd_synchronize();
8834d88a97aSDan Williams 
8844d88a97aSDan Williams 	device_for_each_child(&nvdimm_bus->dev, &count, count_dimms);
885426824d6SDan Williams 	dev_dbg(&nvdimm_bus->dev, "count: %d\n", count);
8864d88a97aSDan Williams 	if (count != dimm_count)
8874d88a97aSDan Williams 		return -ENXIO;
8884d88a97aSDan Williams 	return 0;
8894d88a97aSDan Williams }
8904d88a97aSDan Williams EXPORT_SYMBOL_GPL(nvdimm_bus_check_dimm_count);
891b354aba0SDan Williams 
892b354aba0SDan Williams void __exit nvdimm_devs_exit(void)
893b354aba0SDan Williams {
894b354aba0SDan Williams 	ida_destroy(&dimm_ida);
895b354aba0SDan Williams }
896